Check classpath version consistency.
Add 'Kotlin-version' attribute to kotlin-runtime and kotlin-reflect JARs.
This commit is contained in:
committed by
Alexander Udalov
parent
ab72460f28
commit
1866a33781
@@ -453,6 +453,8 @@
|
||||
|
||||
<manifest>
|
||||
<attribute name="Built-By" value="${manifest.impl.vendor}"/>
|
||||
<attribute name="${manifest.impl.attribute.kotlin.version}" value="${manifest.impl.value.kotlin.version}"/>
|
||||
<attribute name="${manifest.impl.attribute.kotlin.runtime.component}" value="${manifest.impl.value.kotlin.runtime.component.core}"/>
|
||||
|
||||
<attribute name="Implementation-Vendor" value="${manifest.impl.vendor}"/>
|
||||
<attribute name="Implementation-Title" value="${manifest.impl.title.kotlin.javascript.stdlib}"/>
|
||||
@@ -469,6 +471,8 @@
|
||||
|
||||
<manifest>
|
||||
<attribute name="Built-By" value="${manifest.impl.vendor}"/>
|
||||
<attribute name="${manifest.impl.attribute.kotlin.version}" value="${manifest.impl.value.kotlin.version}"/>
|
||||
<attribute name="${manifest.impl.attribute.kotlin.runtime.component}" value="${manifest.impl.value.kotlin.runtime.component.core}"/>
|
||||
|
||||
<attribute name="Implementation-Vendor" value="${manifest.impl.vendor}"/>
|
||||
<attribute name="Implementation-Title" value="${manifest.impl.title.kotlin.jvm.runtime.sources}"/>
|
||||
@@ -1155,6 +1159,9 @@
|
||||
|
||||
<manifest>
|
||||
<attribute name="Built-By" value="${manifest.impl.vendor}"/>
|
||||
<attribute name="${manifest.impl.attribute.kotlin.version}" value="${manifest.impl.value.kotlin.version}"/>
|
||||
<attribute name="${manifest.impl.attribute.kotlin.runtime.component}" value="${manifest.impl.value.kotlin.runtime.component.core}"/>
|
||||
|
||||
<attribute name="Implementation-Vendor" value="${manifest.impl.vendor}"/>
|
||||
<attribute name="Implementation-Title" value="@{implementation-title}"/>
|
||||
<attribute name="Implementation-Version" value="${build.number}"/>
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import java.util.*
|
||||
import java.util.jar.Manifest
|
||||
|
||||
internal inline fun Properties.getString(propertyName: String, otherwise: () -> String): String =
|
||||
getProperty(propertyName) ?: otherwise()
|
||||
|
||||
object JvmRuntimeVersionsConsistencyChecker {
|
||||
private val LOG = Logger.getInstance(JvmRuntimeVersionsConsistencyChecker::class.java)
|
||||
|
||||
private fun fatal(message: String): Nothing {
|
||||
LOG.error(message)
|
||||
throw AssertionError(message)
|
||||
}
|
||||
|
||||
private fun <T> T?.assertNotNull(message: () -> String): T =
|
||||
if (this == null) fatal(message()) else this
|
||||
|
||||
// TODO replace with ERROR after bootstrapping
|
||||
private val VERSION_ISSUE_SEVERITY = CompilerMessageSeverity.WARNING
|
||||
|
||||
private const val META_INF = "META-INF"
|
||||
private const val MANIFEST_MF = "$META_INF/MANIFEST.MF"
|
||||
|
||||
private const val MANIFEST_KOTLIN_VERSION_ATTRIBUTE = "manifest.impl.attribute.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_CORE = "manifest.impl.value.kotlin.runtime.component.core"
|
||||
|
||||
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 val KOTLIN_VERSION_ATTRIBUTE: String
|
||||
private val CURRENT_COMPILER_VERSION: LanguageVersion
|
||||
|
||||
private val KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE: String
|
||||
private val KOTLIN_RUNTIME_COMPONENT_CORE: String
|
||||
|
||||
init {
|
||||
val manifestProperties: Properties = try {
|
||||
JvmRuntimeVersionsConsistencyChecker::class.java
|
||||
.getResourceAsStream("/kotlinManifest.properties")
|
||||
.let { input -> Properties().apply { load(input) } }
|
||||
}
|
||||
catch (e: Exception) {
|
||||
LOG.error(e)
|
||||
throw e
|
||||
}
|
||||
|
||||
KOTLIN_VERSION_ATTRIBUTE = manifestProperties.getProperty(MANIFEST_KOTLIN_VERSION_ATTRIBUTE)
|
||||
.assertNotNull { "$MANIFEST_KOTLIN_VERSION_ATTRIBUTE not found in kotlinManifest.properties" }
|
||||
|
||||
CURRENT_COMPILER_VERSION = run {
|
||||
val kotlinVersionString = manifestProperties.getProperty(MANIFEST_KOTLIN_VERSION_VALUE)
|
||||
.assertNotNull { "$MANIFEST_KOTLIN_VERSION_VALUE not found in kotlinManifest.properties" }
|
||||
|
||||
LanguageVersion.fromFullVersionString(kotlinVersionString)
|
||||
.assertNotNull { "Incorrect Kotlin version: $kotlinVersionString" }
|
||||
}
|
||||
|
||||
if (CURRENT_COMPILER_VERSION != LanguageVersion.LATEST) {
|
||||
fatal("Kotlin compiler version $CURRENT_COMPILER_VERSION in kotlinManifest.properties doesn't match ${LanguageVersion.LATEST}")
|
||||
}
|
||||
|
||||
KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE = manifestProperties.getProperty(MANIFEST_KOTLIN_RUNTIME_COMPONENT)
|
||||
.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" }
|
||||
}
|
||||
|
||||
class FileWithLanguageVersion(val component: String, val file: VirtualFile, val version: LanguageVersion) {
|
||||
override fun toString(): String =
|
||||
"${file.name}:$version ($component)"
|
||||
}
|
||||
|
||||
class RuntimeJarsInfo(
|
||||
val coreJars: List<FileWithLanguageVersion>
|
||||
) {
|
||||
val hasAnyJarsToCheck: Boolean get() = coreJars.isNotEmpty()
|
||||
}
|
||||
|
||||
fun checkCompilerClasspathConsistency(
|
||||
messageCollector: MessageCollector,
|
||||
languageVersionSettings: LanguageVersionSettings?,
|
||||
classpathJars: List<VirtualFile>
|
||||
) {
|
||||
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJars)
|
||||
if (!runtimeJarsInfo.hasAnyJarsToCheck) return
|
||||
|
||||
val languageVersion = languageVersionSettings?.languageVersion ?: CURRENT_COMPILER_VERSION
|
||||
|
||||
// Even if language version option was explicitly specified, the JAR files SHOULD NOT be newer than the compiler.
|
||||
runtimeJarsInfo.coreJars.forEach {
|
||||
checkNotNewerThanCompiler(messageCollector, it)
|
||||
}
|
||||
|
||||
runtimeJarsInfo.coreJars.forEach {
|
||||
checkCompatibleWithLanguageVersion(messageCollector, it, languageVersion)
|
||||
}
|
||||
|
||||
checkMatchingVersions(messageCollector, runtimeJarsInfo)
|
||||
}
|
||||
|
||||
private fun checkNotNewerThanCompiler(messageCollector: MessageCollector, jar: FileWithLanguageVersion) {
|
||||
if (jar.version > CURRENT_COMPILER_VERSION) {
|
||||
messageCollector.issue("Run-time JAR file $jar is newer than compiler version $CURRENT_COMPILER_VERSION")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCompatibleWithLanguageVersion(messageCollector: MessageCollector, jar: FileWithLanguageVersion, languageVersion: LanguageVersion) {
|
||||
if (jar.version < languageVersion) {
|
||||
messageCollector.issue("Run-time JAR file $jar is older than required for language version $languageVersion")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMatchingVersions(messageCollector: MessageCollector, runtimeJarsInfo: RuntimeJarsInfo) {
|
||||
val oldestCoreJar = runtimeJarsInfo.coreJars.minBy { it.version } ?: return
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
private fun MessageCollector.issue(message: String) {
|
||||
report(VERSION_ISSUE_SEVERITY, message, CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
|
||||
private fun collectRuntimeJarsInfo(classpathJars: List<VirtualFile>): RuntimeJarsInfo {
|
||||
val kotlinCoreJars = ArrayList<FileWithLanguageVersion>(2)
|
||||
|
||||
for (jar in classpathJars) {
|
||||
val manifest = try {
|
||||
val manifestFile = jar.findFileByRelativePath(MANIFEST_MF) ?: continue
|
||||
Manifest(manifestFile.inputStream)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
continue
|
||||
}
|
||||
|
||||
val runtimeComponent = getKotlinRuntimeComponent(jar, manifest) ?: continue
|
||||
val version = manifest.getKotlinLanguageVersion()
|
||||
|
||||
if (runtimeComponent == KOTLIN_RUNTIME_COMPONENT_CORE) {
|
||||
kotlinCoreJars.add(FileWithLanguageVersion(runtimeComponent, jar, version))
|
||||
}
|
||||
}
|
||||
|
||||
return RuntimeJarsInfo(kotlinCoreJars)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun Manifest.getKotlinLanguageVersion(): LanguageVersion =
|
||||
mainAttributes.getValue(KOTLIN_VERSION_ATTRIBUTE)?.let {
|
||||
LanguageVersion.fromFullVersionString(it)
|
||||
}
|
||||
?: LanguageVersion.KOTLIN_1_0
|
||||
|
||||
}
|
||||
@@ -70,6 +70,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
|
||||
@@ -154,6 +155,13 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
val initialRoots = configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS).classpathRoots()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// REPL and kapt2 update classpath dynamically
|
||||
val indexFactory = JvmUpdateableDependenciesIndexFactory()
|
||||
|
||||
|
||||
@@ -34,10 +34,7 @@ import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.asJava.DuplicateJvmSignatureUtilKt;
|
||||
import org.jetbrains.kotlin.config.ApiVersion;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl;
|
||||
import org.jetbrains.kotlin.config.*;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.*;
|
||||
import org.jetbrains.kotlin.load.java.InternalFlexibleTypeTransformer;
|
||||
@@ -327,6 +324,13 @@ public abstract class BaseDiagnosticsTest
|
||||
return enabled != null ? enabled : LanguageVersionSettingsImpl.DEFAULT.supportsFeature(feature);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LanguageVersion getLanguageVersion() {
|
||||
// TODO provide base language version
|
||||
throw new UnsupportedOperationException("This instance of LanguageVersionSettings should be used for tests only");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ApiVersion getApiVersion() {
|
||||
|
||||
@@ -77,11 +77,12 @@ enum class LanguageVersion(val versionString: String) : DescriptionAware {
|
||||
interface LanguageVersionSettings {
|
||||
fun supportsFeature(feature: LanguageFeature): Boolean
|
||||
|
||||
val languageVersion: LanguageVersion
|
||||
val apiVersion: ApiVersion
|
||||
}
|
||||
|
||||
class LanguageVersionSettingsImpl @JvmOverloads constructor(
|
||||
private val languageVersion: LanguageVersion,
|
||||
override val languageVersion: LanguageVersion,
|
||||
override val apiVersion: ApiVersion,
|
||||
additionalFeatures: Collection<LanguageFeature> = emptySet()
|
||||
) : LanguageVersionSettings {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
manifest.impl.vendor=JetBrains
|
||||
|
||||
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.title.kotlin.compiler=Kotlin Compiler
|
||||
manifest.impl.title.kotlin.compiler.javadoc=Kotlin Compiler Javadoc
|
||||
manifest.impl.title.kotlin.compiler.sources=Kotlin Compiler Sources
|
||||
|
||||
Reference in New Issue
Block a user