From 73f0540d5bef3954c25ae23c67cee05e24c909e6 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 2 Sep 2021 01:06:44 +0200 Subject: [PATCH] Remove runtime jar files version checker ... and the compiler argument -Xskip-runtime-version-check. The vast majority of warnings reported by this checker in practice has proven to be false positives. In addition to that, it was needlessly verbose, and also completely untested. If we decide to reintroduce some of these checks, it's probably a better idea to perform them in tools, such as Kotlin Gradle plugin, which usually have slightly more information about the way the project is built and can suggest some meaningful solutions (as opposed to "remove this jar $HOME/.gradle/... from the classpath" which was the best JvmRuntimeVersionsConsistencyChecker could do.) #KT-27256 Obsolete #KT-41664 Fixed --- .../arguments/K2JVMCompilerArguments.kt | 6 - .../JvmRuntimeVersionsConsistencyChecker.kt | 363 ------------------ .../cli/jvm/compiler/KotlinCoreEnvironment.kt | 12 - .../jetbrains/kotlin/cli/jvm/jvmArguments.kt | 1 - .../kotlin/config/JVMConfigurationKeys.java | 3 - compiler/testData/cli/jvm/extraHelp.out | 1 - 6 files changed, 386 deletions(-) delete mode 100644 compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index 26c492e71ff..109806b24f6 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -194,12 +194,6 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xuse-type-table", description = "Use type table in metadata serialization") var useTypeTable: Boolean by FreezableVar(false) - @Argument( - value = "-Xskip-runtime-version-check", - description = "Allow Kotlin runtime libraries of incompatible versions in the classpath" - ) - var skipRuntimeVersionCheck: Boolean by FreezableVar(false) - @Argument( value = "-Xuse-old-class-files-reading", description = "Use old class files reading implementation. This may slow down the build and cause problems with Groovy interop.\n" + diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt deleted file mode 100644 index 01977a80dd7..00000000000 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright 2010-2017 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.VfsUtilCore -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.* -import java.io.IOException -import java.util.* -import java.util.jar.Attributes -import java.util.jar.Manifest - -object JvmRuntimeVersionsConsistencyChecker { - private val LOG = Logger.getInstance(JvmRuntimeVersionsConsistencyChecker::class.java) - - private fun T?.assertNotNull(lazyMessage: () -> String): T = - this ?: lazyMessage().let { message -> - LOG.error(message) - throw AssertionError(message) - } - - 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_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_STDLIB_JRE_7_MODULE = "$META_INF/kotlin-stdlib-jre7.kotlin_module" - private const val KOTLIN_STDLIB_JRE_8_MODULE = "$META_INF/kotlin-stdlib-jre8.kotlin_module" - private const val KOTLIN_REFLECT_MODULE = "$META_INF/kotlin-reflection.kotlin_module" - - private val RUNTIME_IMPLEMENTATION_TITLES = setOf( - "kotlin-runtime", "kotlin-stdlib", "kotlin-reflect", "Kotlin Runtime", "Kotlin Standard Library", "Kotlin Reflect" - ) - - private val KOTLIN_VERSION_ATTRIBUTE: String - 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 { - 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" } - 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" } - 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 file: VirtualFile, val version: MavenComparableVersion) { - override fun toString(): String = - "${file.name}:$version" - } - - private class RuntimeJarsInfo( - // Runtime jars with components "Main" and "Core" - val jars: List, - // Runtime jars with components "Core" only (a subset of [jars]) - val coreJars: List, - // Library jars which have some Kotlin Runtime library bundled into them - val otherLibrariesWithBundledRuntime: List, - val stdlibJre7: List, - val stdlibJre8: List - ) - - fun checkCompilerClasspathConsistency( - messageCollector: MessageCollector, - configuration: CompilerConfiguration, - classpathJarRoots: List - ) { - val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots) - if (runtimeJarsInfo.jars.isEmpty()) return - - val languageVersionSettings = configuration.languageVersionSettings - val currentApi = languageVersionSettings.apiVersion - - val consistency = checkCompilerClasspathConsistency(messageCollector, currentApi.version, runtimeJarsInfo) - if (consistency is ClasspathConsistency.InconsistentWithApiVersion) { - val actualRuntimeVersion = consistency.actualRuntimeVersion - if (currentApi.isStable) { - messageCollector.issue( - null, - "Runtime JAR files in the classpath have the version $actualRuntimeVersion, " + - "which is older than the API version ${currentApi.version}. " + - "Consider using the runtime of version ${currentApi.version}, 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" - ) - - for (jar in consistency.incompatibleJars) { - messageCollector.issue( - jar.file, - "Runtime JAR file has version ${jar.version} which is older than required for API version ${currentApi.version}" - ) - } - } - - val actualApi = ApiVersion.parse(actualRuntimeVersion.toString()) - if (actualApi == null) { - messageCollector.issue(null, "Could not parse runtime JAR version: $actualRuntimeVersion") - } else if (!languageVersionSettings.getFlag(AnalysisFlags.explicitApiVersion) && actualApi < currentApi) { - // If there's no explicit "-api-version" AND there's an old stdlib in the classpath (older than the default value of API), - // we infer API = the version of that stdlib. - // Note that "no explicit -api-version" requirement is necessary because for example, in - // "kotlinc-1.2 -language-version 1.0 -cp kotlin-runtime-1.1.jar" we should still infer API = 1.0 - configuration.languageVersionSettings = object : LanguageVersionSettings by languageVersionSettings { - override val apiVersion: ApiVersion get() = actualApi - } - - messageCollector.issue( - null, "Old runtime has been found in the classpath. " + - "Initial language version settings: $languageVersionSettings. " + - "Updated API version: $actualApi", CompilerMessageSeverity.LOGGING - ) - } - } 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" - ) - } - - if (configuration.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_2) { - for (stdlibJre7 in runtimeJarsInfo.stdlibJre7) { - messageCollector.issue(stdlibJre7.file, "kotlin-stdlib-jre7 is deprecated. Please use kotlin-stdlib-jdk7 instead") - } - for (stdlibJre8 in runtimeJarsInfo.stdlibJre8) { - messageCollector.issue(stdlibJre8.file, "kotlin-stdlib-jre8 is deprecated. Please use kotlin-stdlib-jdk8 instead") - } - } - - val librariesWithBundled = runtimeJarsInfo.otherLibrariesWithBundledRuntime - if (librariesWithBundled.isNotEmpty()) { - messageCollector.issue( - null, - "Some JAR files in the classpath have the Kotlin Runtime library bundled into them. " + - "This may cause difficult to debug problems if there's a different version of the Kotlin Runtime library in the classpath. " + - "Consider removing these libraries from the classpath" - ) - - for (library in librariesWithBundled) { - messageCollector.issue(library, "Library has Kotlin runtime bundled into it") - } - } - } - - private sealed class ClasspathConsistency { - object Consistent : ClasspathConsistency() - class InconsistentWithApiVersion( - val actualRuntimeVersion: MavenComparableVersion, - val incompatibleJars: List - ) : ClasspathConsistency() - - object InconsistentWithCompilerVersion : ClasspathConsistency() - object InconsistentBecauseOfRuntimesWithDifferentVersions : ClasspathConsistency() - } - - private fun checkCompilerClasspathConsistency( - messageCollector: MessageCollector, - apiVersion: MavenComparableVersion, - runtimeJarsInfo: RuntimeJarsInfo - ): ClasspathConsistency { - // 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, VersionRequirement in metadata, etc.) - if (runtimeJarsInfo.coreJars.map { - checkNotNewerThanCompiler(messageCollector, it) - }.any { it }) return ClasspathConsistency.InconsistentWithCompilerVersion - - val jars = runtimeJarsInfo.jars - if (jars.isEmpty()) return ClasspathConsistency.Consistent - - val runtimeVersion = checkMatchingVersionsAndGetRuntimeVersion(messageCollector, jars) - ?: return ClasspathConsistency.InconsistentBecauseOfRuntimesWithDifferentVersions - - val jarsIncompatibleWithApiVersion = jars.filter { it.version < apiVersion } - if (jarsIncompatibleWithApiVersion.isNotEmpty()) { - return ClasspathConsistency.InconsistentWithApiVersion(runtimeVersion, jarsIncompatibleWithApiVersion) - } - - return ClasspathConsistency.Consistent - } - - private fun checkNotNewerThanCompiler(messageCollector: MessageCollector, jar: KotlinLibraryFile): Boolean { - if (jar.version > ApiVersion.LATEST_STABLE.version) { - messageCollector.issue( - jar.file, - "Runtime JAR file has version ${jar.version} which is newer than compiler version ${ApiVersion.LATEST_STABLE.version}", - CompilerMessageSeverity.ERROR - ) - return true - } - return false - } - - // Returns the version if it's the same across all jars, or null if versions of some jars differ. - private fun checkMatchingVersionsAndGetRuntimeVersion( - messageCollector: MessageCollector, - jars: List - ): MavenComparableVersion? { - assert(jars.isNotEmpty()) { "'jars' must not be empty" } - val oldestVersion = jars.minOf { it.version } - val newestVersion = jars.maxOf { it.version } - - // If the oldest version is the same as the newest version, then all jars have the same version - if (oldestVersion == newestVersion) return oldestVersion - - messageCollector.issue(null, buildString { - append("Runtime JAR files in the classpath should have the same version. These files were found in the classpath:\n") - for (jar in jars) { - append(" ${jar.file.path} (version ${jar.version})\n") - } - }.trimEnd()) - - // If there's kotlin-stdlib of version X in the classpath and kotlin-reflect of version Y < X, - // we suggest to provide an explicit dependency on version X. - // TODO: report this depending on the content of the jars instead - val minReflectJar = - jars.filter { it.file.name.startsWith("kotlin-reflect") }.minByOrNull { it.version } - val maxStdlibJar = - jars.filter { it.file.name.startsWith("kotlin-runtime") || it.file.name.startsWith("kotlin-stdlib") }.maxByOrNull { it.version } - if (minReflectJar != null && maxStdlibJar != null && minReflectJar.version < maxStdlibJar.version) { - messageCollector.issue( - null, - "Consider providing an explicit dependency on kotlin-reflect ${maxStdlibJar.version} to prevent strange errors" - ) - } - - return null - } - - private fun MessageCollector.issue( - file: VirtualFile?, - message: String, - severity: CompilerMessageSeverity = CompilerMessageSeverity.STRONG_WARNING - ) { - report(severity, message, CompilerMessageLocation.create(file?.let(VfsUtilCore::virtualToIoFile)?.path)) - } - - private fun collectRuntimeJarsInfo(classpathJarRoots: List): RuntimeJarsInfo { - val jars = ArrayList(2) - val coreJars = ArrayList(2) - val otherLibrariesWithBundledRuntime = ArrayList(0) - val stdlibJre7 = ArrayList(0) - val stdlibJre8 = ArrayList(0) - - val visitedPaths = hashSetOf() - - for (jarRoot in classpathJarRoots) { - val fileKind = determineFileKind(jarRoot) - if (fileKind is FileKind.Irrelevant) continue - - val jarFile = VfsUtilCore.getVirtualFileForJar(jarRoot) ?: continue - if (!visitedPaths.add(jarFile.path)) continue - - when (fileKind) { - is FileKind.Runtime -> { - val file = KotlinLibraryFile(jarFile, fileKind.version) - jars.add(file) - if (fileKind.isCoreComponent) { - coreJars.add(file) - } - if (fileKind.isStdlibJre7) { - stdlibJre7.add(file) - } - if (fileKind.isStdlibJre8) { - stdlibJre8.add(file) - } - } - FileKind.OldRuntime -> jars.add(KotlinLibraryFile(jarFile, ApiVersion.KOTLIN_1_0.version)) - FileKind.LibraryWithBundledRuntime -> otherLibrariesWithBundledRuntime.add(jarFile) - FileKind.Irrelevant -> {} - } - } - - return RuntimeJarsInfo(jars, coreJars, otherLibrariesWithBundledRuntime, stdlibJre7, stdlibJre8) - } - - private sealed class FileKind { - class Runtime( - val version: MavenComparableVersion, - val isStdlibJre7: Boolean, - val isStdlibJre8: Boolean, - val isCoreComponent: Boolean - ) : FileKind() - - // Runtime library of Kotlin 1.0 - object OldRuntime : FileKind() - - object LibraryWithBundledRuntime : FileKind() - - object Irrelevant : FileKind() - } - - private fun determineFileKind(jarRoot: VirtualFile): FileKind { - val manifestFile = jarRoot.findFileByRelativePath(MANIFEST_MF) - val manifest = try { - manifestFile?.let { Manifest(it.inputStream) } - } catch (e: IOException) { - return FileKind.Irrelevant - } - - val runtimeComponent = manifest?.mainAttributes?.getValue(KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE) - val isStdlibJre7 = jarRoot.findFileByRelativePath(KOTLIN_STDLIB_JRE_7_MODULE) != null - val isStdlibJre8 = jarRoot.findFileByRelativePath(KOTLIN_STDLIB_JRE_8_MODULE) != null - return when (runtimeComponent) { - KOTLIN_RUNTIME_COMPONENT_MAIN -> - FileKind.Runtime(manifest.getKotlinLanguageVersion(), isStdlibJre7, isStdlibJre8, isCoreComponent = false) - KOTLIN_RUNTIME_COMPONENT_CORE -> - FileKind.Runtime(manifest.getKotlinLanguageVersion(), isStdlibJre7, isStdlibJre8, isCoreComponent = true) - null -> when { - jarRoot.findFileByRelativePath(KOTLIN_STDLIB_MODULE) == null && - jarRoot.findFileByRelativePath(KOTLIN_REFLECT_MODULE) == null -> FileKind.Irrelevant - isGenuineKotlinRuntime(manifest) -> FileKind.OldRuntime - else -> FileKind.LibraryWithBundledRuntime - } - else -> FileKind.Irrelevant - } - } - - // Returns true if the manifest is from the original Kotlin Runtime jar, false if it's from a library with a bundled runtime - private fun isGenuineKotlinRuntime(manifest: Manifest?): Boolean { - return manifest != null && - manifest.mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE) in RUNTIME_IMPLEMENTATION_TITLES - } - - private fun Manifest.getKotlinLanguageVersion(): MavenComparableVersion = - (mainAttributes.getValue(KOTLIN_VERSION_ATTRIBUTE)?.let(ApiVersion.Companion::parse) ?: ApiVersion.KOTLIN_1_0).version -} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 09b3d429051..0954b77f9ca 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -67,7 +67,6 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.toBooleanLenient -import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem import org.jetbrains.kotlin.cli.jvm.config.* import org.jetbrains.kotlin.cli.jvm.index.* @@ -267,17 +266,6 @@ class KotlinCoreEnvironment private constructor( classpathRootsResolver.convertClasspathRoots(configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS)) this.initialRoots.addAll(initialRoots) - if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) { - /* - // Temporarily disable until compiler is bootstrapped to 1.6. - JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency( - messageCollector, - configuration, - initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null } - ) - */ - } - val (roots, singleJavaFileRoots) = initialRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index a1309b96348..b61405ea3bb 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt @@ -271,7 +271,6 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr put(JVMConfigurationKeys.ASSERTIONS_MODE, assertionsMode ?: JVMAssertionsMode.DEFAULT) put(JVMConfigurationKeys.USE_TYPE_TABLE, arguments.useTypeTable) - put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, arguments.skipRuntimeVersionCheck) put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, arguments.useOldClassFilesReading) put(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM, arguments.useFastJarFileSystem) diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index 8289873ff0d..8c7d82e1528 100644 --- a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -61,9 +61,6 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey USE_SINGLE_MODULE = CompilerConfigurationKey.create("combine modules for source files and binary dependencies into a single module"); - public static final CompilerConfigurationKey SKIP_RUNTIME_VERSION_CHECK = - CompilerConfigurationKey.create("do not perform checks on runtime versions consistency"); - public static final CompilerConfigurationKey JVM_TARGET = CompilerConfigurationKey.create("JVM bytecode target version"); diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index a679eaaa202..74941cbc54b 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -119,7 +119,6 @@ where advanced options include: -Xserialize-ir={none|inline|all} Save IR to metadata (EXPERIMENTAL) -Xsingle-module Combine modules for source files and binary dependencies into a single module - -Xskip-runtime-version-check Allow Kotlin runtime libraries of incompatible versions in the classpath -Xgenerate-strict-metadata-version Generate metadata with strict version semantics (see kdoc on Metadata.extraInt) -Xstring-concat={indy-with-constants|indy|inline}