Refactor compiler arguments to configuration conversion:

extract into independent functions, rearrange logic
This commit is contained in:
Ilya Chernikov
2019-01-08 18:00:45 +01:00
parent 298aaf999e
commit 04b04ea0ee
11 changed files with 431 additions and 333 deletions
@@ -331,7 +331,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
}
}
fun configureLanguageVersionSettings(collector: MessageCollector): LanguageVersionSettings {
fun toLanguageVersionSettings(collector: MessageCollector): LanguageVersionSettings {
// If only "-api-version" is specified, language version is assumed to be the latest stable
val languageVersion = parseVersion(collector, languageVersion, "language") ?: LanguageVersion.LATEST_STABLE
@@ -40,25 +40,25 @@ fun <From : Any, To : From> mergeBeans(from: From, to: To): To {
@Suppress("UNCHECKED_CAST")
fun <From : Any, To : Any> copyInheritedFields(from: From, to: To) =
copyProperties(from, to, true, collectProperties(from::class as KClass<From>, true))
copyProperties(from, to, true, collectProperties(from::class as KClass<From>, true))
@Suppress("UNCHECKED_CAST")
fun <From : Any, To : Any> copyFieldsSatisfying(from: From, to: To, predicate: (KProperty1<From, Any?>) -> Boolean) =
copyProperties(from, to, true, collectProperties(from::class as KClass<From>, false).filter(predicate))
copyProperties(from, to, true, collectProperties(from::class as KClass<From>, false).filter(predicate))
private fun <From : Any, To : Any> copyProperties(
from: From,
to: To,
deepCopyWhenNeeded: Boolean,
propertiesToCopy: List<KProperty1<From, Any?>>,
filter: ((KProperty1<From, Any?>, Any?) -> Boolean)? = null
from: From,
to: To,
deepCopyWhenNeeded: Boolean,
propertiesToCopy: List<KProperty1<From, Any?>>,
filter: ((KProperty1<From, Any?>, Any?) -> Boolean)? = null
): To {
if (from == to) return to
for (fromProperty in propertiesToCopy) {
@Suppress("UNCHECKED_CAST")
val toProperty = to::class.memberProperties.firstOrNull { it.name == fromProperty.name } as? KMutableProperty1<To, Any?>
?: continue
?: continue
val fromValue = fromProperty.get(from)
if (filter != null && !filter(fromProperty, fromValue)) continue
toProperty.set(to, if (deepCopyWhenNeeded) fromValue?.copyValueIfNeeded() else fromValue)
@@ -69,14 +69,14 @@ private fun <From : Any, To : Any> copyProperties(
private fun Any.copyValueIfNeeded(): Any {
@Suppress("UNCHECKED_CAST")
return when (this) {
is ByteArray -> Arrays.copyOf(this, size)
is CharArray -> Arrays.copyOf(this, size)
is ShortArray -> Arrays.copyOf(this, size)
is IntArray -> Arrays.copyOf(this, size)
is LongArray -> Arrays.copyOf(this, size)
is FloatArray -> Arrays.copyOf(this, size)
is DoubleArray -> Arrays.copyOf(this, size)
is BooleanArray -> Arrays.copyOf(this, size)
is ByteArray -> this.copyOf(size)
is CharArray -> this.copyOf(size)
is ShortArray -> this.copyOf(size)
is IntArray -> this.copyOf(size)
is LongArray -> this.copyOf(size)
is FloatArray -> this.copyOf(size)
is DoubleArray -> this.copyOf(size)
is BooleanArray -> this.copyOf(size)
is Array<*> -> java.lang.reflect.Array.newInstance(this::class.java.componentType, size).apply {
this as Array<Any?>
@@ -100,8 +100,8 @@ fun <T : Any> collectProperties(kClass: KClass<T>, inheritedOnly: Boolean): List
if (inheritedOnly) {
properties.removeAll(kClass.declaredMemberProperties)
}
return properties.filter {
it.visibility == KVisibility.PUBLIC && (it.annotations.firstOrNull { it is Transient } as Transient?) == null
return properties.filter { property ->
property.visibility == KVisibility.PUBLIC && (property.annotations.firstOrNull { it is Transient } as Transient?) == null
}
}
@@ -109,4 +109,5 @@ fun CommonCompilerArguments.setApiVersionToLanguageVersionIfNeeded() {
if (languageVersion != null && VersionComparatorUtil.compare(languageVersion, apiVersion) < 0) {
apiVersion = languageVersion
}
}
}
@@ -18,15 +18,17 @@ package org.jetbrains.kotlin.cli.common
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import kotlin.collections.*
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY
import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR
import org.jetbrains.kotlin.cli.common.ExitCode.INTERNAL_ERROR
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
@@ -34,18 +36,9 @@ import org.jetbrains.kotlin.progress.CompilationCanceledException
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.io.PrintStream
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY
import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR
import org.jetbrains.kotlin.cli.common.ExitCode.INTERNAL_ERROR
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
protected abstract val performanceManager: CommonCompilerPerformanceManager
@@ -60,22 +53,24 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
return exec(errStream, Services.EMPTY, MessageRenderer.PLAIN_FULL_PATHS, args)
}
public override fun execImpl(messageCollector: MessageCollector, services: Services, arguments: A): ExitCode {
public override fun execImpl(baseMessageCollector: MessageCollector, services: Services, arguments: A): ExitCode {
val performanceManager = performanceManager
if (arguments.reportPerf || arguments.dumpPerf != null) {
performanceManager.enableCollectingPerformanceStatistics()
}
val groupingCollector = GroupingMessageCollector(messageCollector, arguments.allWarningsAsErrors)
val configuration = CompilerConfiguration()
configuration.put(MESSAGE_COLLECTOR_KEY, groupingCollector)
val messageCollector = GroupingMessageCollector(baseMessageCollector, arguments.allWarningsAsErrors).also {
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, it)
}
configuration.put(CLIConfigurationKeys.PERF_MANAGER, performanceManager)
try {
setupCommonArguments(configuration, arguments)
setupPlatformSpecificArgumentsAndServices(configuration, arguments, services)
val paths = computeKotlinPaths(groupingCollector, arguments)
if (groupingCollector.hasErrors()) {
val paths = computeKotlinPaths(messageCollector, arguments)
if (messageCollector.hasErrors()) {
return ExitCode.COMPILATION_ERROR
}
@@ -85,6 +80,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
val rootDisposable = Disposer.newDisposable()
try {
setIdeaIoUseFallback()
val code = doExecute(arguments, configuration, rootDisposable, paths)
performanceManager.notifyCompilationFinished()
@@ -97,7 +93,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
performanceManager.dumpPerformanceReport(File(arguments.dumpPerf!!))
}
return if (groupingCollector.hasErrors()) COMPILATION_ERROR else code
return if (messageCollector.hasErrors()) COMPILATION_ERROR else code
} catch (e: CompilationCanceledException) {
messageCollector.report(INFO, "Compilation was canceled", null)
return ExitCode.OK
@@ -115,61 +111,19 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
} catch (e: AnalysisResult.CompilationErrorException) {
return COMPILATION_ERROR
} catch (t: Throwable) {
MessageCollectorUtil.reportException(groupingCollector, t)
MessageCollectorUtil.reportException(messageCollector, t)
return INTERNAL_ERROR
} finally {
groupingCollector.flush()
messageCollector.flush()
}
}
private fun setupCommonArguments(configuration: CompilerConfiguration, arguments: A) {
if (arguments.noInline) {
configuration.put(CommonConfigurationKeys.DISABLE_INLINE, true)
}
if (arguments.intellijPluginRoot != null) {
configuration.put(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT, arguments.intellijPluginRoot!!)
}
if (arguments.reportOutputFiles) {
configuration.put(CommonConfigurationKeys.REPORT_OUTPUT_FILES, true)
}
val metadataVersionString = arguments.metadataVersion
if (metadataVersionString != null) {
val versionArray = BinaryVersion.parseVersionArray(metadataVersionString)
if (versionArray == null) {
configuration.getNotNull(MESSAGE_COLLECTOR_KEY).report(ERROR, "Invalid metadata version: $metadataVersionString", null)
} else {
configuration.put(CommonConfigurationKeys.METADATA_VERSION, createMetadataVersion(versionArray))
}
}
setupLanguageVersionSettings(configuration, arguments)
configuration.put(CommonConfigurationKeys.LIST_PHASES, arguments.listPhases)
if (arguments.disablePhases != null) {
configuration.put(CommonConfigurationKeys.DISABLED_PHASES, setOf(*arguments.disablePhases!!))
}
if (arguments.verbosePhases != null) {
configuration.put(CommonConfigurationKeys.VERBOSE_PHASES, setOf(*arguments.verbosePhases!!))
}
if (arguments.phasesToDumpBefore != null) {
configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE_BEFORE, setOf(*arguments.phasesToDumpBefore!!))
}
if (arguments.phasesToDumpAfter != null) {
configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE_AFTER, setOf(*arguments.phasesToDumpAfter!!))
}
if (arguments.phasesToDump != null) {
configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE, setOf(*arguments.phasesToDump!!))
}
configuration.put(CommonConfigurationKeys.PROFILE_PHASES, arguments.profilePhases)
configuration.setupCommonArguments(arguments, this::createMetadataVersion)
}
protected abstract fun createMetadataVersion(versionArray: IntArray): BinaryVersion
private fun setupLanguageVersionSettings(configuration: CompilerConfiguration, arguments: A) {
configuration.languageVersionSettings = arguments.configureLanguageVersionSettings(configuration.getNotNull(MESSAGE_COLLECTOR_KEY))
}
protected abstract fun setupPlatformSpecificArgumentsAndServices(
configuration: CompilerConfiguration, arguments: A, services: Services
)
@@ -180,58 +134,5 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
rootDisposable: Disposable,
paths: KotlinPaths?
): ExitCode
companion object {
var KOTLIN_HOME_PROPERTY = "kotlin.home"
private fun computeKotlinPaths(messageCollector: MessageCollector, arguments: CommonCompilerArguments): KotlinPaths? {
val paths: KotlinPaths?
val kotlinHomeProperty = System.getProperty(KOTLIN_HOME_PROPERTY)
val kotlinHome = if (arguments.kotlinHome != null)
File(arguments.kotlinHome!!)
else if (kotlinHomeProperty != null)
File(kotlinHomeProperty)
else
null
if (kotlinHome != null) {
if (kotlinHome.isDirectory) {
paths = KotlinPathsFromHomeDir(kotlinHome)
} else {
messageCollector.report(ERROR, "Kotlin home does not exist or is not a directory: $kotlinHome", null)
paths = null
}
} else {
paths = PathUtil.kotlinPathsForCompiler
}
if (paths != null) {
messageCollector.report(LOGGING, "Using Kotlin home directory " + paths.homePath, null)
}
return paths
}
fun getLibraryFromHome(
paths: KotlinPaths?,
getLibrary: Function1<KotlinPaths, File>,
libraryName: String,
messageCollector: MessageCollector,
noLibraryArgument: String
): File? {
if (paths != null) {
val stdlibJar = getLibrary.invoke(paths)
if (stdlibJar.exists()) {
return stdlibJar
}
}
messageCollector.report(
STRONG_WARNING, "Unable to find " + libraryName + " in the Kotlin home directory. " +
"Pass either " + noLibraryArgument + " to prevent adding it to the classpath, " +
"or the correct '-kotlin-home'", null
)
return null
}
}
}
@@ -89,7 +89,7 @@ abstract class CLITool<A : CommonToolArguments> {
messageCollector
}
reportArgumentParseProblems(fixedMessageCollector, arguments)
fixedMessageCollector.reportArgumentParseProblems(arguments)
return execImpl(fixedMessageCollector, services, arguments)
}
@@ -0,0 +1,133 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.cli.common
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.cli.common.arguments.ManualLanguageFeatureSetting
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
fun <A : CommonCompilerArguments> CompilerConfiguration.setupCommonArguments(
arguments: A,
createMetadataVersion: ((IntArray) -> BinaryVersion)? = null
) {
put(CommonConfigurationKeys.DISABLE_INLINE, arguments.noInline)
putIfNotNull(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT, arguments.intellijPluginRoot)
put(CommonConfigurationKeys.REPORT_OUTPUT_FILES, arguments.reportOutputFiles)
val metadataVersionString = arguments.metadataVersion
if (metadataVersionString != null) {
val versionArray = BinaryVersion.parseVersionArray(metadataVersionString)
val messageCollector = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
when {
versionArray == null -> messageCollector.report(
CompilerMessageSeverity.ERROR, "Invalid metadata version: $metadataVersionString", null
)
createMetadataVersion == null -> throw IllegalStateException("Unable to create metadata version: missing argument")
else -> put(CommonConfigurationKeys.METADATA_VERSION, createMetadataVersion(versionArray))
}
}
setupLanguageVersionSettings(arguments)
put(CommonConfigurationKeys.LIST_PHASES, arguments.listPhases)
listOf(
CommonConfigurationKeys.DISABLED_PHASES to arguments.disablePhases,
CommonConfigurationKeys.VERBOSE_PHASES to arguments.verbosePhases,
CommonConfigurationKeys.PHASES_TO_DUMP_STATE_BEFORE to arguments.phasesToDumpBefore,
CommonConfigurationKeys.PHASES_TO_DUMP_STATE_AFTER to arguments.phasesToDumpAfter,
CommonConfigurationKeys.PHASES_TO_DUMP_STATE to arguments.phasesToDump
).forEach { (k, v) ->
if (v != null) put(k, setOf(*v))
}
put(CommonConfigurationKeys.PROFILE_PHASES, arguments.profilePhases)
}
fun <A : CommonCompilerArguments> CompilerConfiguration.setupLanguageVersionSettings(arguments: A) {
languageVersionSettings = arguments.toLanguageVersionSettings(getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
}
const val KOTLIN_HOME_PROPERTY = "kotlin.home"
fun computeKotlinPaths(messageCollector: MessageCollector, arguments: CommonCompilerArguments): KotlinPaths? {
val kotlinHomeProperty = System.getProperty(KOTLIN_HOME_PROPERTY)
val kotlinHome = when {
arguments.kotlinHome != null -> File(arguments.kotlinHome!!)
kotlinHomeProperty != null -> File(kotlinHomeProperty)
else -> null
}
return when {
kotlinHome == null -> PathUtil.kotlinPathsForCompiler
kotlinHome.isDirectory -> KotlinPathsFromHomeDir(kotlinHome)
else -> {
messageCollector.report(CompilerMessageSeverity.ERROR, "Kotlin home does not exist or is not a directory: $kotlinHome", null)
null
}
}?.also {
messageCollector.report(CompilerMessageSeverity.LOGGING, "Using Kotlin home directory " + it.homePath, null)
}
}
fun <A : CommonToolArguments> MessageCollector.reportArgumentParseProblems(arguments: A) {
val errors = arguments.errors ?: return
for (flag in errors.unknownExtraFlags) {
report(CompilerMessageSeverity.STRONG_WARNING, "Flag is not supported by this version of the compiler: $flag")
}
for (argument in errors.extraArgumentsPassedInObsoleteForm) {
report(
CompilerMessageSeverity.STRONG_WARNING,
"Advanced option value is passed in an obsolete form. Please use the '=' character to specify the value: $argument=..."
)
}
for ((key, value) in errors.duplicateArguments) {
report(CompilerMessageSeverity.STRONG_WARNING, "Argument $key is passed multiple times. Only the last value will be used: $value")
}
for ((deprecatedName, newName) in errors.deprecatedArguments) {
report(CompilerMessageSeverity.STRONG_WARNING, "Argument $deprecatedName is deprecated. Please use $newName instead")
}
for (argfileError in errors.argfileErrors) {
report(CompilerMessageSeverity.STRONG_WARNING, argfileError)
}
reportUnsafeInternalArgumentsIfAny(arguments)
for (internalArgumentsError in errors.internalArgumentsParsingProblems) {
report(CompilerMessageSeverity.STRONG_WARNING, internalArgumentsError)
}
}
private fun <A : CommonToolArguments> MessageCollector.reportUnsafeInternalArgumentsIfAny(arguments: A) {
val unsafeArguments = arguments.internalArguments.filterNot {
// -XXLanguage which turns on BUG_FIX considered safe
it is ManualLanguageFeatureSetting && it.languageFeature.kind == LanguageFeature.Kind.BUG_FIX && it.state == LanguageFeature.State.ENABLED
}
if (unsafeArguments.isNotEmpty()) {
val unsafeArgumentsString = unsafeArguments.joinToString(prefix = "\n", postfix = "\n\n", separator = "\n") {
it.stringRepresentation
}
report(
CompilerMessageSeverity.STRONG_WARNING,
"ATTENTION!\n" +
"This build uses unsafe internal compiler arguments:\n" +
unsafeArgumentsString +
"This mode is not recommended for production use,\n" +
"as no stability/compatibility guarantees are given on\n" +
"compiler or generated code. Use it at your own risk!\n"
)
}
}
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isSubpackageOf
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.KotlinPaths
import java.io.File
fun checkKotlinPackageUsage(environment: KotlinCoreEnvironment, files: Collection<KtFile>): Boolean {
if (environment.configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE)) {
@@ -42,3 +44,25 @@ fun checkKotlinPackageUsage(environment: KotlinCoreEnvironment, files: Collectio
}
return true
}
fun getLibraryFromHome(
paths: KotlinPaths?,
getLibrary: (KotlinPaths) -> File,
libraryName: String,
messageCollector: MessageCollector,
noLibraryArgument: String
): File? {
if (paths != null) {
val stdlibJar = getLibrary(paths)
if (stdlibJar.exists()) {
return stdlibJar
}
}
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING, "Unable to find " + libraryName + " in the Kotlin home directory. " +
"Pass either " + noLibraryArgument + " to prevent adding it to the classpath, " +
"or the correct '-kotlin-home'", null
)
return null
}
@@ -77,6 +77,7 @@ import java.util.*;
import static org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR;
import static org.jetbrains.kotlin.cli.common.ExitCode.OK;
import static org.jetbrains.kotlin.cli.common.UtilsKt.checkKotlinPackageUsage;
import static org.jetbrains.kotlin.cli.common.UtilsKt.getLibraryFromHome;
import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*;
public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
@@ -460,7 +461,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
) {
List<String> libraries = new SmartList<>();
if (!arguments.getNoStdlib()) {
File stdlibJar = Companion.getLibraryFromHome(
File stdlibJar = getLibraryFromHome(
paths, KotlinPaths::getJsStdLibJarPath, PathUtil.JS_LIB_JAR_NAME, messageCollector, "'-no-stdlib'");
if (stdlibJar != null) {
libraries.add(stdlibJar.getAbsolutePath());
@@ -31,10 +31,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
import org.jetbrains.kotlin.codegen.CompilationException
@@ -66,13 +63,9 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
): ExitCode {
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
configureJdkHome(arguments, configuration, messageCollector).let {
if (it != OK) return it
}
if (!configuration.configureJdkHome(arguments)) return COMPILATION_ERROR
if (arguments.disableStandardScript) {
configuration.put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, true)
}
configuration.put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, arguments.disableStandardScript)
val pluginLoadResult = loadPlugins(arguments, configuration)
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
@@ -94,7 +87,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
configuration.put(CommonConfigurationKeys.MODULE_NAME, arguments.moduleName ?: JvmAbi.DEFAULT_MODULE_NAME)
configureContentRoots(paths, arguments, configuration)
configuration.configureExplicitContentRoots(arguments)
configuration.configureStandardLibs(paths, arguments)
if (arguments.buildFile == null && arguments.freeArgs.isEmpty() && !arguments.version) {
if (arguments.script) {
@@ -105,29 +99,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
return ExitCode.OK
}
if (arguments.includeRuntime) {
configuration.put(JVMConfigurationKeys.INCLUDE_RUNTIME, true)
}
val friendPaths = arguments.friendPaths?.toList()
if (friendPaths != null) {
configuration.put(JVMConfigurationKeys.FRIEND_PATHS, friendPaths)
}
if (arguments.jvmTarget != null) {
val jvmTarget = JvmTarget.fromString(arguments.jvmTarget!!)
if (jvmTarget != null) {
configuration.put(JVMConfigurationKeys.JVM_TARGET, jvmTarget)
} else {
messageCollector.report(
ERROR, "Unknown JVM target version: ${arguments.jvmTarget}\n" +
"Supported versions: ${JvmTarget.values().joinToString { it.description }}"
)
}
}
configuration.put(JVMConfigurationKeys.PARAMETERS_METADATA, arguments.javaParameters)
putAdvancedOptions(configuration, arguments)
configuration.configureAdvancedJvmOptions(arguments)
messageCollector.report(LOGGING, "Configuring the compilation environment")
try {
@@ -152,7 +124,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
?: return COMPILATION_ERROR
registerJavacIfNeeded(environment, arguments).let {
environment.registerJavacIfNeeded(arguments).let {
if (!it) return COMPILATION_ERROR
}
@@ -190,7 +162,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
?: return COMPILATION_ERROR
registerJavacIfNeeded(environment, arguments).let {
environment.registerJavacIfNeeded(arguments).let {
if (!it) return COMPILATION_ERROR
}
@@ -263,21 +235,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
return PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, configuration)
}
private fun registerJavacIfNeeded(
environment: KotlinCoreEnvironment,
arguments: K2JVMCompilerArguments
): Boolean {
if (arguments.useJavac) {
environment.configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
if (arguments.compileJava) {
environment.configuration.put(JVMConfigurationKeys.COMPILE_JAVA, true)
}
return environment.registerJavac(arguments = arguments.javacArguments)
}
return true
}
private fun compileJavaFilesIfNeeded(
environment: KotlinCoreEnvironment,
arguments: K2JVMCompilerArguments
@@ -305,26 +262,20 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
override fun setupPlatformSpecificArgumentsAndServices(
configuration: CompilerConfiguration, arguments: K2JVMCompilerArguments, services: Services
) {
if (IncrementalCompilation.isEnabledForJvm()) {
services[LookupTracker::class.java]?.let {
configuration.put(CommonConfigurationKeys.LOOKUP_TRACKER, it)
}
with(configuration) {
if (IncrementalCompilation.isEnabledForJvm()) {
putIfNotNull(CommonConfigurationKeys.LOOKUP_TRACKER, services[LookupTracker::class.java])
services[ExpectActualTracker::class.java]?.let {
configuration.put(CommonConfigurationKeys.EXPECT_ACTUAL_TRACKER, it)
}
putIfNotNull(CommonConfigurationKeys.EXPECT_ACTUAL_TRACKER, services[ExpectActualTracker::class.java])
services[IncrementalCompilationComponents::class.java]?.let {
configuration.put(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS, it)
}
putIfNotNull(
JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS,
services[IncrementalCompilationComponents::class.java]
)
services[JavaClassesTracker::class.java]?.let {
configuration.put(JVMConfigurationKeys.JAVA_CLASSES_TRACKER, it)
putIfNotNull(JVMConfigurationKeys.JAVA_CLASSES_TRACKER, services[JavaClassesTracker::class.java])
}
}
arguments.additionalJavaModules?.let { additionalJavaModules ->
configuration.addAll(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, additionalJavaModules.toList())
setupJvmSpecificArguments(arguments)
}
}
@@ -346,131 +297,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
CLITool.doMain(K2JVMCompiler(), args)
}
private fun putAdvancedOptions(configuration: CompilerConfiguration, arguments: K2JVMCompilerArguments) {
configuration.put(JVMConfigurationKeys.IR, arguments.useIR)
configuration.put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, arguments.noCallAssertions)
configuration.put(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS, arguments.noReceiverAssertions)
configuration.put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions)
configuration.put(
JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL,
arguments.noExceptionOnExplicitEqualsForBoxedNull
)
configuration.put(JVMConfigurationKeys.DISABLE_OPTIMIZATION, arguments.noOptimize)
if (!JVMConstructorCallNormalizationMode.isSupportedValue(arguments.constructorCallNormalizationMode)) {
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
ERROR,
"Unknown constructor call normalization mode: ${arguments.constructorCallNormalizationMode}, " +
"supported modes: ${JVMConstructorCallNormalizationMode.values().map { it.description }}"
)
}
val constructorCallNormalizationMode =
JVMConstructorCallNormalizationMode.fromStringOrNull(arguments.constructorCallNormalizationMode)
if (constructorCallNormalizationMode != null) {
configuration.put(
JVMConfigurationKeys.CONSTRUCTOR_CALL_NORMALIZATION_MODE,
constructorCallNormalizationMode
)
}
val assertionsMode =
JVMAssertionsMode.fromStringOrNull(arguments.assertionsMode)
if (assertionsMode == null) {
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
ERROR,
"Unknown assertions mode: ${arguments.assertionsMode}, " +
"supported modes: ${JVMAssertionsMode.values().map { it.description }}"
)
}
configuration.put(
JVMConfigurationKeys.ASSERTIONS_MODE,
assertionsMode ?: JVMAssertionsMode.DEFAULT
)
configuration.put(JVMConfigurationKeys.USE_TYPE_TABLE, arguments.useTypeTable)
configuration.put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, arguments.skipRuntimeVersionCheck)
configuration.put(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING, !arguments.useOldClassFilesReading)
if (arguments.useOldClassFilesReading) {
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
.report(INFO, "Using the old java class files reading implementation")
}
configuration.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
configuration.put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule)
configuration.put(JVMConfigurationKeys.ADD_BUILT_INS_FROM_COMPILER_TO_DEPENDENCIES, arguments.addCompilerBuiltIns)
configuration.put(JVMConfigurationKeys.CREATE_BUILT_INS_FROM_MODULE_DEPENDENCIES, arguments.loadBuiltInsFromDependencies)
arguments.declarationsOutputPath?.let { configuration.put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) }
}
private fun configureContentRoots(paths: KotlinPaths?, arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration) {
for (modularRoot in arguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(File(modularRoot)))
}
if (arguments.buildFile != null) {
// In the .xml compilation mode, all content roots except module path will be loaded from the .xml build file.
return
}
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
for (path in arguments.classpath?.split(File.pathSeparatorChar).orEmpty()) {
configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(File(path)))
}
val isModularJava = configuration.get(JVMConfigurationKeys.JDK_HOME).let { it != null && CoreJrtFileSystem.isModularJdk(it) }
fun addRoot(moduleName: String, libraryName: String, getLibrary: (KotlinPaths) -> File, noLibraryArgument: String) {
val file = getLibraryFromHome(paths, getLibrary, libraryName, messageCollector, noLibraryArgument) ?: return
if (isModularJava) {
configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file))
configuration.add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, moduleName)
} else {
configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(file))
}
}
if (!arguments.noStdlib) {
addRoot("kotlin.stdlib", PathUtil.KOTLIN_JAVA_STDLIB_JAR, KotlinPaths::getStdlibPath, "'-no-stdlib'")
addRoot("kotlin.script.runtime", PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR, KotlinPaths::getScriptRuntimePath, "'-no-stdlib'")
}
// "-no-stdlib" implies "-no-reflect": otherwise we would be able to transitively read stdlib classes through kotlin-reflect,
// which is likely not what user wants since s/he manually provided "-no-stdlib"
if (!arguments.noReflect && !arguments.noStdlib) {
addRoot("kotlin.reflect", PathUtil.KOTLIN_JAVA_REFLECT_JAR, KotlinPaths::getReflectPath, "'-no-reflect' or '-no-stdlib'")
}
}
private fun configureJdkHome(
arguments: K2JVMCompilerArguments,
configuration: CompilerConfiguration,
messageCollector: MessageCollector
): ExitCode {
if (arguments.noJdk) {
configuration.put(JVMConfigurationKeys.NO_JDK, true)
if (arguments.jdkHome != null) {
messageCollector.report(STRONG_WARNING, "The '-jdk-home' option is ignored because '-no-jdk' is specified")
}
return OK
}
if (arguments.jdkHome != null) {
val jdkHome = File(arguments.jdkHome)
if (!jdkHome.exists()) {
messageCollector.report(ERROR, "JDK home directory does not exist: $jdkHome")
return COMPILATION_ERROR
}
messageCollector.report(LOGGING, "Using JDK home directory $jdkHome")
configuration.put(JVMConfigurationKeys.JDK_HOME, jdkHome)
}
return OK
}
}
}
fun main(args: Array<String>) = K2JVMCompiler.main(args)
@@ -0,0 +1,202 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.cli.jvm
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.getLibraryFromHome
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArguments) {
val messageCollector = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
put(JVMConfigurationKeys.INCLUDE_RUNTIME, arguments.includeRuntime)
putIfNotNull(JVMConfigurationKeys.FRIEND_PATHS, arguments.friendPaths?.asList())
if (arguments.jvmTarget != null) {
val jvmTarget = JvmTarget.fromString(arguments.jvmTarget!!)
if (jvmTarget != null) {
put(JVMConfigurationKeys.JVM_TARGET, jvmTarget)
} else {
messageCollector.report(
ERROR, "Unknown JVM target version: ${arguments.jvmTarget}\n" +
"Supported versions: ${JvmTarget.values().joinToString { it.description }}"
)
}
}
addAll(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, arguments.additionalJavaModules?.asList())
}
fun CompilerConfiguration.configureJdkHome(arguments: K2JVMCompilerArguments): Boolean {
val messageCollector = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
if (arguments.noJdk) {
put(JVMConfigurationKeys.NO_JDK, true)
if (arguments.jdkHome != null) {
messageCollector.report(STRONG_WARNING, "The '-jdk-home' option is ignored because '-no-jdk' is specified")
}
return true
}
if (arguments.jdkHome != null) {
val jdkHome = File(arguments.jdkHome)
if (!jdkHome.exists()) {
messageCollector.report(ERROR, "JDK home directory does not exist: $jdkHome")
return false
}
messageCollector.report(LOGGING, "Using JDK home directory $jdkHome")
put(JVMConfigurationKeys.JDK_HOME, jdkHome)
}
return true
}
fun CompilerConfiguration.configureExplicitContentRoots(arguments: K2JVMCompilerArguments) {
for (modularRoot in arguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(File(modularRoot)))
}
if (arguments.buildFile != null) {
// In the .xml compilation mode, all content roots except module path will be loaded from the .xml build file.
return
}
for (path in arguments.classpath?.split(File.pathSeparatorChar).orEmpty()) {
add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(File(path)))
}
}
fun CompilerConfiguration.configureStandardLibs(paths: KotlinPaths?, arguments: K2JVMCompilerArguments) {
val messageCollector = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
val isModularJava = isModularJava()
fun addRoot(moduleName: String, libraryName: String, getLibrary: (KotlinPaths) -> File, noLibraryArgument: String) {
addModularRootIfNotNull(
isModularJava, moduleName,
getLibraryFromHome(paths, getLibrary, libraryName, messageCollector, noLibraryArgument)
)
}
if (!arguments.noStdlib) {
addRoot("kotlin.stdlib", PathUtil.KOTLIN_JAVA_STDLIB_JAR, KotlinPaths::getStdlibPath, "'-no-stdlib'")
addRoot("kotlin.script.runtime", PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR, KotlinPaths::getScriptRuntimePath, "'-no-stdlib'")
}
// "-no-stdlib" implies "-no-reflect": otherwise we would be able to transitively read stdlib classes through kotlin-reflect,
// which is likely not what user wants since s/he manually provided "-no-stdlib"
if (!arguments.noReflect && !arguments.noStdlib) {
addRoot("kotlin.reflect", PathUtil.KOTLIN_JAVA_REFLECT_JAR, KotlinPaths::getReflectPath, "'-no-reflect' or '-no-stdlib'")
}
}
fun CompilerConfiguration.isModularJava(): Boolean {
return get(JVMConfigurationKeys.JDK_HOME)?.let {
CoreJrtFileSystem.isModularJdk(it)
} ?: false
}
fun CompilerConfiguration.addModularRootIfNotNull(isModularJava: Boolean, moduleName: String, file: File?) {
when {
file == null -> {
}
isModularJava -> {
add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file))
add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, moduleName)
}
else -> add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(file))
}
}
fun KotlinCoreEnvironment.registerJavacIfNeeded(
arguments: K2JVMCompilerArguments
): Boolean {
if (arguments.useJavac) {
configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
if (arguments.compileJava) {
configuration.put(JVMConfigurationKeys.COMPILE_JAVA, true)
}
return registerJavac(arguments = arguments.javacArguments)
}
return true
}
fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerArguments) {
put(JVMConfigurationKeys.PARAMETERS_METADATA, arguments.javaParameters)
put(JVMConfigurationKeys.IR, arguments.useIR)
put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, arguments.noCallAssertions)
put(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS, arguments.noReceiverAssertions)
put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions)
put(
JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL,
arguments.noExceptionOnExplicitEqualsForBoxedNull
)
put(JVMConfigurationKeys.DISABLE_OPTIMIZATION, arguments.noOptimize)
if (!JVMConstructorCallNormalizationMode.isSupportedValue(arguments.constructorCallNormalizationMode)) {
getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
ERROR,
"Unknown constructor call normalization mode: ${arguments.constructorCallNormalizationMode}, " +
"supported modes: ${JVMConstructorCallNormalizationMode.values().map { it.description }}"
)
}
val constructorCallNormalizationMode =
JVMConstructorCallNormalizationMode.fromStringOrNull(arguments.constructorCallNormalizationMode)
if (constructorCallNormalizationMode != null) {
put(
JVMConfigurationKeys.CONSTRUCTOR_CALL_NORMALIZATION_MODE,
constructorCallNormalizationMode
)
}
val assertionsMode =
JVMAssertionsMode.fromStringOrNull(arguments.assertionsMode)
if (assertionsMode == null) {
getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
ERROR,
"Unknown assertions mode: ${arguments.assertionsMode}, " +
"supported modes: ${JVMAssertionsMode.values().map { it.description }}"
)
}
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_FAST_CLASS_FILES_READING, !arguments.useOldClassFilesReading)
if (arguments.useOldClassFilesReading) {
getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
.report(INFO, "Using the old java class files reading implementation")
}
put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule)
put(JVMConfigurationKeys.ADD_BUILT_INS_FROM_COMPILER_TO_DEPENDENCIES, arguments.addCompilerBuiltIns)
put(JVMConfigurationKeys.CREATE_BUILT_INS_FROM_MODULE_DEPENDENCIES, arguments.loadBuiltInsFromDependencies)
arguments.declarationsOutputPath?.let { put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) }
}
@@ -73,6 +73,12 @@ public class CompilerConfiguration {
map.put(key.ideaKey, value);
}
public <T> void putIfNotNull(@NotNull CompilerConfigurationKey<T> key, @Nullable T value) {
if (value != null) {
put(key, value);
}
}
public <T> void add(@NotNull CompilerConfigurationKey<List<T>> key, @NotNull T value) {
checkReadOnly();
Key<List<T>> ideaKey = key.ideaKey;
@@ -89,8 +95,10 @@ public class CompilerConfiguration {
data.put(key, value);
}
public <T> void addAll(@NotNull CompilerConfigurationKey<List<T>> key, @NotNull Collection<T> values) {
addAll(key, getList(key).size(), values);
public <T> void addAll(@NotNull CompilerConfigurationKey<List<T>> key, @Nullable Collection<T> values) {
if (values != null) {
addAll(key, getList(key).size(), values);
}
}
public <T> void addAll(@NotNull CompilerConfigurationKey<List<T>> key, int index, @NotNull Collection<T> values) {
@@ -97,7 +97,7 @@ private fun getLanguageSettingsForScripts(project: Project, scriptDefinition: Ko
val compilerArguments = K2JVMCompilerArguments()
parseCommandLineArguments(args.toList(), compilerArguments)
// TODO: reporting
val verSettings = compilerArguments.configureLanguageVersionSettings(MessageCollector.NONE)
val verSettings = compilerArguments.toLanguageVersionSettings(MessageCollector.NONE)
val jvmTarget = compilerArguments.jvmTarget?.let { JvmTarget.fromString(it) } ?: TargetPlatformVersion.NoVersion
ScriptLanguageSettings(verSettings, jvmTarget)
}.also { scriptDefinition.putUserData(SCRIPT_LANGUAGE_SETTINGS, it) }