Implement '-Xexport-library=<path>' compiler argument (#2262)
Produced framework includes the entire API of exported libraries
This commit is contained in:
committed by
GitHub
parent
4f0633412d
commit
d02a51ba98
@@ -188,6 +188,8 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
||||
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<String> {
|
||||
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<String>) = K2Native.main(args)
|
||||
|
||||
|
||||
@@ -103,6 +103,14 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(value = "-Xenable", deprecatedName = "--enable", valueDescription = "<Phase>", description = "Enable backend phase")
|
||||
var enablePhases: Array<String>? = null
|
||||
|
||||
@Argument(
|
||||
value = "-Xexport-library",
|
||||
valueDescription = "<path>",
|
||||
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<String>? = null
|
||||
|
||||
@Argument(value= "-Xlist-phases", deprecatedName = "--list_phases", description = "List all backend phases")
|
||||
var listPhases: Boolean = false
|
||||
|
||||
|
||||
+84
@@ -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<ModuleDescriptor> {
|
||||
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<KonanLibrary> {
|
||||
val exportedLibraries = configuration.getList(KonanConfigKeys.EXPORTED_LIBRARIES).map { File(it) }.toSet()
|
||||
val remainingExportedLibraries = exportedLibraries.toMutableSet()
|
||||
|
||||
val result = mutableListOf<KonanLibrary>()
|
||||
|
||||
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
|
||||
}
|
||||
+4
-1
@@ -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<KonanLibrary> {
|
||||
|
||||
+2
@@ -28,6 +28,8 @@ class KonanConfigKeys {
|
||||
= CompilerConfigurationKey.create("enable backend phases")
|
||||
val ENTRY: CompilerConfigurationKey<String?>
|
||||
= CompilerConfigurationKey.create("fully qualified main() name")
|
||||
val EXPORTED_LIBRARIES: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create<List<String>>("libraries included into produced framework API")
|
||||
val FRIEND_MODULES: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create<List<String>>("friend module paths")
|
||||
val GENERATE_TEST_RUNNER: CompilerConfigurationKey<Boolean>
|
||||
|
||||
+1
-1
@@ -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()
|
||||
|
||||
+24
-7
@@ -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<ModuleDescriptor>,
|
||||
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<ModuleDescriptor>,
|
||||
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<ClassDescriptor>()
|
||||
internal val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>()
|
||||
|
||||
private val stdlibModule = moduleDescriptors.first().allDependencyModules.single { it.isKonanStdlib() }
|
||||
|
||||
private val mappedToNSNumber: List<ClassDescriptor> = 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 {
|
||||
|
||||
+7
-3
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-8
@@ -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<ModuleDescriptor>,
|
||||
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<ModuleDescriptor>,
|
||||
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())
|
||||
|
||||
Reference in New Issue
Block a user