diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 30cbef46618..24aaeafab02 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -188,6 +188,8 @@ class K2Native : CLICompiler() { if (arguments.friendModules != null) put(FRIEND_MODULES, arguments.friendModules!!.split(File.pathSeparator).filterNot(String::isEmpty)) + put(EXPORTED_LIBRARIES, selectExportedLibraries(configuration, arguments, outputKind)) + put(BITCODE_EMBEDDING_MODE, selectBitcodeEmbeddingMode(this, arguments, outputKind)) put(DEBUG_INFO_VERSION, arguments.debugInfoFormatVersion.toInt()) } @@ -254,5 +256,22 @@ private fun selectBitcodeEmbeddingMode( } } +private fun selectExportedLibraries( + configuration: CompilerConfiguration, + arguments: K2NativeCompilerArguments, + outputKind: CompilerOutputKind +): List { + val exportedLibraries = arguments.exportedLibraries?.toList().orEmpty() + + return if (exportedLibraries.isNotEmpty() && outputKind != CompilerOutputKind.FRAMEWORK) { + configuration.report(STRONG_WARNING, "-Xexport-library is only supported when producing frameworks, " + + "but the compiler is producing ${outputKind.name.toLowerCase()}") + + emptyList() + } else { + exportedLibraries + } +} + fun main(args: Array) = K2Native.main(args) diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index 40c209f5e02..e48c1f8e179 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -103,6 +103,14 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xenable", deprecatedName = "--enable", valueDescription = "", description = "Enable backend phase") var enablePhases: Array? = null + @Argument( + value = "-Xexport-library", + valueDescription = "", + description = "Path to the library to be included into produced framework API\n" + + "Must be the path of a library passed with '-library'" + ) + var exportedLibraries: Array? = null + @Argument(value= "-Xlist-phases", deprecatedName = "--list_phases", description = "List all backend phases") var listPhases: Boolean = false diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ExportedLibraries.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ExportedLibraries.kt new file mode 100644 index 00000000000..49f9c8d6b50 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ExportedLibraries.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin +import org.jetbrains.kotlin.descriptors.konan.DeserializedKonanModuleOrigin +import org.jetbrains.kotlin.descriptors.konan.SyntheticModulesOrigin +import org.jetbrains.kotlin.descriptors.konan.konanModuleOrigin +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.isInterop +import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolveResult + +internal fun validateExportedLibraries(configuration: CompilerConfiguration, resolvedLibraries: KonanLibraryResolveResult) { + getExportedLibraries(configuration, resolvedLibraries, report = true) +} + +internal fun Context.getExportedDependencies(): List { + val exportedLibraries = getExportedLibraries(this.config.configuration, this.config.resolvedLibraries, report = false) + .toSet() + + val result = this.moduleDescriptor.allDependencyModules.filter { + val origin = it.konanModuleOrigin + when (origin) { + CurrentKonanModuleOrigin, SyntheticModulesOrigin -> false + is DeserializedKonanModuleOrigin -> { + origin.library in exportedLibraries + } + } + } + + return result +} + +private fun getExportedLibraries( + configuration: CompilerConfiguration, + resolvedLibraries: KonanLibraryResolveResult, + report: Boolean +): List { + val exportedLibraries = configuration.getList(KonanConfigKeys.EXPORTED_LIBRARIES).map { File(it) }.toSet() + val remainingExportedLibraries = exportedLibraries.toMutableSet() + + val result = mutableListOf() + + val libraries = resolvedLibraries.getFullList(null) + + for (library in libraries) { + val libraryFile = library.libraryFile + if (libraryFile in exportedLibraries) { + remainingExportedLibraries -= libraryFile + if (library.isInterop || library.isDefault) { + if (report) { + val kind = if (library.isInterop) "Interop" else "Default" + configuration.report( + CompilerMessageSeverity.STRONG_WARNING, + "$kind library ${library.libraryName} can't be exported with -Xexport-library" + ) + } + } else { + result += library + } + } + } + + if (report && remainingExportedLibraries.isNotEmpty()) { + val message = buildString { + appendln("Following libraries are specified to be exported with -Xexport-library, but not included to the build:") + remainingExportedLibraries.forEach { appendln(it) } + appendln() + appendln("Included libraries:") + libraries.forEach { appendln(it.libraryFile) } + } + + configuration.report(CompilerMessageSeverity.STRONG_WARNING, message) + } + + return result +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 9f26ccf28d1..372bb64ca10 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -94,7 +94,10 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration resolver.resolveWithDependencies( unresolvedLibraries, noStdLib = configuration.getBoolean(KonanConfigKeys.NOSTDLIB), - noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS) ) + noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS)).also { + + validateExportedLibraries(configuration, it) + } } fun librariesWithDependencies(moduleDescriptor: ModuleDescriptor?): List { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index a4ac173c08b..77299be3ff3 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -28,6 +28,8 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("enable backend phases") val ENTRY: CompilerConfigurationKey = CompilerConfigurationKey.create("fully qualified main() name") + val EXPORTED_LIBRARIES: CompilerConfigurationKey> + = CompilerConfigurationKey.create>("libraries included into produced framework API") val FRIEND_MODULES: CompilerConfigurationKey> = CompilerConfigurationKey.create>("friend module paths") val GENERATE_TEST_RUNNER: CompilerConfigurationKey diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt index 64ba034a8c6..79d254dd873 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt @@ -54,7 +54,7 @@ internal class ObjCExport(val codegen: CodeGenerator) { } - val namer = ObjCExportNamerImpl(context.moduleDescriptor, context.builtIns, mapper) + val namer = ObjCExportNamerImpl(emptySet(), context.builtIns, mapper, context.moduleDescriptor.namePrefix) objCCodeGenerator = ObjCExportCodeGenerator(codegen, namer, mapper) generatedClasses = emptySet() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt index 25c3d1bd87d..353bc9722f5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name @@ -24,10 +25,24 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.utils.addIfNotNull abstract class ObjCExportHeaderGenerator( - val moduleDescriptor: ModuleDescriptor, + val moduleDescriptors: List, val builtIns: KotlinBuiltIns, - topLevelNamePrefix: String = moduleDescriptor.namePrefix + topLevelNamePrefix: String ) { + + constructor( + moduleDescriptor: ModuleDescriptor, + builtIns: KotlinBuiltIns, + topLevelNamePrefix: String = moduleDescriptor.namePrefix + ) : this(moduleDescriptor, emptyList(), builtIns, topLevelNamePrefix) + + constructor( + moduleDescriptor: ModuleDescriptor, + exportedDependencies: List, + builtIns: KotlinBuiltIns, + topLevelNamePrefix: String = moduleDescriptor.namePrefix + ) : this(listOf(moduleDescriptor) + exportedDependencies, builtIns, topLevelNamePrefix) + internal val mapper: ObjCExportMapper = object : ObjCExportMapper() { override fun getCategoryMembersFor(descriptor: ClassDescriptor) = extensions[descriptor].orEmpty() @@ -39,16 +54,18 @@ abstract class ObjCExportHeaderGenerator( } } - internal val namer = ObjCExportNamerImpl(moduleDescriptor, builtIns, mapper, topLevelNamePrefix) + internal val namer = ObjCExportNamerImpl(moduleDescriptors.toSet(), builtIns, mapper, topLevelNamePrefix) internal val generatedClasses = mutableSetOf() internal val topLevel = mutableMapOf>() + private val stdlibModule = moduleDescriptors.first().allDependencyModules.single { it.isKonanStdlib() } + private val mappedToNSNumber: List = with(builtIns) { val result = mutableListOf(boolean, byte, short, int, long, float, double) UnsignedType.values().mapTo(result) { unsignedType -> - moduleDescriptor.findClassAcrossModuleDependencies(unsignedType.classId)!! + stdlibModule.findClassAcrossModuleDependencies(unsignedType.classId)!! } result @@ -69,7 +86,7 @@ abstract class ObjCExportHeaderGenerator( NSNumberKind.values().forEach { // TODO: NSNumber seem to have different equality semantics. if (it.mappedKotlinClassId != null) { - val descriptor = moduleDescriptor.findClassAcrossModuleDependencies(it.mappedKotlinClassId)!! + val descriptor = stdlibModule.findClassAcrossModuleDependencies(it.mappedKotlinClassId)!! result += CustomTypeMapper.Simple(descriptor, namer.numberBoxName(descriptor).objCName) } @@ -181,7 +198,7 @@ abstract class ObjCExportHeaderGenerator( genKotlinNumbers() - val packageFragments = moduleDescriptor.getPackageFragments() + val packageFragments = moduleDescriptors.flatMap { it.getPackageFragments() } packageFragments.forEach { packageFragment -> packageFragment.getMemberScope().getContributedDescriptors() @@ -263,7 +280,7 @@ abstract class ObjCExportHeaderGenerator( } private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface { - val descriptor = moduleDescriptor.findClassAcrossModuleDependencies(kotlinClassId)!! + val descriptor = stdlibModule.findClassAcrossModuleDependencies(kotlinClassId)!! val name = namer.numberBoxName(descriptor) val members = buildMembers { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt index 68b1786e2b3..28df7c0b223 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt @@ -6,12 +6,16 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.getExportedDependencies import org.jetbrains.kotlin.backend.konan.reportCompilationWarning import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.util.report -internal class ObjCExportHeaderGeneratorImpl(val context: Context) - : ObjCExportHeaderGenerator(context.moduleDescriptor, context.builtIns) { +internal class ObjCExportHeaderGeneratorImpl(val context: Context) : ObjCExportHeaderGenerator( + context.moduleDescriptor, + context.getExportedDependencies(), + context.builtIns +) { override fun reportWarning(text: String) { context.reportCompilationWarning(text) @@ -24,4 +28,4 @@ internal class ObjCExportHeaderGeneratorImpl(val context: Context) isError = false ) } -} \ No newline at end of file +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt index 90a13a41745..bb74a22bbbd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt @@ -31,8 +31,18 @@ interface ObjCExportNamer { } fun createNamer(moduleDescriptor: ModuleDescriptor, + topLevelNamePrefix: String = moduleDescriptor.namePrefix): ObjCExportNamer = + createNamer(moduleDescriptor, emptyList(), topLevelNamePrefix) + +fun createNamer(moduleDescriptor: ModuleDescriptor, + exportedDependencies: List, topLevelNamePrefix: String = moduleDescriptor.namePrefix): ObjCExportNamer { - val generator = object : ObjCExportHeaderGenerator(moduleDescriptor, moduleDescriptor.builtIns, topLevelNamePrefix) { + val generator = object : ObjCExportHeaderGenerator( + moduleDescriptor, + exportedDependencies, + moduleDescriptor.builtIns, + topLevelNamePrefix + ) { override fun reportWarning(text: String) {} override fun reportWarning(method: FunctionDescriptor, text: String) {} } @@ -40,10 +50,10 @@ fun createNamer(moduleDescriptor: ModuleDescriptor, } internal class ObjCExportNamerImpl( - val moduleDescriptor: ModuleDescriptor, + val moduleDescriptors: Set, builtIns: KotlinBuiltIns, val mapper: ObjCExportMapper, - private val topLevelNamePrefix: String = moduleDescriptor.namePrefix + private val topLevelNamePrefix: String ) : ObjCExportNamer { private fun String.toUnmangledClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName = @@ -128,10 +138,15 @@ internal class ObjCExportNamerImpl( override fun getFileClassName(file: SourceFile): ObjCExportNamer.ClassOrProtocolName { val baseName by lazy { - val psiSourceFile = file as? PsiSourceFile ?: error("SourceFile '$file' is not PsiSourceFile") - val psiFile = psiSourceFile.psiFile - val ktFile = psiFile as? KtFile ?: error("PsiFile '$psiFile' is not KtFile") - PackagePartClassUtils.getFilePartShortName(ktFile.name) + val fileName = when (file) { + is PsiSourceFile -> { + val psiFile = file.psiFile + val ktFile = psiFile as? KtFile ?: error("PsiFile '$psiFile' is not KtFile") + ktFile.name + } + else -> file.name ?: error("$file has no name") + } + PackagePartClassUtils.getFilePartShortName(fileName) } val objCName = objCClassNames.getOrPut(file) { @@ -196,7 +211,7 @@ internal class ObjCExportNamerImpl( } private fun StringBuilder.appendTopLevelClassBaseName(descriptor: ClassDescriptor) = apply { - if (descriptor.module != moduleDescriptor) { + if (descriptor.module !in moduleDescriptors) { append(descriptor.module.namePrefix) } append(descriptor.name.asString())