Support "Main" component in JVM runtime version consistency checker

The point here is to disable the "checkNotNewerThanCompiler" check because it
breaks a real life use case of compiling with an old compiler against the new
runtime library (that may come implicitly as a transitive dependency of another
library). However, to keep the possibility of restoring this check in the
future without backporting the needed changes into the old branches, we now
only perform this check for the "Core" runtime components, and all other checks
-- for "Core" and "Main" runtime components. In the follow-up commit, we change
the runtime component of our libraries to "Main". If we decide we need the
"checkNotNewerThanCompiler" behavior in the future, we can change the runtime
component to "Core", effectively restoring this check in the old compilers
This commit is contained in:
Alexander Udalov
2017-01-20 20:29:40 +03:00
parent 7b4e826650
commit 139d37cfd0
2 changed files with 37 additions and 21 deletions
@@ -51,6 +51,7 @@ object JvmRuntimeVersionsConsistencyChecker {
private const val MANIFEST_KOTLIN_VERSION_VALUE = "manifest.impl.value.kotlin.version"
private const val MANIFEST_KOTLIN_RUNTIME_COMPONENT = "manifest.impl.attribute.kotlin.runtime.component"
private const val MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE = "manifest.impl.value.kotlin.runtime.component.core"
private const val MANIFEST_KOTLIN_RUNTIME_COMPONENT_MAIN = "manifest.impl.value.kotlin.runtime.component.main"
private const val KOTLIN_STDLIB_MODULE = "$META_INF/kotlin-stdlib.kotlin_module"
private const val KOTLIN_REFLECT_MODULE = "$META_INF/kotlin-reflection.kotlin_module"
@@ -60,6 +61,7 @@ object JvmRuntimeVersionsConsistencyChecker {
private val KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE: String
private val KOTLIN_RUNTIME_COMPONENT_CORE: String
private val KOTLIN_RUNTIME_COMPONENT_MAIN: String
init {
val manifestProperties: Properties = try {
@@ -90,6 +92,8 @@ object JvmRuntimeVersionsConsistencyChecker {
.assertNotNull { "$MANIFEST_KOTLIN_RUNTIME_COMPONENT not found in kotlinManifest.properties" }
KOTLIN_RUNTIME_COMPONENT_CORE = manifestProperties.getProperty(MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE)
.assertNotNull { "$MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE not found in kotlinManifest.properties" }
KOTLIN_RUNTIME_COMPONENT_MAIN = manifestProperties.getProperty(MANIFEST_KOTLIN_RUNTIME_COMPONENT_MAIN)
.assertNotNull { "$MANIFEST_KOTLIN_RUNTIME_COMPONENT_MAIN not found in kotlinManifest.properties" }
}
private class KotlinLibraryFile(val component: String, val file: VirtualFile, val version: MavenComparableVersion) {
@@ -98,10 +102,11 @@ object JvmRuntimeVersionsConsistencyChecker {
}
private class RuntimeJarsInfo(
// Runtime jars with components "Main" and "Core"
val jars: List<KotlinLibraryFile>,
// Runtime jars with components "Core" only (a subset of [jars])
val coreJars: List<KotlinLibraryFile>
) {
val hasAnyJarsToCheck: Boolean get() = coreJars.isNotEmpty()
}
)
fun checkCompilerClasspathConsistency(
messageCollector: MessageCollector,
@@ -109,13 +114,13 @@ object JvmRuntimeVersionsConsistencyChecker {
classpathJarRoots: List<VirtualFile>
) {
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
if (runtimeJarsInfo.hasAnyJarsToCheck) {
val languageVersion = languageVersionSettings?.let { MavenComparableVersion(it.languageVersion) } ?: CURRENT_COMPILER_VERSION
if (runtimeJarsInfo.jars.isEmpty()) return
if (checkCompilerClasspathConsistency(messageCollector, languageVersion, runtimeJarsInfo)) {
messageCollector.issue(null, "Some runtime JAR files in the classpath have an incompatible version. " +
"Remove them from the classpath or use '-Xskip-runtime-version-check' to suppress errors")
}
val languageVersion = languageVersionSettings?.let { MavenComparableVersion(it.languageVersion) } ?: CURRENT_COMPILER_VERSION
if (checkCompilerClasspathConsistency(messageCollector, languageVersion, runtimeJarsInfo)) {
messageCollector.issue(null, "Some runtime JAR files in the classpath have an incompatible version. " +
"Remove them from the classpath or use '-Xskip-runtime-version-check' to suppress errors")
}
}
@@ -124,12 +129,15 @@ object JvmRuntimeVersionsConsistencyChecker {
languageVersion: MavenComparableVersion,
runtimeJarsInfo: RuntimeJarsInfo
): Boolean {
// Even if language version option was explicitly specified, the JAR files SHOULD NOT be newer than the compiler.
// The "Core" jar files should not be newer than the compiler. This behavior is reserved for the future if we realise that we're
// going to break language/library compatibility in such a way that it's easier to make the old compiler just report an error
// in the case the new runtime library is specified in the classpath, rather than employing any other compatibility breakage tools
// we have at our disposal (Deprecated, SinceKotlin, SinceKotlinInfo in metadata, etc.)
if (runtimeJarsInfo.coreJars.map {
checkNotNewerThanCompiler(messageCollector, it)
}.any { it }) return true
if (runtimeJarsInfo.coreJars.map {
if (runtimeJarsInfo.jars.map {
checkCompatibleWithLanguageVersion(messageCollector, it, languageVersion)
}.any { it }) return true
@@ -155,13 +163,13 @@ object JvmRuntimeVersionsConsistencyChecker {
}
private fun checkMatchingVersions(messageCollector: MessageCollector, runtimeJarsInfo: RuntimeJarsInfo): Boolean {
val oldestCoreJar = runtimeJarsInfo.coreJars.minBy { it.version } ?: return false
val newestCoreJar = runtimeJarsInfo.coreJars.maxBy { it.version } ?: return false
val oldestJar = runtimeJarsInfo.jars.minBy { it.version } ?: return false
val newestJar = runtimeJarsInfo.jars.maxBy { it.version } ?: return false
if (oldestCoreJar.version != newestCoreJar.version) {
if (oldestJar.version != newestJar.version) {
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) {
for (jar in runtimeJarsInfo.jars) {
appendln(" ${jar.file.path} (version ${jar.version})")
}
}.trimEnd())
@@ -176,7 +184,8 @@ object JvmRuntimeVersionsConsistencyChecker {
}
private fun collectRuntimeJarsInfo(classpathJarRoots: List<VirtualFile>): RuntimeJarsInfo {
val kotlinCoreJars = ArrayList<KotlinLibraryFile>(2)
val jars = ArrayList<KotlinLibraryFile>(2)
val coreJars = ArrayList<KotlinLibraryFile>(2)
for (jarRoot in classpathJarRoots) {
val manifest = try {
@@ -190,20 +199,26 @@ object JvmRuntimeVersionsConsistencyChecker {
val runtimeComponent = getKotlinRuntimeComponent(jarRoot, manifest) ?: continue
val version = manifest.getKotlinLanguageVersion()
val jarFile = VfsUtilCore.getVirtualFileForJar(jarRoot) ?: continue
val file = KotlinLibraryFile(runtimeComponent, jarFile, version)
if (runtimeComponent == KOTLIN_RUNTIME_COMPONENT_CORE) {
val jarFile = VfsUtilCore.getVirtualFileForJar(jarRoot) ?: continue
kotlinCoreJars.add(KotlinLibraryFile(runtimeComponent, jarFile, version))
jars.add(file)
coreJars.add(file)
}
else if (runtimeComponent == KOTLIN_RUNTIME_COMPONENT_MAIN) {
jars.add(file)
}
}
return RuntimeJarsInfo(kotlinCoreJars)
return RuntimeJarsInfo(jars, coreJars)
}
private fun getKotlinRuntimeComponent(jar: VirtualFile, manifest: Manifest): String? {
manifest.mainAttributes.getValue(KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE)?.let { return it }
if (jar.findFileByRelativePath(KOTLIN_STDLIB_MODULE) != null) return KOTLIN_RUNTIME_COMPONENT_CORE
if (jar.findFileByRelativePath(KOTLIN_REFLECT_MODULE) != null) return KOTLIN_RUNTIME_COMPONENT_CORE
if (jar.findFileByRelativePath(KOTLIN_STDLIB_MODULE) != null) return KOTLIN_RUNTIME_COMPONENT_MAIN
if (jar.findFileByRelativePath(KOTLIN_REFLECT_MODULE) != null) return KOTLIN_RUNTIME_COMPONENT_MAIN
return null
}
+1
View File
@@ -4,6 +4,7 @@ manifest.impl.attribute.kotlin.version=Kotlin-Version
manifest.impl.value.kotlin.version=1.1
manifest.impl.attribute.kotlin.runtime.component=Kotlin-Runtime-Component
manifest.impl.value.kotlin.runtime.component.core=Core
manifest.impl.value.kotlin.runtime.component.main=Main
manifest.impl.title.kotlin.compiler=Kotlin Compiler
manifest.impl.title.kotlin.compiler.javadoc=Kotlin Compiler Javadoc