From 9aa287257b3d85babe76ae2b303b6fb75aff09f3 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Wed, 21 Dec 2022 20:33:08 +0200 Subject: [PATCH] [K/N] Refactored a bit configuration setup --- .../org/jetbrains/kotlin/cli/bc/K2Native.kt | 489 +---------------- .../kotlin/backend/konan/CacheBuilder.kt | 15 +- .../kotlin/backend/konan/KonanConfig.kt | 4 +- .../backend/konan/SetupConfiguration.kt | 491 ++++++++++++++++++ 4 files changed, 505 insertions(+), 494 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 005df6a05e0..9fb7e1966c3 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -14,8 +14,6 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.cli.common.* import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments -import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot -import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.* import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageRenderer @@ -27,8 +25,6 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.CompilerVersion -import org.jetbrains.kotlin.konan.file.File -import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.library.metadata.KlibMetadataVersion import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.psi.KtFile @@ -132,7 +128,7 @@ class K2Native : CLICompiler() { spawnedConfiguration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)) spawnedConfiguration.setupCommonArguments(spawnedArguments, this::createMetadataVersion) - setupPlatformSpecificArgumentsAndServices(spawnedConfiguration, spawnedArguments, Services.EMPTY) + spawnedConfiguration.setupFromArguments(spawnedArguments) spawnedConfiguration.setupConfiguration() val spawnedEnvironment = prepareEnvironment(spawnedArguments, spawnedConfiguration, rootDisposable) runKonanDriver(spawnedConfiguration, spawnedEnvironment, rootDisposable) @@ -140,265 +136,17 @@ class K2Native : CLICompiler() { konanDriver.run() } - val K2NativeCompilerArguments.isUsefulWithoutFreeArgs: Boolean + private val K2NativeCompilerArguments.isUsefulWithoutFreeArgs: Boolean get() = listTargets || listPhases || checkDependencies || !includes.isNullOrEmpty() || libraryToAddToCache != null || !exportedLibraries.isNullOrEmpty() - fun Array?.toNonNullList(): List { - return this?.asList() ?: listOf() - } - // It is executed before doExecute(). override fun setupPlatformSpecificArgumentsAndServices( configuration: CompilerConfiguration, - arguments : K2NativeCompilerArguments, - services : Services) { - - val commonSources = arguments.commonSources?.toSet().orEmpty() - arguments.freeArgs.forEach { - configuration.addKotlinSourceRoot(it, it in commonSources) - } - - with(KonanConfigKeys) { - with(configuration) { - // Can be overwritten by explicit arguments below. - parseBinaryOptions(arguments, configuration).forEach { optionWithValue -> - configuration.put(optionWithValue) - } - - arguments.kotlinHome?.let { put(KONAN_HOME, it) } - - put(NODEFAULTLIBS, arguments.nodefaultlibs || !arguments.libraryToAddToCache.isNullOrEmpty()) - put(NOENDORSEDLIBS, arguments.noendorsedlibs || !arguments.libraryToAddToCache.isNullOrEmpty()) - put(NOSTDLIB, arguments.nostdlib || !arguments.libraryToAddToCache.isNullOrEmpty()) - put(NOPACK, arguments.nopack) - put(NOMAIN, arguments.nomain) - put(LIBRARY_FILES, - arguments.libraries.toNonNullList()) - put(LINKER_ARGS, arguments.linkerArguments.toNonNullList() + - arguments.singleLinkerArguments.toNonNullList()) - arguments.moduleName?.let{ put(MODULE_NAME, it) } - - // TODO: allow overriding the prefix directly. - arguments.moduleName?.let { put(FULL_EXPORTED_NAME_PREFIX, it) } - - arguments.target?.let{ put(TARGET, it) } - - put(INCLUDED_BINARY_FILES, - arguments.includeBinaries.toNonNullList()) - put(NATIVE_LIBRARY_FILES, - arguments.nativeLibraries.toNonNullList()) - put(REPOSITORIES, - arguments.repositories.toNonNullList()) - - // TODO: Collect all the explicit file names into an object - // and teach the compiler to work with temporaries and -save-temps. - - arguments.outputName ?.let { put(OUTPUT, it) } - val outputKind = CompilerOutputKind.valueOf( - (arguments.produce ?: "program").uppercase()) - put(PRODUCE, outputKind) - put(METADATA_KLIB, arguments.metadataKlib) - - arguments.libraryVersion ?. let { put(LIBRARY_VERSION, it) } - - arguments.mainPackage ?.let{ put(ENTRY, it) } - arguments.manifestFile ?.let{ put(MANIFEST_FILE, it) } - arguments.runtimeFile ?.let{ put(RUNTIME_FILE, it) } - arguments.temporaryFilesDir?.let { put(TEMPORARY_FILES_DIR, it) } - put(SAVE_LLVM_IR, arguments.saveLlvmIrAfter.toList()) - - put(LIST_TARGETS, arguments.listTargets) - put(OPTIMIZATION, arguments.optimization) - put(DEBUG, arguments.debug) - // TODO: remove after 1.4 release. - if (arguments.lightDebugDeprecated) { - configuration.report(WARNING, - "-Xg0 is now deprecated and skipped by compiler. Light debug information is enabled by default for Darwin platforms." + - " For other targets, please, use `-Xadd-light-debug=enable` instead.") - } - putIfNotNull(LIGHT_DEBUG, when (val it = arguments.lightDebugString) { - "enable" -> true - "disable" -> false - null -> null - else -> { - configuration.report(ERROR, "Unsupported -Xadd-light-debug= value: $it. Possible values are 'enable'/'disable'") - null - } - }) - putIfNotNull(GENERATE_DEBUG_TRAMPOLINE, when (val it = arguments.generateDebugTrampolineString) { - "enable" -> true - "disable" -> false - null -> null - else -> { - configuration.report(ERROR, "Unsupported -Xg-generate-debug-tramboline= value: $it. Possible values are 'enable'/'disable'") - null - } - }) - put(STATIC_FRAMEWORK, selectFrameworkType(configuration, arguments, outputKind)) - put(OVERRIDE_CLANG_OPTIONS, arguments.clangOptions.toNonNullList()) - - put(EXPORT_KDOC, arguments.exportKDoc) - - put(PRINT_IR, arguments.printIr) - put(PRINT_BITCODE, arguments.printBitCode) - put(CHECK_EXTERNAL_CALLS, arguments.checkExternalCalls) - put(PRINT_FILES, arguments.printFiles) - - put(PURGE_USER_LIBS, arguments.purgeUserLibs) - - if (arguments.verifyCompiler != null) - put(VERIFY_COMPILER, arguments.verifyCompiler == "true") - put(VERIFY_IR, arguments.verifyIr) - put(VERIFY_BITCODE, arguments.verifyBitCode) - - put(ENABLE_ASSERTIONS, arguments.enableAssertions) - - val memoryModelFromArgument = when (arguments.memoryModel) { - "relaxed" -> MemoryModel.RELAXED - "strict" -> MemoryModel.STRICT - "experimental" -> MemoryModel.EXPERIMENTAL - null -> null - else -> { - configuration.report(ERROR, "Unsupported memory model ${arguments.memoryModel}") - null - } - } - - // TODO: revise priority and/or report conflicting values. - if (get(BinaryOptions.memoryModel) == null) { - putIfNotNull(BinaryOptions.memoryModel, memoryModelFromArgument) - } - - when { - arguments.generateWorkerTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.WORKER) - arguments.generateTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.MAIN_THREAD) - arguments.generateNoExitTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.MAIN_THREAD_NO_EXIT) - else -> put(GENERATE_TEST_RUNNER, TestRunnerKind.NONE) - } - // We need to download dependencies only if we use them ( = there are files to compile). - put( - CHECK_DEPENDENCIES, - configuration.kotlinSourceRoots.isNotEmpty() - || !arguments.includes.isNullOrEmpty() - || !arguments.exportedLibraries.isNullOrEmpty() - || outputKind.isCache - || arguments.checkDependencies - ) - if (arguments.friendModules != null) - put(FRIEND_MODULES, arguments.friendModules!!.split(File.pathSeparator).filterNot(String::isEmpty)) - - if(arguments.refinesPaths != null) - put(REFINES_MODULES, arguments.refinesPaths!!.filterNot(String::isEmpty)) - - put(EXPORTED_LIBRARIES, selectExportedLibraries(configuration, arguments, outputKind)) - put(INCLUDED_LIBRARIES, selectIncludes(configuration, arguments, outputKind)) - put(FRAMEWORK_IMPORT_HEADERS, arguments.frameworkImportHeaders.toNonNullList()) - arguments.emitLazyObjCHeader?.let { put(EMIT_LAZY_OBJC_HEADER_FILE, it) } - - put(BITCODE_EMBEDDING_MODE, selectBitcodeEmbeddingMode(this, arguments)) - put(DEBUG_INFO_VERSION, arguments.debugInfoFormatVersion.toInt()) - put(COVERAGE, arguments.coverage) - put(LIBRARIES_TO_COVER, arguments.coveredLibraries.toNonNullList()) - arguments.coverageFile?.let { put(PROFRAW_PATH, it) } - put(OBJC_GENERICS, !arguments.noObjcGenerics) - put(DEBUG_PREFIX_MAP, parseDebugPrefixMap(arguments, configuration)) - - val libraryToAddToCache = parseLibraryToAddToCache(arguments, configuration, outputKind) - if (libraryToAddToCache != null && !arguments.outputName.isNullOrEmpty()) - configuration.report(ERROR, "${K2NativeCompilerArguments.ADD_CACHE} already implicitly sets output file name") - libraryToAddToCache?.let { put(LIBRARY_TO_ADD_TO_CACHE, it) } - put(CACHED_LIBRARIES, parseCachedLibraries(arguments, configuration)) - put(CACHE_DIRECTORIES, arguments.cacheDirectories.toNonNullList()) - put(AUTO_CACHEABLE_FROM, arguments.autoCacheableFrom.toNonNullList()) - arguments.autoCacheDir?.let { put(AUTO_CACHE_DIR, it) } - arguments.filesToCache?.let { put(FILES_TO_CACHE, it.toList()) } - put(MAKE_PER_FILE_CACHE, arguments.makePerFileCache) - - parseShortModuleName(arguments, configuration, outputKind)?.let { - put(SHORT_MODULE_NAME, it) - } - put(FAKE_OVERRIDE_VALIDATOR, arguments.fakeOverrideValidator) - putIfNotNull(PRE_LINK_CACHES, parsePreLinkCachesValue(configuration, arguments.preLinkCaches)) - putIfNotNull(OVERRIDE_KONAN_PROPERTIES, parseOverrideKonanProperties(arguments, configuration)) - put(DESTROY_RUNTIME_MODE, when (arguments.destroyRuntimeMode) { - "legacy" -> DestroyRuntimeMode.LEGACY - "on-shutdown" -> DestroyRuntimeMode.ON_SHUTDOWN - else -> { - configuration.report(ERROR, "Unsupported destroy runtime mode ${arguments.destroyRuntimeMode}") - DestroyRuntimeMode.ON_SHUTDOWN - } - }) - putIfNotNull(GARBAGE_COLLECTOR, when (arguments.gc) { - null -> null - "noop" -> GC.NOOP - "stms" -> GC.SAME_THREAD_MARK_AND_SWEEP - "cms" -> GC.CONCURRENT_MARK_AND_SWEEP - else -> { - configuration.report(ERROR, "Unsupported GC ${arguments.gc}") - null - } - }) - putIfNotNull(PROPERTY_LAZY_INITIALIZATION, when (arguments.propertyLazyInitialization) { - null -> null - "enable" -> true - "disable" -> false - else -> { - configuration.report(ERROR, "Expected 'enable' or 'disable' for lazy property initialization") - false - } - }) - putIfNotNull(ALLOCATION_MODE, when (arguments.allocator) { - null -> null - "std" -> AllocationMode.STD - "mimalloc" -> AllocationMode.MIMALLOC - "custom" -> AllocationMode.CUSTOM - else -> { - configuration.report(ERROR, "Expected 'std', 'mimalloc', or 'custom' for allocator") - AllocationMode.STD - } - }) - putIfNotNull(WORKER_EXCEPTION_HANDLING, when (arguments.workerExceptionHandling) { - null -> null - "legacy" -> WorkerExceptionHandling.LEGACY - "use-hook" -> WorkerExceptionHandling.USE_HOOK - else -> { - configuration.report(ERROR, "Unsupported worker exception handling mode ${arguments.workerExceptionHandling}") - WorkerExceptionHandling.LEGACY - } - }) - put(LAZY_IR_FOR_CACHES, when (arguments.lazyIrForCaches) { - null -> true - "enable" -> true - "disable" -> false - else -> { - configuration.report(ERROR, "Expected 'enable' or 'disable' for lazy IR usage for cached libraries") - false - } - }) - - arguments.externalDependencies?.let { put(EXTERNAL_DEPENDENCIES, it) } - putIfNotNull(LLVM_VARIANT, when (val variant = arguments.llvmVariant) { - "user" -> LlvmVariant.User - "dev" -> LlvmVariant.Dev - null -> null - else -> { - val file = File(variant) - if (!file.exists) { - configuration.report(ERROR, "`-Xllvm-variant` should be `user`, `dev` or an absolute path. Got: $variant") - null - } else { - LlvmVariant.Custom(file) - } - } - }) - putIfNotNull(RUNTIME_LOGS, arguments.runtimeLogs) - putIfNotNull(BUNDLE_ID, parseBundleId(arguments, outputKind, configuration)) - arguments.testDumpOutputPath?.let { put(TEST_DUMP_OUTPUT_PATH, it) } - put(PARTIAL_LINKAGE, arguments.partialLinkage) - put(OMIT_FRAMEWORK_BINARY, arguments.omitFrameworkBinary) - } - } + arguments: K2NativeCompilerArguments, + services: Services + ) { + configuration.setupFromArguments(arguments) } override fun createArguments() = K2NativeCompilerArguments() @@ -429,232 +177,13 @@ class K2Native : CLICompiler() { } } -private fun selectFrameworkType( - configuration: CompilerConfiguration, - arguments: K2NativeCompilerArguments, - outputKind: CompilerOutputKind -): Boolean { - return if (outputKind != CompilerOutputKind.FRAMEWORK && arguments.staticFramework) { - configuration.report( - STRONG_WARNING, - "'${K2NativeCompilerArguments.STATIC_FRAMEWORK_FLAG}' is only supported when producing frameworks, " + - "but the compiler is producing ${outputKind.name.lowercase()}" - ) - false - } else { - arguments.staticFramework - } -} - -private fun parsePreLinkCachesValue( - configuration: CompilerConfiguration, - value: String? -): Boolean? = when (value) { - "enable" -> true - "disable" -> false - null -> null - else -> { - configuration.report(ERROR, "Unsupported `-Xpre-link-caches` value: $value. Possible values are 'enable'/'disable'") - null - } - } - -private fun selectBitcodeEmbeddingMode( - configuration: CompilerConfiguration, - arguments: K2NativeCompilerArguments -): BitcodeEmbedding.Mode = when { - arguments.embedBitcodeMarker -> { - if (arguments.embedBitcode) { - configuration.report( - STRONG_WARNING, - "'${K2NativeCompilerArguments.EMBED_BITCODE_FLAG}' is ignored because '${K2NativeCompilerArguments.EMBED_BITCODE_MARKER_FLAG}' is specified" - ) - } - BitcodeEmbedding.Mode.MARKER - } - arguments.embedBitcode -> { - BitcodeEmbedding.Mode.FULL - } - else -> BitcodeEmbedding.Mode.NONE -} - -private fun selectExportedLibraries( - configuration: CompilerConfiguration, - arguments: K2NativeCompilerArguments, - outputKind: CompilerOutputKind -): List { - val exportedLibraries = arguments.exportedLibraries?.toList().orEmpty() - - return if (exportedLibraries.isNotEmpty() && outputKind != CompilerOutputKind.FRAMEWORK && - outputKind != CompilerOutputKind.STATIC && outputKind != CompilerOutputKind.DYNAMIC) { - configuration.report(STRONG_WARNING, - "-Xexport-library is only supported when producing frameworks or native libraries, " + - "but the compiler is producing ${outputKind.name.lowercase()}") - - emptyList() - } else { - exportedLibraries - } -} - -private fun selectIncludes( - configuration: CompilerConfiguration, - arguments: K2NativeCompilerArguments, - outputKind: CompilerOutputKind -): List { - val includes = arguments.includes?.toList().orEmpty() - - return if (includes.isNotEmpty() && outputKind == CompilerOutputKind.LIBRARY) { - configuration.report( - ERROR, - "The ${K2NativeCompilerArguments.INCLUDE_ARG} flag is not supported when producing ${outputKind.name.lowercase()}" - ) - emptyList() - } else { - includes - } -} - -private fun parseCachedLibraries( - arguments: K2NativeCompilerArguments, - configuration: CompilerConfiguration -): Map = arguments.cachedLibraries?.asList().orEmpty().mapNotNull { - val libraryAndCache = it.split(",") - if (libraryAndCache.size != 2) { - configuration.report( - ERROR, - "incorrect ${K2NativeCompilerArguments.CACHED_LIBRARY} format: expected ',', got '$it'" - ) - null - } else { - libraryAndCache[0] to libraryAndCache[1] - } -}.toMap() - -private fun parseLibraryToAddToCache( - arguments: K2NativeCompilerArguments, - configuration: CompilerConfiguration, - outputKind: CompilerOutputKind -): String? { - val input = arguments.libraryToAddToCache - - return if (input != null && !outputKind.isCache) { - configuration.report(ERROR, "${K2NativeCompilerArguments.ADD_CACHE} can't be used when not producing cache") - null - } else { - input - } -} - -// TODO: Support short names for current module in ObjC export and lift this limitation. -private fun parseShortModuleName( - arguments: K2NativeCompilerArguments, - configuration: CompilerConfiguration, - outputKind: CompilerOutputKind -): String? { - val input = arguments.shortModuleName - - return if (input != null && outputKind != CompilerOutputKind.LIBRARY) { - configuration.report( - STRONG_WARNING, - "${K2NativeCompilerArguments.SHORT_MODULE_NAME_ARG} is only supported when producing a Kotlin library, " + - "but the compiler is producing ${outputKind.name.lowercase()}" - ) - null - } else { - input - } -} - -private fun parseDebugPrefixMap( - arguments: K2NativeCompilerArguments, - configuration: CompilerConfiguration -): Map = arguments.debugPrefixMap?.asList().orEmpty().mapNotNull { - val libraryAndCache = it.split("=") - if (libraryAndCache.size != 2) { - configuration.report( - ERROR, - "incorrect debug prefix map format: expected '=', got '$it'" - ) - null - } else { - libraryAndCache[0] to libraryAndCache[1] - } -}.toMap() - -class BinaryOptionWithValue(val option: BinaryOption, val value: T) - -private fun CompilerConfiguration.put(binaryOptionWithValue: BinaryOptionWithValue) { - this.put(binaryOptionWithValue.option.compilerConfigurationKey, binaryOptionWithValue.value) -} +typealias BinaryOptionWithValue = org.jetbrains.kotlin.backend.konan.BinaryOptionWithValue +@Suppress("unused") fun parseBinaryOptions( arguments: K2NativeCompilerArguments, configuration: CompilerConfiguration -): List> { - val keyValuePairs = parseKeyValuePairs(arguments.binaryOptions, configuration) ?: return emptyList() - - return keyValuePairs.mapNotNull { (key, value) -> - val option = BinaryOptions.getByName(key) - if (option == null) { - configuration.report(STRONG_WARNING, "Unknown binary option '$key'") - null - } else { - parseBinaryOption(option, value, configuration) - } - } -} - -private fun parseBinaryOption( - option: BinaryOption, - valueName: String, - configuration: CompilerConfiguration -): BinaryOptionWithValue? { - val value = option.valueParser.parse(valueName) - return if (value == null) { - configuration.report(STRONG_WARNING, "Unknown value '$valueName' of binary option '${option.name}'. " + - "Possible values are: ${option.valueParser.validValuesHint}") - null - } else { - BinaryOptionWithValue(option, value) - } -} - -private fun parseOverrideKonanProperties( - arguments: K2NativeCompilerArguments, - configuration: CompilerConfiguration -): Map? = parseKeyValuePairs(arguments.overrideKonanProperties, configuration) - -private fun parseKeyValuePairs( - argumentValue: Array?, - configuration: CompilerConfiguration -): Map? = argumentValue?.mapNotNull { - val keyValueSeparatorIndex = it.indexOf('=') - if (keyValueSeparatorIndex > 0) { - it.substringBefore('=') to it.substringAfter('=') - } else { - configuration.report( - ERROR, - "incorrect property format: expected '=', got '$it'" - ) - null - } -}?.toMap() - -private fun parseBundleId( - arguments: K2NativeCompilerArguments, - outputKind: CompilerOutputKind, - configuration: CompilerConfiguration -): String? { - val argumentValue = arguments.bundleId - return if (argumentValue != null && outputKind != CompilerOutputKind.FRAMEWORK) { - configuration.report(STRONG_WARNING, "Setting a bundle ID is only supported when producing a framework " + - "but the compiler is producing ${outputKind.name.lowercase()}") - null - } else { - argumentValue - } -} +): List> = org.jetbrains.kotlin.backend.konan.parseBinaryOptions(arguments, configuration) fun main(args: Array) = K2Native.main(args) fun mainNoExitWithGradleRenderer(args: Array) = K2Native.mainNoExitWithGradleRenderer(args) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt index 0d8c2040faa..4bb1f8d4aab 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt @@ -73,8 +73,8 @@ class CacheBuilder( cachedLibraries.entries.joinToString("\n ") { "${it.key}: ${it.value}" }) configuration.report(CompilerMessageSeverity.LOGGING, " cache dir: " + libraryCacheDirectory.absolutePath) - put(KonanConfigKeys.TARGET, konanConfig.target.toString()) - put(KonanConfigKeys.DEBUG, konanConfig.debug) + + setupCommonOptionsForCaches(konanConfig) put(KonanConfigKeys.PRODUCE, CompilerOutputKind.STATIC_CACHE) put(KonanConfigKeys.LIBRARY_TO_ADD_TO_CACHE, libraryPath) put(KonanConfigKeys.NODEFAULTLIBS, true) @@ -84,17 +84,6 @@ class CacheBuilder( put(KonanConfigKeys.CACHED_LIBRARIES, cachedLibraries) put(KonanConfigKeys.CACHE_DIRECTORIES, listOf(libraryCacheDirectory.absolutePath)) put(KonanConfigKeys.MAKE_PER_FILE_CACHE, makePerFileCache) - - put(KonanConfigKeys.PARTIAL_LINKAGE, konanConfig.partialLinkage) - putIfNotNull(KonanConfigKeys.EXTERNAL_DEPENDENCIES, configuration.get(KonanConfigKeys.EXTERNAL_DEPENDENCIES)) - put(BinaryOptions.memoryModel, konanConfig.memoryModel) - put(KonanConfigKeys.PROPERTY_LAZY_INITIALIZATION, konanConfig.propertyLazyInitialization) - put(BinaryOptions.stripDebugInfoFromNativeLibs, !konanConfig.useDebugInfoInNativeLibs) - put(KonanConfigKeys.ALLOCATION_MODE, konanConfig.allocationMode) - put(KonanConfigKeys.GARBAGE_COLLECTOR, konanConfig.gc) - put(BinaryOptions.gcSchedulerType, konanConfig.gcSchedulerType) - put(BinaryOptions.freezing, konanConfig.freezing) - put(BinaryOptions.runtimeAssertionsMode, konanConfig.runtimeAssertsMode) } caches[library] = libraryCache.absolutePath } catch (t: Throwable) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index a22cc8299af..76342a68973 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -219,9 +219,11 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration val resolvedLibraries get() = resolve.resolvedLibraries + internal val externalDependenciesFile = configuration.get(KonanConfigKeys.EXTERNAL_DEPENDENCIES)?.let(::File) + internal val userVisibleIrModulesSupport = KonanUserVisibleIrModulesSupport( externalDependenciesLoader = UserVisibleIrModulesSupport.ExternalDependenciesLoader.from( - externalDependenciesFile = configuration.get(KonanConfigKeys.EXTERNAL_DEPENDENCIES)?.let(::File), + externalDependenciesFile = externalDependenciesFile, onMalformedExternalDependencies = { warningMessage -> configuration.report(CompilerMessageSeverity.STRONG_WARNING, warningMessage) }), diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt new file mode 100644 index 00000000000..829b36f023c --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt @@ -0,0 +1,491 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.backend.konan + +import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot +import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.* +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.target.CompilerOutputKind + +fun CompilerConfiguration.setupFromArguments(arguments: K2NativeCompilerArguments) = with(KonanConfigKeys) { + val commonSources = arguments.commonSources?.toSet().orEmpty() + arguments.freeArgs.forEach { + addKotlinSourceRoot(it, it in commonSources) + } + + // Can be overwritten by explicit arguments below. + parseBinaryOptions(arguments, this@setupFromArguments).forEach { optionWithValue -> + put(optionWithValue) + } + + arguments.kotlinHome?.let { put(KONAN_HOME, it) } + + put(NODEFAULTLIBS, arguments.nodefaultlibs || !arguments.libraryToAddToCache.isNullOrEmpty()) + put(NOENDORSEDLIBS, arguments.noendorsedlibs || !arguments.libraryToAddToCache.isNullOrEmpty()) + put(NOSTDLIB, arguments.nostdlib || !arguments.libraryToAddToCache.isNullOrEmpty()) + put(NOPACK, arguments.nopack) + put(NOMAIN, arguments.nomain) + put(LIBRARY_FILES, arguments.libraries.toNonNullList()) + put(LINKER_ARGS, arguments.linkerArguments.toNonNullList() + + arguments.singleLinkerArguments.toNonNullList()) + arguments.moduleName?.let { put(MODULE_NAME, it) } + + // TODO: allow overriding the prefix directly. + arguments.moduleName?.let { put(FULL_EXPORTED_NAME_PREFIX, it) } + + arguments.target?.let { put(TARGET, it) } + + put(INCLUDED_BINARY_FILES, arguments.includeBinaries.toNonNullList()) + put(NATIVE_LIBRARY_FILES, arguments.nativeLibraries.toNonNullList()) + put(REPOSITORIES, arguments.repositories.toNonNullList()) + + // TODO: Collect all the explicit file names into an object + // and teach the compiler to work with temporaries and -save-temps. + + arguments.outputName?.let { put(OUTPUT, it) } + val outputKind = CompilerOutputKind.valueOf( + (arguments.produce ?: "program").uppercase()) + put(PRODUCE, outputKind) + put(METADATA_KLIB, arguments.metadataKlib) + + arguments.libraryVersion?.let { put(LIBRARY_VERSION, it) } + + arguments.mainPackage?.let { put(ENTRY, it) } + arguments.manifestFile?.let { put(MANIFEST_FILE, it) } + arguments.runtimeFile?.let { put(RUNTIME_FILE, it) } + arguments.temporaryFilesDir?.let { put(TEMPORARY_FILES_DIR, it) } + put(SAVE_LLVM_IR, arguments.saveLlvmIrAfter.toList()) + + put(LIST_TARGETS, arguments.listTargets) + put(OPTIMIZATION, arguments.optimization) + put(DEBUG, arguments.debug) + // TODO: remove after 1.4 release. + if (arguments.lightDebugDeprecated) { + report(WARNING, + "-Xg0 is now deprecated and skipped by compiler. Light debug information is enabled by default for Darwin platforms." + + " For other targets, please, use `-Xadd-light-debug=enable` instead.") + } + putIfNotNull(LIGHT_DEBUG, when (val it = arguments.lightDebugString) { + "enable" -> true + "disable" -> false + null -> null + else -> { + report(ERROR, "Unsupported -Xadd-light-debug= value: $it. Possible values are 'enable'/'disable'") + null + } + }) + putIfNotNull(GENERATE_DEBUG_TRAMPOLINE, when (val it = arguments.generateDebugTrampolineString) { + "enable" -> true + "disable" -> false + null -> null + else -> { + report(ERROR, "Unsupported -Xg-generate-debug-tramboline= value: $it. Possible values are 'enable'/'disable'") + null + } + }) + put(STATIC_FRAMEWORK, selectFrameworkType(this@setupFromArguments, arguments, outputKind)) + put(OVERRIDE_CLANG_OPTIONS, arguments.clangOptions.toNonNullList()) + + put(EXPORT_KDOC, arguments.exportKDoc) + + put(PRINT_IR, arguments.printIr) + put(PRINT_BITCODE, arguments.printBitCode) + put(CHECK_EXTERNAL_CALLS, arguments.checkExternalCalls) + put(PRINT_FILES, arguments.printFiles) + + put(PURGE_USER_LIBS, arguments.purgeUserLibs) + + if (arguments.verifyCompiler != null) + put(VERIFY_COMPILER, arguments.verifyCompiler == "true") + put(VERIFY_IR, arguments.verifyIr) + put(VERIFY_BITCODE, arguments.verifyBitCode) + + put(ENABLE_ASSERTIONS, arguments.enableAssertions) + + val memoryModelFromArgument = when (arguments.memoryModel) { + "relaxed" -> MemoryModel.RELAXED + "strict" -> MemoryModel.STRICT + "experimental" -> MemoryModel.EXPERIMENTAL + null -> null + else -> { + report(ERROR, "Unsupported memory model ${arguments.memoryModel}") + null + } + } + + // TODO: revise priority and/or report conflicting values. + if (get(BinaryOptions.memoryModel) == null) { + putIfNotNull(BinaryOptions.memoryModel, memoryModelFromArgument) + } + + when { + arguments.generateWorkerTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.WORKER) + arguments.generateTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.MAIN_THREAD) + arguments.generateNoExitTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.MAIN_THREAD_NO_EXIT) + else -> put(GENERATE_TEST_RUNNER, TestRunnerKind.NONE) + } + // We need to download dependencies only if we use them ( = there are files to compile). + put(CHECK_DEPENDENCIES, + kotlinSourceRoots.isNotEmpty() + || !arguments.includes.isNullOrEmpty() + || !arguments.exportedLibraries.isNullOrEmpty() + || outputKind.isCache + || arguments.checkDependencies + ) + if (arguments.friendModules != null) + put(FRIEND_MODULES, arguments.friendModules!!.split(File.pathSeparator).filterNot(String::isEmpty)) + + if (arguments.refinesPaths != null) + put(REFINES_MODULES, arguments.refinesPaths!!.filterNot(String::isEmpty)) + + put(EXPORTED_LIBRARIES, selectExportedLibraries(this@setupFromArguments, arguments, outputKind)) + put(INCLUDED_LIBRARIES, selectIncludes(this@setupFromArguments, arguments, outputKind)) + put(FRAMEWORK_IMPORT_HEADERS, arguments.frameworkImportHeaders.toNonNullList()) + arguments.emitLazyObjCHeader?.let { put(EMIT_LAZY_OBJC_HEADER_FILE, it) } + + put(BITCODE_EMBEDDING_MODE, selectBitcodeEmbeddingMode(this@setupFromArguments, arguments)) + put(DEBUG_INFO_VERSION, arguments.debugInfoFormatVersion.toInt()) + put(COVERAGE, arguments.coverage) + put(LIBRARIES_TO_COVER, arguments.coveredLibraries.toNonNullList()) + arguments.coverageFile?.let { put(PROFRAW_PATH, it) } + put(OBJC_GENERICS, !arguments.noObjcGenerics) + put(DEBUG_PREFIX_MAP, parseDebugPrefixMap(arguments, this@setupFromArguments)) + + val libraryToAddToCache = parseLibraryToAddToCache(arguments, this@setupFromArguments, outputKind) + if (libraryToAddToCache != null && !arguments.outputName.isNullOrEmpty()) + report(ERROR, "${K2NativeCompilerArguments.ADD_CACHE} already implicitly sets output file name") + libraryToAddToCache?.let { put(LIBRARY_TO_ADD_TO_CACHE, it) } + put(CACHED_LIBRARIES, parseCachedLibraries(arguments, this@setupFromArguments)) + put(CACHE_DIRECTORIES, arguments.cacheDirectories.toNonNullList()) + put(AUTO_CACHEABLE_FROM, arguments.autoCacheableFrom.toNonNullList()) + arguments.autoCacheDir?.let { put(AUTO_CACHE_DIR, it) } + arguments.filesToCache?.let { put(FILES_TO_CACHE, it.toList()) } + put(MAKE_PER_FILE_CACHE, arguments.makePerFileCache) + + parseShortModuleName(arguments, this@setupFromArguments, outputKind)?.let { + put(SHORT_MODULE_NAME, it) + } + put(FAKE_OVERRIDE_VALIDATOR, arguments.fakeOverrideValidator) + putIfNotNull(PRE_LINK_CACHES, parsePreLinkCachesValue(this@setupFromArguments, arguments.preLinkCaches)) + putIfNotNull(OVERRIDE_KONAN_PROPERTIES, parseOverrideKonanProperties(arguments, this@setupFromArguments)) + put(DESTROY_RUNTIME_MODE, when (arguments.destroyRuntimeMode) { + "legacy" -> DestroyRuntimeMode.LEGACY + "on-shutdown" -> DestroyRuntimeMode.ON_SHUTDOWN + else -> { + report(ERROR, "Unsupported destroy runtime mode ${arguments.destroyRuntimeMode}") + DestroyRuntimeMode.ON_SHUTDOWN + } + }) + putIfNotNull(GARBAGE_COLLECTOR, when (arguments.gc) { + null -> null + "noop" -> GC.NOOP + "stms" -> GC.SAME_THREAD_MARK_AND_SWEEP + "cms" -> GC.CONCURRENT_MARK_AND_SWEEP + else -> { + report(ERROR, "Unsupported GC ${arguments.gc}") + null + } + }) + putIfNotNull(PROPERTY_LAZY_INITIALIZATION, when (arguments.propertyLazyInitialization) { + null -> null + "enable" -> true + "disable" -> false + else -> { + report(ERROR, "Expected 'enable' or 'disable' for lazy property initialization") + false + } + }) + putIfNotNull(ALLOCATION_MODE, when (arguments.allocator) { + null -> null + "std" -> AllocationMode.STD + "mimalloc" -> AllocationMode.MIMALLOC + "custom" -> AllocationMode.CUSTOM + else -> { + report(ERROR, "Expected 'std', 'mimalloc', or 'custom' for allocator") + AllocationMode.STD + } + }) + putIfNotNull(WORKER_EXCEPTION_HANDLING, when (arguments.workerExceptionHandling) { + null -> null + "legacy" -> WorkerExceptionHandling.LEGACY + "use-hook" -> WorkerExceptionHandling.USE_HOOK + else -> { + report(ERROR, "Unsupported worker exception handling mode ${arguments.workerExceptionHandling}") + WorkerExceptionHandling.LEGACY + } + }) + put(LAZY_IR_FOR_CACHES, when (arguments.lazyIrForCaches) { + null -> true + "enable" -> true + "disable" -> false + else -> { + report(ERROR, "Expected 'enable' or 'disable' for lazy IR usage for cached libraries") + false + } + }) + + arguments.externalDependencies?.let { put(EXTERNAL_DEPENDENCIES, it) } + putIfNotNull(LLVM_VARIANT, when (val variant = arguments.llvmVariant) { + "user" -> LlvmVariant.User + "dev" -> LlvmVariant.Dev + null -> null + else -> { + val file = File(variant) + if (!file.exists) { + report(ERROR, "`-Xllvm-variant` should be `user`, `dev` or an absolute path. Got: $variant") + null + } else { + LlvmVariant.Custom(file) + } + } + }) + putIfNotNull(RUNTIME_LOGS, arguments.runtimeLogs) + putIfNotNull(BUNDLE_ID, parseBundleId(arguments, outputKind, this@setupFromArguments)) + arguments.testDumpOutputPath?.let { put(TEST_DUMP_OUTPUT_PATH, it) } + put(PARTIAL_LINKAGE, arguments.partialLinkage) + put(OMIT_FRAMEWORK_BINARY, arguments.omitFrameworkBinary) +} + +internal fun CompilerConfiguration.setupCommonOptionsForCaches(konanConfig: KonanConfig) = with(KonanConfigKeys) { + put(TARGET, konanConfig.target.toString()) + put(DEBUG, konanConfig.debug) + put(PARTIAL_LINKAGE, konanConfig.partialLinkage) + putIfNotNull(EXTERNAL_DEPENDENCIES, konanConfig.externalDependenciesFile?.absolutePath) + put(BinaryOptions.memoryModel, konanConfig.memoryModel) + put(PROPERTY_LAZY_INITIALIZATION, konanConfig.propertyLazyInitialization) + put(BinaryOptions.stripDebugInfoFromNativeLibs, !konanConfig.useDebugInfoInNativeLibs) + put(ALLOCATION_MODE, konanConfig.allocationMode) + put(GARBAGE_COLLECTOR, konanConfig.gc) + put(BinaryOptions.gcSchedulerType, konanConfig.gcSchedulerType) + put(BinaryOptions.freezing, konanConfig.freezing) + put(BinaryOptions.runtimeAssertionsMode, konanConfig.runtimeAssertsMode) +} + +private fun Array?.toNonNullList() = this?.asList().orEmpty() + +private fun selectFrameworkType( + configuration: CompilerConfiguration, + arguments: K2NativeCompilerArguments, + outputKind: CompilerOutputKind +): Boolean { + return if (outputKind != CompilerOutputKind.FRAMEWORK && arguments.staticFramework) { + configuration.report( + STRONG_WARNING, + "'${K2NativeCompilerArguments.STATIC_FRAMEWORK_FLAG}' is only supported when producing frameworks, " + + "but the compiler is producing ${outputKind.name.lowercase()}" + ) + false + } else { + arguments.staticFramework + } +} + +private fun parsePreLinkCachesValue( + configuration: CompilerConfiguration, + value: String? +): Boolean? = when (value) { + "enable" -> true + "disable" -> false + null -> null + else -> { + configuration.report(ERROR, "Unsupported `-Xpre-link-caches` value: $value. Possible values are 'enable'/'disable'") + null + } +} + +private fun selectBitcodeEmbeddingMode( + configuration: CompilerConfiguration, + arguments: K2NativeCompilerArguments +): BitcodeEmbedding.Mode = when { + arguments.embedBitcodeMarker -> { + if (arguments.embedBitcode) { + configuration.report( + STRONG_WARNING, + "'${K2NativeCompilerArguments.EMBED_BITCODE_FLAG}' is ignored because '${K2NativeCompilerArguments.EMBED_BITCODE_MARKER_FLAG}' is specified" + ) + } + BitcodeEmbedding.Mode.MARKER + } + arguments.embedBitcode -> { + BitcodeEmbedding.Mode.FULL + } + else -> BitcodeEmbedding.Mode.NONE +} + +private fun selectExportedLibraries( + configuration: CompilerConfiguration, + arguments: K2NativeCompilerArguments, + outputKind: CompilerOutputKind +): List { + val exportedLibraries = arguments.exportedLibraries?.toList().orEmpty() + + return if (exportedLibraries.isNotEmpty() && outputKind != CompilerOutputKind.FRAMEWORK && + outputKind != CompilerOutputKind.STATIC && outputKind != CompilerOutputKind.DYNAMIC) { + configuration.report(STRONG_WARNING, + "-Xexport-library is only supported when producing frameworks or native libraries, " + + "but the compiler is producing ${outputKind.name.lowercase()}") + + emptyList() + } else { + exportedLibraries + } +} + +private fun selectIncludes( + configuration: CompilerConfiguration, + arguments: K2NativeCompilerArguments, + outputKind: CompilerOutputKind +): List { + val includes = arguments.includes?.toList().orEmpty() + + return if (includes.isNotEmpty() && outputKind == CompilerOutputKind.LIBRARY) { + configuration.report( + ERROR, + "The ${K2NativeCompilerArguments.INCLUDE_ARG} flag is not supported when producing ${outputKind.name.lowercase()}" + ) + emptyList() + } else { + includes + } +} + +private fun parseCachedLibraries( + arguments: K2NativeCompilerArguments, + configuration: CompilerConfiguration +): Map = arguments.cachedLibraries?.asList().orEmpty().mapNotNull { + val libraryAndCache = it.split(",") + if (libraryAndCache.size != 2) { + configuration.report( + ERROR, + "incorrect ${K2NativeCompilerArguments.CACHED_LIBRARY} format: expected ',', got '$it'" + ) + null + } else { + libraryAndCache[0] to libraryAndCache[1] + } +}.toMap() + +private fun parseLibraryToAddToCache( + arguments: K2NativeCompilerArguments, + configuration: CompilerConfiguration, + outputKind: CompilerOutputKind +): String? { + val input = arguments.libraryToAddToCache + + return if (input != null && !outputKind.isCache) { + configuration.report(ERROR, "${K2NativeCompilerArguments.ADD_CACHE} can't be used when not producing cache") + null + } else { + input + } +} + +// TODO: Support short names for current module in ObjC export and lift this limitation. +private fun parseShortModuleName( + arguments: K2NativeCompilerArguments, + configuration: CompilerConfiguration, + outputKind: CompilerOutputKind +): String? { + val input = arguments.shortModuleName + + return if (input != null && outputKind != CompilerOutputKind.LIBRARY) { + configuration.report( + STRONG_WARNING, + "${K2NativeCompilerArguments.SHORT_MODULE_NAME_ARG} is only supported when producing a Kotlin library, " + + "but the compiler is producing ${outputKind.name.lowercase()}" + ) + null + } else { + input + } +} + +private fun parseDebugPrefixMap( + arguments: K2NativeCompilerArguments, + configuration: CompilerConfiguration +): Map = arguments.debugPrefixMap?.asList().orEmpty().mapNotNull { + val libraryAndCache = it.split("=") + if (libraryAndCache.size != 2) { + configuration.report(ERROR, "incorrect debug prefix map format: expected '=', got '$it'") + null + } else { + libraryAndCache[0] to libraryAndCache[1] + } +}.toMap() + +class BinaryOptionWithValue(val option: BinaryOption, val value: T) + +private fun CompilerConfiguration.put(binaryOptionWithValue: BinaryOptionWithValue) { + this.put(binaryOptionWithValue.option.compilerConfigurationKey, binaryOptionWithValue.value) +} + +fun parseBinaryOptions( + arguments: K2NativeCompilerArguments, + configuration: CompilerConfiguration +): List> { + val keyValuePairs = parseKeyValuePairs(arguments.binaryOptions, configuration) ?: return emptyList() + + return keyValuePairs.mapNotNull { (key, value) -> + val option = BinaryOptions.getByName(key) + if (option == null) { + configuration.report(STRONG_WARNING, "Unknown binary option '$key'") + null + } else { + parseBinaryOption(option, value, configuration) + } + } +} + +private fun parseBinaryOption( + option: BinaryOption, + valueName: String, + configuration: CompilerConfiguration +): BinaryOptionWithValue? { + val value = option.valueParser.parse(valueName) + return if (value == null) { + configuration.report(STRONG_WARNING, "Unknown value '$valueName' of binary option '${option.name}'. " + + "Possible values are: ${option.valueParser.validValuesHint}") + null + } else { + BinaryOptionWithValue(option, value) + } +} + +private fun parseOverrideKonanProperties( + arguments: K2NativeCompilerArguments, + configuration: CompilerConfiguration +): Map? = parseKeyValuePairs(arguments.overrideKonanProperties, configuration) + +private fun parseKeyValuePairs( + argumentValue: Array?, + configuration: CompilerConfiguration +): Map? = argumentValue?.mapNotNull { + val keyValueSeparatorIndex = it.indexOf('=') + if (keyValueSeparatorIndex > 0) { + it.substringBefore('=') to it.substringAfter('=') + } else { + configuration.report(ERROR, "incorrect property format: expected '=', got '$it'") + null + } +}?.toMap() + +private fun parseBundleId( + arguments: K2NativeCompilerArguments, + outputKind: CompilerOutputKind, + configuration: CompilerConfiguration +): String? { + val argumentValue = arguments.bundleId + return if (argumentValue != null && outputKind != CompilerOutputKind.FRAMEWORK) { + configuration.report(STRONG_WARNING, "Setting a bundle ID is only supported when producing a framework " + + "but the compiler is producing ${outputKind.name.lowercase()}") + null + } else { + argumentValue + } +}