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:
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.utils.StringsKt;
|
|||||||
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
@@ -280,7 +281,12 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
|||||||
|
|
||||||
configuration.put(
|
configuration.put(
|
||||||
CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS,
|
CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS,
|
||||||
new LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(apiVersion), extraLanguageFeatures)
|
new LanguageVersionSettingsImpl(
|
||||||
|
languageVersion,
|
||||||
|
ApiVersion.createByLanguageVersion(apiVersion),
|
||||||
|
extraLanguageFeatures,
|
||||||
|
arguments.apiVersion != null
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
-13
@@ -19,12 +19,11 @@ package org.jetbrains.kotlin.cli.jvm
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.vfs.VfsUtilCore
|
import com.intellij.openapi.vfs.VfsUtilCore
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
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.CompilerMessageLocation
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
import org.jetbrains.kotlin.config.ApiVersion
|
import org.jetbrains.kotlin.config.*
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
|
||||||
import org.jetbrains.kotlin.config.MavenComparableVersion
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
@@ -113,29 +112,71 @@ object JvmRuntimeVersionsConsistencyChecker {
|
|||||||
|
|
||||||
fun checkCompilerClasspathConsistency(
|
fun checkCompilerClasspathConsistency(
|
||||||
messageCollector: MessageCollector,
|
messageCollector: MessageCollector,
|
||||||
languageVersionSettings: LanguageVersionSettings?,
|
configuration: CompilerConfiguration,
|
||||||
classpathJarRoots: List<VirtualFile>
|
classpathJarRoots: List<VirtualFile>
|
||||||
) {
|
) {
|
||||||
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
|
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
|
||||||
if (runtimeJarsInfo.jars.isEmpty()) return
|
if (runtimeJarsInfo.jars.isEmpty()) return
|
||||||
|
|
||||||
|
val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)
|
||||||
val apiVersion = languageVersionSettings?.apiVersion?.version ?: CURRENT_COMPILER_VERSION
|
val apiVersion = languageVersionSettings?.apiVersion?.version ?: CURRENT_COMPILER_VERSION
|
||||||
|
|
||||||
val consistency = checkCompilerClasspathConsistency(messageCollector, apiVersion, runtimeJarsInfo)
|
val consistency = checkCompilerClasspathConsistency(messageCollector, apiVersion, runtimeJarsInfo)
|
||||||
if (consistency != ClasspathConsistency.Consistent) {
|
if (consistency is ClasspathConsistency.InconsistentWithApiVersion) {
|
||||||
val message = when (consistency) {
|
val actualRuntimeVersion = consistency.actualRuntimeVersion
|
||||||
is ClasspathConsistency.InconsistentWithApiVersion ->
|
messageCollector.issue(
|
||||||
"Runtime JAR files in the classpath have the version ${consistency.actualRuntimeVersion}, " +
|
null,
|
||||||
|
"Runtime JAR files in the classpath have the version $actualRuntimeVersion, " +
|
||||||
"which is older than the API version $apiVersion. " +
|
"which is older than the API version $apiVersion. " +
|
||||||
"Consider removing them from the classpath or passing '-api-version ${consistency.actualRuntimeVersion}' explicitly. " +
|
"Consider using the runtime of version $apiVersion, or pass '-api-version $actualRuntimeVersion' explicitly to " +
|
||||||
"You can also pass '-language-version ${consistency.actualRuntimeVersion}' instead, which will restrict " +
|
"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. " +
|
"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"
|
"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. " +
|
"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"
|
"Consider removing them from the classpath or use '-Xskip-runtime-version-check' to suppress this warning"
|
||||||
}
|
)
|
||||||
messageCollector.issue(null, message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val librariesWithBundled = runtimeJarsInfo.otherLibrariesWithBundledRuntime
|
val librariesWithBundled = runtimeJarsInfo.otherLibrariesWithBundledRuntime
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
if (messageCollector != null) {
|
if (messageCollector != null) {
|
||||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||||
messageCollector,
|
messageCollector,
|
||||||
configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS),
|
configuration,
|
||||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.psi.KtDeclaration
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
@@ -108,6 +107,12 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
|
|||||||
|
|
||||||
override fun supportsFeature(feature: LanguageFeature): Boolean =
|
override fun supportsFeature(feature: LanguageFeature): Boolean =
|
||||||
languageFeatures[feature] ?: delegate.supportsFeature(feature)
|
languageFeatures[feature] ?: delegate.supportsFeature(feature)
|
||||||
|
|
||||||
|
override val additionalFeatures: Collection<LanguageFeature>
|
||||||
|
get() = error("Must not be called")
|
||||||
|
|
||||||
|
override val isApiVersionExplicit: Boolean
|
||||||
|
get() = error("Must not be called")
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class TestFile(
|
inner class TestFile(
|
||||||
|
|||||||
@@ -99,14 +99,21 @@ interface LanguageVersionSettings {
|
|||||||
|
|
||||||
// Please do not use this to enable/disable specific features/checks. Instead add a new LanguageFeature entry and call supportsFeature
|
// Please do not use this to enable/disable specific features/checks. Instead add a new LanguageFeature entry and call supportsFeature
|
||||||
val languageVersion: LanguageVersion
|
val languageVersion: LanguageVersion
|
||||||
|
|
||||||
|
// TODO: refactor arguments related to coroutines so that this list is empty by default
|
||||||
|
val additionalFeatures: Collection<LanguageFeature>
|
||||||
|
|
||||||
|
@Deprecated("This is a temporary solution, please do not use.")
|
||||||
|
val isApiVersionExplicit: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class LanguageVersionSettingsImpl @JvmOverloads constructor(
|
class LanguageVersionSettingsImpl @JvmOverloads constructor(
|
||||||
override val languageVersion: LanguageVersion,
|
override val languageVersion: LanguageVersion,
|
||||||
override val apiVersion: ApiVersion,
|
override val apiVersion: ApiVersion,
|
||||||
additionalFeatures: Collection<LanguageFeature> = emptySet()
|
additionalFeatures: Collection<LanguageFeature> = emptySet(),
|
||||||
|
override val isApiVersionExplicit: Boolean = false
|
||||||
) : LanguageVersionSettings {
|
) : LanguageVersionSettings {
|
||||||
private val additionalFeatures = additionalFeatures.toSet()
|
override val additionalFeatures = additionalFeatures.toSet()
|
||||||
|
|
||||||
override fun supportsFeature(feature: LanguageFeature): Boolean {
|
override fun supportsFeature(feature: LanguageFeature): Boolean {
|
||||||
val since = feature.sinceVersion
|
val since = feature.sinceVersion
|
||||||
|
|||||||
Reference in New Issue
Block a user