[K/N] Decompose ObjCExport framework generator
Split framework generation into several specialized classes to reduce coupling.
This commit is contained in:
committed by
Space Team
parent
6a491dfd0f
commit
56602290ec
+13
-4
@@ -16,11 +16,20 @@ import org.jetbrains.kotlin.library.SearchPathResolver
|
||||
import org.jetbrains.kotlin.library.isInterop
|
||||
import org.jetbrains.kotlin.library.toUnresolvedLibraries
|
||||
|
||||
internal fun Context.getExportedDependencies(): List<ModuleDescriptor> = getDescriptorsFromLibraries((config.resolve.exportedLibraries + config.resolve.includedLibraries).toSet())
|
||||
internal fun Context.getIncludedLibraryDescriptors(): List<ModuleDescriptor> = getDescriptorsFromLibraries(config.resolve.includedLibraries.toSet())
|
||||
internal fun Context.getExportedDependencies(): List<ModuleDescriptor> =
|
||||
moduleDescriptor.getExportedDependencies(config)
|
||||
|
||||
private fun Context.getDescriptorsFromLibraries(libraries: Set<KonanLibrary>) =
|
||||
moduleDescriptor.allDependencyModules.filter {
|
||||
internal fun ModuleDescriptor.getExportedDependencies(konanConfig: KonanConfig): List<ModuleDescriptor> =
|
||||
getDescriptorsFromLibraries((konanConfig.resolve.exportedLibraries + konanConfig.resolve.includedLibraries).toSet())
|
||||
|
||||
internal fun Context.getIncludedLibraryDescriptors(): List<ModuleDescriptor> =
|
||||
moduleDescriptor.getIncludedLibraryDescriptors(config)
|
||||
|
||||
internal fun ModuleDescriptor.getIncludedLibraryDescriptors(konanConfig: KonanConfig): List<ModuleDescriptor> =
|
||||
getDescriptorsFromLibraries(konanConfig.resolve.includedLibraries.toSet())
|
||||
|
||||
private fun ModuleDescriptor.getDescriptorsFromLibraries(libraries: Set<KonanLibrary>) =
|
||||
allDependencyModules.filter {
|
||||
when (val origin = it.klibModuleOrigin) {
|
||||
CurrentKlibModuleOrigin, SyntheticModulesOrigin -> false
|
||||
is DeserializedKlibModuleOrigin -> origin.library in libraries
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfig
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
|
||||
/**
|
||||
* Constructs an Apple framework without a binary.
|
||||
*/
|
||||
internal class FrameworkBuilder(
|
||||
private val config: KonanConfig,
|
||||
private val infoPListBuilder: InfoPListBuilder,
|
||||
private val moduleMapBuilder: ModuleMapBuilder,
|
||||
private val objCHeaderWriter: ObjCHeaderWriter,
|
||||
private val mainPackageGuesser: MainPackageGuesser,
|
||||
) {
|
||||
fun build(
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
frameworkDirectory: File,
|
||||
frameworkName: String,
|
||||
headerLines: List<String>,
|
||||
moduleDependencies: Set<String>,
|
||||
) {
|
||||
val target = config.target
|
||||
val frameworkContents = when (target.family) {
|
||||
Family.IOS,
|
||||
Family.WATCHOS,
|
||||
Family.TVOS -> frameworkDirectory
|
||||
|
||||
Family.OSX -> frameworkDirectory.child("Versions/A")
|
||||
else -> error(target)
|
||||
}
|
||||
|
||||
val headers = frameworkContents.child("Headers")
|
||||
|
||||
headers.mkdirs()
|
||||
objCHeaderWriter.write("$frameworkName.h", headerLines, headers)
|
||||
|
||||
val modules = frameworkContents.child("Modules")
|
||||
modules.mkdirs()
|
||||
|
||||
val moduleMap = moduleMapBuilder.build(frameworkName, moduleDependencies)
|
||||
|
||||
modules.child("module.modulemap").writeBytes(moduleMap.toByteArray())
|
||||
|
||||
val directory = when (target.family) {
|
||||
Family.IOS,
|
||||
Family.WATCHOS,
|
||||
Family.TVOS -> frameworkContents
|
||||
|
||||
Family.OSX -> frameworkContents.child("Resources").also { it.mkdirs() }
|
||||
else -> error(target)
|
||||
}
|
||||
|
||||
val infoPlistFile = directory.child("Info.plist")
|
||||
val infoPlistContents = infoPListBuilder.build(frameworkName, mainPackageGuesser, moduleDescriptor)
|
||||
infoPlistFile.writeBytes(infoPlistContents.toByteArray())
|
||||
if (target.family == Family.OSX) {
|
||||
frameworkDirectory.child("Versions/Current").createAsSymlink("A")
|
||||
for (child in listOf(frameworkName, "Headers", "Modules", "Resources")) {
|
||||
frameworkDirectory.child(child).createAsSymlink("Versions/Current/$child")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.konan.target.AppleConfigurables
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.platformName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
/**
|
||||
* Creates an Info.plist file for an Apple framework.
|
||||
*
|
||||
* Some documentation about its contents can be found here:
|
||||
* https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html
|
||||
*/
|
||||
internal class InfoPListBuilder(
|
||||
private val config: KonanConfig,
|
||||
) {
|
||||
private val configuration = config.configuration
|
||||
|
||||
fun build(
|
||||
name: String,
|
||||
mainPackageGuesser: MainPackageGuesser,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
): String {
|
||||
val bundleId = computeBundleID(name, mainPackageGuesser, moduleDescriptor)
|
||||
|
||||
val bundleShortVersionString = configuration[BinaryOptions.bundleShortVersionString] ?: "1.0"
|
||||
val bundleVersion = configuration[BinaryOptions.bundleVersion] ?: "1"
|
||||
val properties = config.platform.configurables as AppleConfigurables
|
||||
val platform = properties.platformName()
|
||||
val minimumOsVersion = properties.osVersionMin
|
||||
|
||||
val contents = StringBuilder()
|
||||
contents.append("""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$name</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$bundleId</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$name</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$bundleShortVersionString</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>$platform</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$bundleVersion</string>
|
||||
|
||||
""".trimIndent())
|
||||
|
||||
fun addUiDeviceFamilies(vararg values: Int) {
|
||||
val xmlValues = values.joinToString(separator = "\n") {
|
||||
" <integer>$it</integer>"
|
||||
}
|
||||
contents.append("""
|
||||
| <key>MinimumOSVersion</key>
|
||||
| <string>$minimumOsVersion</string>
|
||||
| <key>UIDeviceFamily</key>
|
||||
| <array>
|
||||
|$xmlValues
|
||||
| </array>
|
||||
|
||||
""".trimMargin())
|
||||
}
|
||||
val target = config.target
|
||||
// UIDeviceFamily mapping:
|
||||
// 1 - iPhone
|
||||
// 2 - iPad
|
||||
// 3 - AppleTV
|
||||
// 4 - Apple Watch
|
||||
when (target.family) {
|
||||
Family.IOS -> addUiDeviceFamilies(1, 2)
|
||||
Family.TVOS -> addUiDeviceFamilies(3)
|
||||
Family.WATCHOS -> addUiDeviceFamilies(4)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
if (target == KonanTarget.IOS_ARM64) {
|
||||
contents.append("""
|
||||
| <key>UIRequiredDeviceCapabilities</key>
|
||||
| <array>
|
||||
| <string>arm64</string>
|
||||
| </array>
|
||||
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
|
||||
if (target == KonanTarget.IOS_ARM32) {
|
||||
contents.append("""
|
||||
| <key>UIRequiredDeviceCapabilities</key>
|
||||
| <array>
|
||||
| <string>armv7</string>
|
||||
| </array>
|
||||
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
|
||||
contents.append("""
|
||||
</dict>
|
||||
</plist>
|
||||
""".trimIndent())
|
||||
|
||||
// TODO: Xcode also add some number of DT* keys.
|
||||
return contents.toString()
|
||||
}
|
||||
|
||||
private fun computeBundleID(
|
||||
bundleName: String,
|
||||
mainPackageGuesser: MainPackageGuesser,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
): String {
|
||||
val deprecatedBundleIdOption = configuration[KonanConfigKeys.BUNDLE_ID]
|
||||
val bundleIdOption = configuration[BinaryOptions.bundleId]
|
||||
if (deprecatedBundleIdOption != null && bundleIdOption != null && deprecatedBundleIdOption != bundleIdOption) {
|
||||
configuration.report(
|
||||
CompilerMessageSeverity.ERROR,
|
||||
"Both the deprecated -Xbundle-id=<id> and the new -Xbinary=bundleId=<id> options supplied with different values: " +
|
||||
"'$deprecatedBundleIdOption' and '$bundleIdOption'. " +
|
||||
"Please use only one of the options or make sure they have the same value."
|
||||
)
|
||||
}
|
||||
deprecatedBundleIdOption?.let { return it } ?: bundleIdOption?.let { return it }
|
||||
|
||||
val mainPackage = mainPackageGuesser.guess(
|
||||
moduleDescriptor,
|
||||
moduleDescriptor.getIncludedLibraryDescriptors(config),
|
||||
moduleDescriptor.getExportedDependencies(config),
|
||||
)
|
||||
val bundleID = mainPackage.child(Name.identifier(bundleName)).asString()
|
||||
|
||||
if (mainPackage.isRoot) {
|
||||
configuration.report(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
"Cannot infer a bundle ID from packages of source files and exported dependencies, " +
|
||||
"use the bundle name instead: $bundleName. " +
|
||||
"Please specify the bundle ID explicitly using the -Xbinary=bundleId=<id> compiler flag."
|
||||
)
|
||||
}
|
||||
return bundleID
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
|
||||
/**
|
||||
* Tries to infer a main package name that can be used
|
||||
* for bundle ID of a framework.
|
||||
*/
|
||||
internal class MainPackageGuesser {
|
||||
fun guess(
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
includedLibraryDescriptors: List<ModuleDescriptor>,
|
||||
exportedDependencies: List<ModuleDescriptor>,
|
||||
): FqName {
|
||||
// Consider exported libraries only if we cannot infer the package from sources or included libs.
|
||||
return guessMainPackage(includedLibraryDescriptors + moduleDescriptor)
|
||||
?: guessMainPackage(exportedDependencies)
|
||||
?: FqName.ROOT
|
||||
}
|
||||
|
||||
private fun guessMainPackage(modules: List<ModuleDescriptor>): FqName? {
|
||||
if (modules.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val allPackages = modules.flatMap {
|
||||
it.getPackageFragments() // Includes also all parent packages, e.g. the root one.
|
||||
}
|
||||
|
||||
val nonEmptyPackages = allPackages
|
||||
.filter { it.getMemberScope().getContributedDescriptors().isNotEmpty() }
|
||||
.map { it.fqName }.distinct()
|
||||
|
||||
return allPackages.map { it.fqName }.distinct()
|
||||
.filter { candidate -> nonEmptyPackages.all { it.isSubpackageOf(candidate) } }
|
||||
// Now there are all common ancestors of non-empty packages. Longest of them is the least common accessor:
|
||||
.maxByOrNull { it.asString().length }
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.objcexport
|
||||
|
||||
/**
|
||||
* Creates a module map for a framework.
|
||||
*
|
||||
* Reference: https://clang.llvm.org/docs/Modules.html
|
||||
*/
|
||||
class ModuleMapBuilder {
|
||||
fun build(frameworkName: String, moduleDependencies: Set<String>): String = buildString {
|
||||
appendLine("framework module $frameworkName {")
|
||||
appendLine(" umbrella header \"$frameworkName.h\"")
|
||||
appendLine()
|
||||
appendLine(" export *")
|
||||
appendLine(" module * { export * }")
|
||||
appendLine()
|
||||
moduleDependencies.forEach {
|
||||
appendLine(" use $it")
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
}
|
||||
+17
-203
@@ -6,24 +6,17 @@
|
||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys.Companion.BUNDLE_ID
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportBlockCodeGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerator
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SourceFile
|
||||
import org.jetbrains.kotlin.konan.exec.Command
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.file.createTempFile
|
||||
import org.jetbrains.kotlin.konan.target.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
|
||||
internal class ObjCExportedInterface(
|
||||
val generatedClasses: Set<ClassDescriptor>,
|
||||
@@ -108,150 +101,22 @@ internal class ObjCExport(
|
||||
}
|
||||
|
||||
private fun produceFrameworkSpecific(headerLines: List<String>) {
|
||||
val framework = File(context.generationState.outputFile)
|
||||
val frameworkContents = when(target.family) {
|
||||
Family.IOS,
|
||||
Family.WATCHOS,
|
||||
Family.TVOS -> framework
|
||||
Family.OSX -> framework.child("Versions/A")
|
||||
else -> error(target)
|
||||
}
|
||||
|
||||
val headers = frameworkContents.child("Headers")
|
||||
|
||||
val frameworkName = framework.name.removeSuffix(".framework")
|
||||
val headerName = frameworkName + ".h"
|
||||
val header = headers.child(headerName)
|
||||
headers.mkdirs()
|
||||
header.writeLines(headerLines)
|
||||
|
||||
val modules = frameworkContents.child("Modules")
|
||||
modules.mkdirs()
|
||||
|
||||
val moduleMap = """
|
||||
|framework module $frameworkName {
|
||||
| umbrella header "$headerName"
|
||||
|
|
||||
| export *
|
||||
| module * { export * }
|
||||
|
|
||||
| use Foundation
|
||||
|}
|
||||
""".trimMargin()
|
||||
|
||||
modules.child("module.modulemap").writeBytes(moduleMap.toByteArray())
|
||||
|
||||
emitInfoPlist(frameworkContents, frameworkName)
|
||||
if (target.family == Family.OSX) {
|
||||
framework.child("Versions/Current").createAsSymlink("A")
|
||||
for (child in listOf(frameworkName, "Headers", "Modules", "Resources")) {
|
||||
framework.child(child).createAsSymlink("Versions/Current/$child")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitInfoPlist(frameworkContents: File, name: String) {
|
||||
val properties = context.config.platform.configurables as AppleConfigurables
|
||||
|
||||
val directory = when (target.family) {
|
||||
Family.IOS,
|
||||
Family.WATCHOS,
|
||||
Family.TVOS -> frameworkContents
|
||||
Family.OSX -> frameworkContents.child("Resources").also { it.mkdirs() }
|
||||
else -> error(target)
|
||||
}
|
||||
|
||||
val file = directory.child("Info.plist")
|
||||
val bundleId = guessBundleID(name)
|
||||
val bundleShortVersionString = context.configuration[BinaryOptions.bundleShortVersionString] ?: "1.0"
|
||||
val bundleVersion = context.configuration[BinaryOptions.bundleVersion] ?: "1"
|
||||
val platform = properties.platformName()
|
||||
val minimumOsVersion = properties.osVersionMin
|
||||
|
||||
val contents = StringBuilder()
|
||||
contents.append("""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$name</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$bundleId</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$name</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$bundleShortVersionString</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>$platform</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$bundleVersion</string>
|
||||
|
||||
""".trimIndent())
|
||||
|
||||
fun addUiDeviceFamilies(vararg values: Int) {
|
||||
val xmlValues = values.joinToString(separator = "\n") {
|
||||
" <integer>$it</integer>"
|
||||
}
|
||||
contents.append("""
|
||||
| <key>MinimumOSVersion</key>
|
||||
| <string>$minimumOsVersion</string>
|
||||
| <key>UIDeviceFamily</key>
|
||||
| <array>
|
||||
|$xmlValues
|
||||
| </array>
|
||||
|
||||
""".trimMargin())
|
||||
}
|
||||
|
||||
// UIDeviceFamily mapping:
|
||||
// 1 - iPhone
|
||||
// 2 - iPad
|
||||
// 3 - AppleTV
|
||||
// 4 - Apple Watch
|
||||
when (target.family) {
|
||||
Family.IOS -> addUiDeviceFamilies(1, 2)
|
||||
Family.TVOS -> addUiDeviceFamilies(3)
|
||||
Family.WATCHOS -> addUiDeviceFamilies(4)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
if (target == KonanTarget.IOS_ARM64) {
|
||||
contents.append("""
|
||||
| <key>UIRequiredDeviceCapabilities</key>
|
||||
| <array>
|
||||
| <string>arm64</string>
|
||||
| </array>
|
||||
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
|
||||
if (target == KonanTarget.IOS_ARM32) {
|
||||
contents.append("""
|
||||
| <key>UIRequiredDeviceCapabilities</key>
|
||||
| <array>
|
||||
| <string>armv7</string>
|
||||
| </array>
|
||||
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
|
||||
contents.append("""
|
||||
</dict>
|
||||
</plist>
|
||||
""".trimIndent())
|
||||
|
||||
// TODO: Xcode also add some number of DT* keys.
|
||||
|
||||
file.writeBytes(contents.toString().toByteArray())
|
||||
val frameworkDirectory = File(context.generationState.outputFile)
|
||||
val frameworkName = frameworkDirectory.name.removeSuffix(".framework")
|
||||
val frameworkBuilder = FrameworkBuilder(
|
||||
context.config,
|
||||
infoPListBuilder = InfoPListBuilder(context.config),
|
||||
moduleMapBuilder = ModuleMapBuilder(),
|
||||
objCHeaderWriter = ObjCHeaderWriter(),
|
||||
mainPackageGuesser = MainPackageGuesser(),
|
||||
)
|
||||
frameworkBuilder.build(
|
||||
context.moduleDescriptor,
|
||||
frameworkDirectory,
|
||||
frameworkName,
|
||||
headerLines,
|
||||
moduleDependencies = emptySet()
|
||||
)
|
||||
}
|
||||
|
||||
// See https://bugs.swift.org/browse/SR-10177
|
||||
@@ -295,55 +160,4 @@ internal class ObjCExport(
|
||||
// In this case resulting framework will likely be unusable due to compile errors when importing it.
|
||||
}
|
||||
}
|
||||
|
||||
private fun guessMainPackage(modules: List<ModuleDescriptor>): FqName? {
|
||||
if (modules.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val allPackages = modules.flatMap {
|
||||
it.getPackageFragments() // Includes also all parent packages, e.g. the root one.
|
||||
}
|
||||
|
||||
val nonEmptyPackages = allPackages
|
||||
.filter { it.getMemberScope().getContributedDescriptors().isNotEmpty() }
|
||||
.map { it.fqName }.distinct()
|
||||
|
||||
return allPackages.map { it.fqName }.distinct()
|
||||
.filter { candidate -> nonEmptyPackages.all { it.isSubpackageOf(candidate) } }
|
||||
// Now there are all common ancestors of non-empty packages. Longest of them is the least common accessor:
|
||||
.maxByOrNull { it.asString().length }
|
||||
}
|
||||
|
||||
private fun guessBundleID(bundleName: String): String {
|
||||
val configuration = context.configuration
|
||||
val deprecatedBundleIdOption = configuration[BUNDLE_ID]
|
||||
val bundleIdOption = configuration[BinaryOptions.bundleId]
|
||||
if (deprecatedBundleIdOption != null && bundleIdOption != null && deprecatedBundleIdOption != bundleIdOption) {
|
||||
configuration.report(
|
||||
CompilerMessageSeverity.ERROR,
|
||||
"Both the deprecated -Xbundle-id=<id> and the new -Xbinary=bundleId=<id> options supplied with different values: " +
|
||||
"'$deprecatedBundleIdOption' and '$bundleIdOption'. " +
|
||||
"Please use only one of the options or make sure they have the same value."
|
||||
)
|
||||
}
|
||||
deprecatedBundleIdOption?.let { return it } ?: bundleIdOption?.let { return it }
|
||||
|
||||
// Consider exported libraries only if we cannot infer the package from sources or included libs.
|
||||
val mainPackage = guessMainPackage(context.getIncludedLibraryDescriptors() + context.moduleDescriptor)
|
||||
?: guessMainPackage(context.getExportedDependencies())
|
||||
?: FqName.ROOT
|
||||
|
||||
val bundleID = mainPackage.child(Name.identifier(bundleName)).asString()
|
||||
|
||||
if (mainPackage.isRoot) {
|
||||
configuration.report(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
"Cannot infer a bundle ID from packages of source files and exported dependencies, " +
|
||||
"use the bundle name instead: $bundleName. " +
|
||||
"Please specify the bundle ID explicitly using the -Xbinary=bundleId=<id> compiler flag."
|
||||
)
|
||||
}
|
||||
return bundleID
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
|
||||
// For now, the object is pretty dumb.
|
||||
// Later it will accept an object with ObjC declarations instead of lines.
|
||||
class ObjCHeaderWriter {
|
||||
fun write(
|
||||
headerName: String,
|
||||
headerLines: List<String>,
|
||||
headersDirectory: File,
|
||||
) {
|
||||
headersDirectory.child(headerName).writeLines(headerLines)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user