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 "")
companion object {
@JvmField val NO_LOCATION: CompilerMessageLocation = CompilerMessageLocation(null, -1, -1, null)
@JvmField
val NO_LOCATION: CompilerMessageLocation = create(null)
@JvmStatic fun create(
path: String?,
line: Int,
column: Int,
lineContent: String?
): CompilerMessageLocation =
@JvmStatic
fun create(path: String?): CompilerMessageLocation =
CompilerMessageLocation(path, -1, -1, null)
@JvmStatic
fun create(path: String?, line: Int, column: Int, lineContent: String?): CompilerMessageLocation =
if (path == null) NO_LOCATION else CompilerMessageLocation(path, line, column, lineContent)
private val serialVersionUID: Long = 8228357578L
@@ -229,9 +229,8 @@ class AnalyzerWithCompilerReport(private val messageCollector: MessageCollector)
severity,
"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,
CompilerMessageLocation.create(toSystemDependentName(data.filePath), -1, -1, null)
CompilerMessageLocation.create(toSystemDependentName(data.filePath))
)
}
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.cli.jvm
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -106,9 +107,9 @@ object JvmRuntimeVersionsConsistencyChecker {
fun checkCompilerClasspathConsistency(
messageCollector: MessageCollector,
languageVersionSettings: LanguageVersionSettings?,
classpathJars: List<VirtualFile>
classpathJarRoots: List<VirtualFile>
) {
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJars)
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
if (!runtimeJarsInfo.hasAnyJarsToCheck) return
val languageVersion =
@@ -128,13 +129,13 @@ object JvmRuntimeVersionsConsistencyChecker {
private fun checkNotNewerThanCompiler(messageCollector: MessageCollector, jar: KotlinLibraryFile) {
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) {
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
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) {
report(VERSION_ISSUE_SEVERITY, message, CompilerMessageLocation.NO_LOCATION)
private fun MessageCollector.issue(file: VirtualFile?, message: String) {
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)
for (jar in classpathJars) {
for (jarRoot in classpathJarRoots) {
val manifest = try {
val manifestFile = jar.findFileByRelativePath(MANIFEST_MF) ?: continue
val manifestFile = jarRoot.findFileByRelativePath(MANIFEST_MF) ?: continue
Manifest(manifestFile.inputStream)
}
catch (e: Exception) {
continue
}
val runtimeComponent = getKotlinRuntimeComponent(jar, manifest) ?: continue
val runtimeComponent = getKotlinRuntimeComponent(jarRoot, manifest) ?: continue
val version = manifest.getKotlinLanguageVersion()
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)
if (messageCollector != null) {
val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)
val classpathJars = initialRoots.mapNotNull { if (it.type == JavaRoot.RootType.BINARY) it.file else null }
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(messageCollector, languageVersionSettings, classpathJars)
val classpathJarRoots = initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(messageCollector, languageVersionSettings, classpathJarRoots)
}
// REPL and kapt2 update classpath dynamically
@@ -206,27 +206,26 @@ class KotlinCoreEnvironment private constructor(
}
private fun Iterable<ContentRoot>.classpathRoots(): List<JavaRoot> =
filterIsInstance(JvmContentRoot::class.java).mapNotNull {
javaRoot -> contentRootToVirtualFile(javaRoot)?.let { virtualFile ->
val prefixPackageFqName = (javaRoot as? JavaSourceRoot)?.packagePrefix?.let {
if (isValidJavaFqName(it)) {
FqName(it)
filterIsInstance(JvmContentRoot::class.java).mapNotNull { javaRoot ->
contentRootToVirtualFile(javaRoot)?.let { virtualFile ->
val prefixPackageFqName = (javaRoot as? JavaSourceRoot)?.packagePrefix?.let {
if (isValidJavaFqName(it)) {
FqName(it)
}
else {
report(WARNING, "Invalid package prefix name is ignored: $it")
null
}
}
else {
report(WARNING, "Invalid package prefix name is ignored: $it")
null
val rootType = when (javaRoot) {
is JavaSourceRoot -> JavaRoot.RootType.SOURCE
is JvmClasspathRoot -> JavaRoot.RootType.BINARY
else -> throw IllegalStateException()
}
}
val rootType = when (javaRoot) {
is JavaSourceRoot -> JavaRoot.RootType.SOURCE
is JvmClasspathRoot -> JavaRoot.RootType.BINARY
else -> throw IllegalStateException()
JavaRoot(virtualFile, rootType, prefixPackageFqName)
}
JavaRoot(virtualFile, rootType, prefixPackageFqName)
}
}
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {