Improve diagnostics for runtime versions consistency checker

- report the diagnostic on the actual problematic file
- output the list of all runtime jars if min version != max version
This commit is contained in:
Alexander Udalov
2016-12-12 19:17:52 +03:00
parent d3e5580ab9
commit 2c527d89ab
4 changed files with 46 additions and 40 deletions
@@ -28,14 +28,15 @@ data class CompilerMessageLocation private constructor(
path + (if (line != -1 || column != -1) " ($line:$column)" else "") path + (if (line != -1 || column != -1) " ($line:$column)" else "")
companion object { companion object {
@JvmField val NO_LOCATION: CompilerMessageLocation = CompilerMessageLocation(null, -1, -1, null) @JvmField
val NO_LOCATION: CompilerMessageLocation = create(null)
@JvmStatic fun create( @JvmStatic
path: String?, fun create(path: String?): CompilerMessageLocation =
line: Int, CompilerMessageLocation(path, -1, -1, null)
column: Int,
lineContent: String? @JvmStatic
): CompilerMessageLocation = fun create(path: String?, line: Int, column: Int, lineContent: String?): CompilerMessageLocation =
if (path == null) NO_LOCATION else CompilerMessageLocation(path, line, column, lineContent) if (path == null) NO_LOCATION else CompilerMessageLocation(path, line, column, lineContent)
private val serialVersionUID: Long = 8228357578L private val serialVersionUID: Long = 8228357578L
@@ -229,9 +229,8 @@ class AnalyzerWithCompilerReport(private val messageCollector: MessageCollector)
severity, severity,
"Class '" + JvmClassName.byClassId(data.classId) + "' was compiled with an incompatible version of Kotlin. " + "Class '" + JvmClassName.byClassId(data.classId) + "' was compiled with an incompatible version of Kotlin. " +
"The binary version of its bytecode is " + data.actualVersion + ", expected version is " + data.expectedVersion, "The binary version of its bytecode is " + data.actualVersion + ", expected version is " + data.expectedVersion,
CompilerMessageLocation.create(toSystemDependentName(data.filePath), -1, -1, null) CompilerMessageLocation.create(toSystemDependentName(data.filePath))
) )
} }
} }
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.cli.jvm package org.jetbrains.kotlin.cli.jvm
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -106,9 +107,9 @@ object JvmRuntimeVersionsConsistencyChecker {
fun checkCompilerClasspathConsistency( fun checkCompilerClasspathConsistency(
messageCollector: MessageCollector, messageCollector: MessageCollector,
languageVersionSettings: LanguageVersionSettings?, languageVersionSettings: LanguageVersionSettings?,
classpathJars: List<VirtualFile> classpathJarRoots: List<VirtualFile>
) { ) {
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJars) val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
if (!runtimeJarsInfo.hasAnyJarsToCheck) return if (!runtimeJarsInfo.hasAnyJarsToCheck) return
val languageVersion = val languageVersion =
@@ -128,13 +129,13 @@ object JvmRuntimeVersionsConsistencyChecker {
private fun checkNotNewerThanCompiler(messageCollector: MessageCollector, jar: KotlinLibraryFile) { private fun checkNotNewerThanCompiler(messageCollector: MessageCollector, jar: KotlinLibraryFile) {
if (jar.version > CURRENT_COMPILER_VERSION) { if (jar.version > CURRENT_COMPILER_VERSION) {
messageCollector.issue("Run-time JAR file $jar is newer than compiler version $CURRENT_COMPILER_VERSION") messageCollector.issue(jar.file, "Runtime JAR file has version ${jar.version} which is newer than compiler version $CURRENT_COMPILER_VERSION")
} }
} }
private fun checkCompatibleWithLanguageVersion(messageCollector: MessageCollector, jar: KotlinLibraryFile, languageVersion: MavenComparableVersion) { private fun checkCompatibleWithLanguageVersion(messageCollector: MessageCollector, jar: KotlinLibraryFile, languageVersion: MavenComparableVersion) {
if (jar.version < languageVersion) { if (jar.version < languageVersion) {
messageCollector.issue("Run-time JAR file $jar is older than required for language version $languageVersion") messageCollector.issue(jar.file, "Runtime JAR file has version ${jar.version} which is older than required for language version $languageVersion")
} }
} }
@@ -143,31 +144,37 @@ object JvmRuntimeVersionsConsistencyChecker {
val newestCoreJar = runtimeJarsInfo.coreJars.maxBy { it.version } ?: return val newestCoreJar = runtimeJarsInfo.coreJars.maxBy { it.version } ?: return
if (oldestCoreJar.version != newestCoreJar.version) { if (oldestCoreJar.version != newestCoreJar.version) {
messageCollector.issue("Run-time JAR file $oldestCoreJar is not compatible with JAR file $newestCoreJar") messageCollector.issue(null, buildString {
appendln("Runtime JAR files in the classpath must have the same version. These files were found in the classpath:")
for (jar in runtimeJarsInfo.coreJars) {
appendln(" ${jar.file.path} (version ${jar.version})")
}
}.trimEnd())
} }
} }
private fun MessageCollector.issue(message: String) { private fun MessageCollector.issue(file: VirtualFile?, message: String) {
report(VERSION_ISSUE_SEVERITY, message, CompilerMessageLocation.NO_LOCATION) report(VERSION_ISSUE_SEVERITY, message, CompilerMessageLocation.create(file?.let(VfsUtilCore::virtualToIoFile)?.path))
} }
private fun collectRuntimeJarsInfo(classpathJars: List<VirtualFile>): RuntimeJarsInfo { private fun collectRuntimeJarsInfo(classpathJarRoots: List<VirtualFile>): RuntimeJarsInfo {
val kotlinCoreJars = ArrayList<KotlinLibraryFile>(2) val kotlinCoreJars = ArrayList<KotlinLibraryFile>(2)
for (jar in classpathJars) { for (jarRoot in classpathJarRoots) {
val manifest = try { val manifest = try {
val manifestFile = jar.findFileByRelativePath(MANIFEST_MF) ?: continue val manifestFile = jarRoot.findFileByRelativePath(MANIFEST_MF) ?: continue
Manifest(manifestFile.inputStream) Manifest(manifestFile.inputStream)
} }
catch (e: Exception) { catch (e: Exception) {
continue continue
} }
val runtimeComponent = getKotlinRuntimeComponent(jar, manifest) ?: continue val runtimeComponent = getKotlinRuntimeComponent(jarRoot, manifest) ?: continue
val version = manifest.getKotlinLanguageVersion() val version = manifest.getKotlinLanguageVersion()
if (runtimeComponent == KOTLIN_RUNTIME_COMPONENT_CORE) { if (runtimeComponent == KOTLIN_RUNTIME_COMPONENT_CORE) {
kotlinCoreJars.add(KotlinLibraryFile(runtimeComponent, jar, version)) val jarFile = VfsUtilCore.getVirtualFileForJar(jarRoot) ?: continue
kotlinCoreJars.add(KotlinLibraryFile(runtimeComponent, jarFile, version))
} }
} }
@@ -158,8 +158,8 @@ class KotlinCoreEnvironment private constructor(
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
if (messageCollector != null) { if (messageCollector != null) {
val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS) val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)
val classpathJars = initialRoots.mapNotNull { if (it.type == JavaRoot.RootType.BINARY) it.file else null } val classpathJarRoots = initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(messageCollector, languageVersionSettings, classpathJars) JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(messageCollector, languageVersionSettings, classpathJarRoots)
} }
// REPL and kapt2 update classpath dynamically // REPL and kapt2 update classpath dynamically
@@ -206,27 +206,26 @@ class KotlinCoreEnvironment private constructor(
} }
private fun Iterable<ContentRoot>.classpathRoots(): List<JavaRoot> = private fun Iterable<ContentRoot>.classpathRoots(): List<JavaRoot> =
filterIsInstance(JvmContentRoot::class.java).mapNotNull { filterIsInstance(JvmContentRoot::class.java).mapNotNull { javaRoot ->
javaRoot -> contentRootToVirtualFile(javaRoot)?.let { virtualFile -> contentRootToVirtualFile(javaRoot)?.let { virtualFile ->
val prefixPackageFqName = (javaRoot as? JavaSourceRoot)?.packagePrefix?.let {
val prefixPackageFqName = (javaRoot as? JavaSourceRoot)?.packagePrefix?.let { if (isValidJavaFqName(it)) {
if (isValidJavaFqName(it)) { FqName(it)
FqName(it) }
else {
report(WARNING, "Invalid package prefix name is ignored: $it")
null
}
} }
else {
report(WARNING, "Invalid package prefix name is ignored: $it") val rootType = when (javaRoot) {
null is JavaSourceRoot -> JavaRoot.RootType.SOURCE
is JvmClasspathRoot -> JavaRoot.RootType.BINARY
else -> throw IllegalStateException()
} }
}
val rootType = when (javaRoot) { JavaRoot(virtualFile, rootType, prefixPackageFqName)
is JavaSourceRoot -> JavaRoot.RootType.SOURCE
is JvmClasspathRoot -> JavaRoot.RootType.BINARY
else -> throw IllegalStateException()
} }
JavaRoot(virtualFile, rootType, prefixPackageFqName)
}
} }
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) { private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {