Infer API version from older runtime in compiler's classpath

For example, if you invoke kotlinc 1.1 with kotlin-stdlib 1.0 in the classpath,
we now infer -api-version 1.0 automatically
This commit is contained in:
Alexander Udalov
2017-02-15 19:19:46 +03:00
parent b56639a775
commit 71fcb07fad
5 changed files with 77 additions and 18 deletions
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.utils.StringsKt;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
@@ -280,7 +281,12 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
configuration.put(
CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS,
new LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(apiVersion), extraLanguageFeatures)
new LanguageVersionSettingsImpl(
languageVersion,
ApiVersion.createByLanguageVersion(apiVersion),
extraLanguageFeatures,
arguments.apiVersion != null
)
);
}
@@ -19,12 +19,11 @@ 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.CLICompiler
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.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.MavenComparableVersion
import org.jetbrains.kotlin.config.*
import java.io.IOException
import java.util.*
import java.util.jar.Attributes
@@ -113,29 +112,71 @@ object JvmRuntimeVersionsConsistencyChecker {
fun checkCompilerClasspathConsistency(
messageCollector: MessageCollector,
languageVersionSettings: LanguageVersionSettings?,
configuration: CompilerConfiguration,
classpathJarRoots: List<VirtualFile>
) {
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
if (runtimeJarsInfo.jars.isEmpty()) return
val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)
val apiVersion = languageVersionSettings?.apiVersion?.version ?: CURRENT_COMPILER_VERSION
val consistency = checkCompilerClasspathConsistency(messageCollector, apiVersion, runtimeJarsInfo)
if (consistency != ClasspathConsistency.Consistent) {
val message = when (consistency) {
is ClasspathConsistency.InconsistentWithApiVersion ->
"Runtime JAR files in the classpath have the version ${consistency.actualRuntimeVersion}, " +
if (consistency is ClasspathConsistency.InconsistentWithApiVersion) {
val actualRuntimeVersion = consistency.actualRuntimeVersion
messageCollector.issue(
null,
"Runtime JAR files in the classpath have the version $actualRuntimeVersion, " +
"which is older than the API version $apiVersion. " +
"Consider removing them from the classpath or passing '-api-version ${consistency.actualRuntimeVersion}' explicitly. " +
"You can also pass '-language-version ${consistency.actualRuntimeVersion}' instead, which will restrict " +
"Consider using the runtime of version $apiVersion, or pass '-api-version $actualRuntimeVersion' explicitly to " +
"restrict the available APIs to the runtime of version $actualRuntimeVersion. " +
"You can also pass '-language-version $actualRuntimeVersion' instead, which will restrict " +
"not only the APIs to the specified version, but also the language features. " +
"Alternatively, you can use '-Xskip-runtime-version-check' to suppress this warning"
else ->
)
val actualApi = ApiVersion.parse(actualRuntimeVersion.toString())
if (actualApi != null) {
val newSettings = if (languageVersionSettings == null) {
LanguageVersionSettingsImpl(
LanguageVersionSettingsImpl.DEFAULT.languageVersion,
actualApi,
listOf(LanguageFeature.WarnOnCoroutines)
)
}
else {
val inferredApiVersion =
if (@Suppress("DEPRECATION") languageVersionSettings.isApiVersionExplicit)
languageVersionSettings.apiVersion
else
minOf(languageVersionSettings.apiVersion, actualApi)
// "minOf" is needed in case when API version was inferred from language version and it's older than actualApi.
// For example, in "kotlinc-1.2 -language-version 1.0 -cp kotlin-runtime-1.1.jar" we should still infer API = 1.0
LanguageVersionSettingsImpl(
languageVersionSettings.languageVersion,
inferredApiVersion,
languageVersionSettings.additionalFeatures,
isApiVersionExplicit = false
)
}
messageCollector.issue(null, "Old runtime has been found in the classpath. " +
"Initial language version settings: $languageVersionSettings. " +
"Updated language version settings: $newSettings", CompilerMessageSeverity.LOGGING)
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, newSettings)
}
else {
messageCollector.issue(null, "Could not parse runtime JAR version: $actualRuntimeVersion")
}
}
else if (consistency != ClasspathConsistency.Consistent) {
messageCollector.issue(
null,
"Some runtime JAR files in the classpath have an incompatible version. " +
"Consider removing them from the classpath or use '-Xskip-runtime-version-check' to suppress this warning"
}
messageCollector.issue(null, message)
)
}
val librariesWithBundled = runtimeJarsInfo.otherLibrariesWithBundledRuntime
@@ -159,7 +159,7 @@ class KotlinCoreEnvironment private constructor(
if (messageCollector != null) {
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
messageCollector,
configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS),
configuration,
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
)
}