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_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 = "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_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_STDLIB_MODULE = "$META_INF/kotlin-stdlib.kotlin_module"
private const val KOTLIN_REFLECT_MODULE = "$META_INF/kotlin-reflection.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_ATTRIBUTE: String
private val KOTLIN_RUNTIME_COMPONENT_CORE: String private val KOTLIN_RUNTIME_COMPONENT_CORE: String
private val KOTLIN_RUNTIME_COMPONENT_MAIN: String
init { init {
val manifestProperties: Properties = try { val manifestProperties: Properties = try {
@@ -90,6 +92,8 @@ object JvmRuntimeVersionsConsistencyChecker {
.assertNotNull { "$MANIFEST_KOTLIN_RUNTIME_COMPONENT not found in kotlinManifest.properties" } .assertNotNull { "$MANIFEST_KOTLIN_RUNTIME_COMPONENT not found in kotlinManifest.properties" }
KOTLIN_RUNTIME_COMPONENT_CORE = manifestProperties.getProperty(MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE) KOTLIN_RUNTIME_COMPONENT_CORE = manifestProperties.getProperty(MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE)
.assertNotNull { "$MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE not found in kotlinManifest.properties" } .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) { private class KotlinLibraryFile(val component: String, val file: VirtualFile, val version: MavenComparableVersion) {
@@ -98,10 +102,11 @@ object JvmRuntimeVersionsConsistencyChecker {
} }
private class RuntimeJarsInfo( 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 coreJars: List<KotlinLibraryFile>
) { )
val hasAnyJarsToCheck: Boolean get() = coreJars.isNotEmpty()
}
fun checkCompilerClasspathConsistency( fun checkCompilerClasspathConsistency(
messageCollector: MessageCollector, messageCollector: MessageCollector,
@@ -109,13 +114,13 @@ object JvmRuntimeVersionsConsistencyChecker {
classpathJarRoots: List<VirtualFile> classpathJarRoots: List<VirtualFile>
) { ) {
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots) val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
if (runtimeJarsInfo.hasAnyJarsToCheck) { if (runtimeJarsInfo.jars.isEmpty()) return
val languageVersion = languageVersionSettings?.let { MavenComparableVersion(it.languageVersion) } ?: CURRENT_COMPILER_VERSION
if (checkCompilerClasspathConsistency(messageCollector, languageVersion, runtimeJarsInfo)) { val languageVersion = languageVersionSettings?.let { MavenComparableVersion(it.languageVersion) } ?: CURRENT_COMPILER_VERSION
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") 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, languageVersion: MavenComparableVersion,
runtimeJarsInfo: RuntimeJarsInfo runtimeJarsInfo: RuntimeJarsInfo
): Boolean { ): 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 { if (runtimeJarsInfo.coreJars.map {
checkNotNewerThanCompiler(messageCollector, it) checkNotNewerThanCompiler(messageCollector, it)
}.any { it }) return true }.any { it }) return true
if (runtimeJarsInfo.coreJars.map { if (runtimeJarsInfo.jars.map {
checkCompatibleWithLanguageVersion(messageCollector, it, languageVersion) checkCompatibleWithLanguageVersion(messageCollector, it, languageVersion)
}.any { it }) return true }.any { it }) return true
@@ -155,13 +163,13 @@ object JvmRuntimeVersionsConsistencyChecker {
} }
private fun checkMatchingVersions(messageCollector: MessageCollector, runtimeJarsInfo: RuntimeJarsInfo): Boolean { private fun checkMatchingVersions(messageCollector: MessageCollector, runtimeJarsInfo: RuntimeJarsInfo): Boolean {
val oldestCoreJar = runtimeJarsInfo.coreJars.minBy { it.version } ?: return false val oldestJar = runtimeJarsInfo.jars.minBy { it.version } ?: return false
val newestCoreJar = runtimeJarsInfo.coreJars.maxBy { 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 { messageCollector.issue(null, buildString {
appendln("Runtime JAR files in the classpath must have the same version. These files were found in the classpath:") 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})") appendln(" ${jar.file.path} (version ${jar.version})")
} }
}.trimEnd()) }.trimEnd())
@@ -176,7 +184,8 @@ object JvmRuntimeVersionsConsistencyChecker {
} }
private fun collectRuntimeJarsInfo(classpathJarRoots: List<VirtualFile>): RuntimeJarsInfo { 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) { for (jarRoot in classpathJarRoots) {
val manifest = try { val manifest = try {
@@ -190,20 +199,26 @@ object JvmRuntimeVersionsConsistencyChecker {
val runtimeComponent = getKotlinRuntimeComponent(jarRoot, manifest) ?: continue val runtimeComponent = getKotlinRuntimeComponent(jarRoot, manifest) ?: continue
val version = manifest.getKotlinLanguageVersion() val version = manifest.getKotlinLanguageVersion()
val jarFile = VfsUtilCore.getVirtualFileForJar(jarRoot) ?: continue
val file = KotlinLibraryFile(runtimeComponent, jarFile, version)
if (runtimeComponent == KOTLIN_RUNTIME_COMPONENT_CORE) { if (runtimeComponent == KOTLIN_RUNTIME_COMPONENT_CORE) {
val jarFile = VfsUtilCore.getVirtualFileForJar(jarRoot) ?: continue jars.add(file)
kotlinCoreJars.add(KotlinLibraryFile(runtimeComponent, jarFile, version)) 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? { private fun getKotlinRuntimeComponent(jar: VirtualFile, manifest: Manifest): String? {
manifest.mainAttributes.getValue(KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE)?.let { return it } 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_STDLIB_MODULE) != null) return KOTLIN_RUNTIME_COMPONENT_MAIN
if (jar.findFileByRelativePath(KOTLIN_REFLECT_MODULE) != null) return KOTLIN_RUNTIME_COMPONENT_CORE if (jar.findFileByRelativePath(KOTLIN_REFLECT_MODULE) != null) return KOTLIN_RUNTIME_COMPONENT_MAIN
return null 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.value.kotlin.version=1.1
manifest.impl.attribute.kotlin.runtime.component=Kotlin-Runtime-Component manifest.impl.attribute.kotlin.runtime.component=Kotlin-Runtime-Component
manifest.impl.value.kotlin.runtime.component.core=Core 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=Kotlin Compiler
manifest.impl.title.kotlin.compiler.javadoc=Kotlin Compiler Javadoc manifest.impl.title.kotlin.compiler.javadoc=Kotlin Compiler Javadoc