Implement Info.plist generation for produced frameworks
This commit is contained in:
committed by
SvyatoslavScherbina
parent
954caf836a
commit
5ade7505b3
+1
-1
@@ -107,7 +107,7 @@ internal open class MacOSBasedPlatform(distribution: Distribution)
|
|||||||
open val osVersionMin by lazy {
|
open val osVersionMin by lazy {
|
||||||
listOf(
|
listOf(
|
||||||
propertyTargetString("osVersionMinFlagLd"),
|
propertyTargetString("osVersionMinFlagLd"),
|
||||||
propertyTargetString("osVersionMin") + ".0")
|
properties.osVersionMin!! + ".0")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun filterStaticLibraries(binaries: List<String>)
|
override fun filterStaticLibraries(binaries: List<String>)
|
||||||
|
|||||||
+18
-4
@@ -35,10 +35,7 @@ import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
|
|||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
@@ -322,3 +319,20 @@ fun AnnotationDescriptor.getStringValueOrNull(name: String): String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun AnnotationDescriptor.getStringValue(name: String): String = this.getStringValueOrNull(name)!!
|
fun AnnotationDescriptor.getStringValue(name: String): String = this.getStringValueOrNull(name)!!
|
||||||
|
|
||||||
|
private fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> {
|
||||||
|
val result = mutableSetOf<FqName>()
|
||||||
|
|
||||||
|
fun getSubPackages(fqName: FqName) {
|
||||||
|
result.add(fqName)
|
||||||
|
module.getSubPackagesOf(fqName) { true }.forEach { getSubPackages(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubPackages(FqName.ROOT)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ModuleDescriptor.getPackageFragments(): List<PackageFragmentDescriptor> =
|
||||||
|
getPackagesFqNames(this).flatMap {
|
||||||
|
getPackage(it).fragments.filter { it.module == this }
|
||||||
|
}
|
||||||
|
|||||||
+110
@@ -17,12 +17,21 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
|
import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerator
|
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerator
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.konan.file.File
|
import org.jetbrains.kotlin.konan.file.File
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||||
|
|
||||||
internal class ObjCExport(val context: Context) {
|
internal class ObjCExport(val context: Context) {
|
||||||
|
|
||||||
|
private val target get() = context.config.targetManager.target
|
||||||
|
|
||||||
internal fun produceObjCFramework() {
|
internal fun produceObjCFramework() {
|
||||||
if (context.config.produce != CompilerOutputKind.FRAMEWORK) return
|
if (context.config.produce != CompilerOutputKind.FRAMEWORK) return
|
||||||
|
|
||||||
@@ -55,7 +64,108 @@ internal class ObjCExport(val context: Context) {
|
|||||||
|
|
||||||
modules.child("module.modulemap").writeBytes(moduleMap.toByteArray())
|
modules.child("module.modulemap").writeBytes(moduleMap.toByteArray())
|
||||||
|
|
||||||
|
emitInfoPlist(framework, frameworkName)
|
||||||
|
|
||||||
val objCCodeGenerator = ObjCExportCodeGenerator(CodeGenerator(context), namer, mapper)
|
val objCCodeGenerator = ObjCExportCodeGenerator(CodeGenerator(context), namer, mapper)
|
||||||
objCCodeGenerator.emitRtti(headerGenerator.generatedClasses, headerGenerator.topLevel)
|
objCCodeGenerator.emitRtti(headerGenerator.generatedClasses, headerGenerator.topLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun emitInfoPlist(framework: File, name: String) {
|
||||||
|
val directory = when (target) {
|
||||||
|
KonanTarget.IPHONE,
|
||||||
|
KonanTarget.IPHONE_SIM -> framework
|
||||||
|
KonanTarget.MACBOOK -> framework.child("Resources").also { it.mkdirs() }
|
||||||
|
else -> error(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = directory.child("Info.plist")
|
||||||
|
val bundleExecutable = name
|
||||||
|
val pkg = context.moduleDescriptor.guessMainPackage() // TODO: consider showing warning if it is root.
|
||||||
|
val bundleId = pkg.child(Name.identifier(name)).asString()
|
||||||
|
|
||||||
|
val platform = when (target) {
|
||||||
|
KonanTarget.IPHONE -> "iPhoneOS"
|
||||||
|
KonanTarget.IPHONE_SIM -> "iPhoneSimulator"
|
||||||
|
KonanTarget.MACBOOK -> "MacOSX"
|
||||||
|
else -> error(target)
|
||||||
|
}
|
||||||
|
val minimumOsVersion = context.config.distribution.targetProperties.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>$bundleExecutable</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>1.0</string>
|
||||||
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
|
<array>
|
||||||
|
<string>$platform</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
|
||||||
|
""".trimIndent())
|
||||||
|
|
||||||
|
|
||||||
|
contents.append(when (target) {
|
||||||
|
KonanTarget.IPHONE,
|
||||||
|
KonanTarget.IPHONE_SIM -> """
|
||||||
|
| <key>MinimumOSVersion</key>
|
||||||
|
| <string>$minimumOsVersion</string>
|
||||||
|
| <key>UIDeviceFamily</key>
|
||||||
|
| <array>
|
||||||
|
| <integer>1</integer>
|
||||||
|
| <integer>2</integer>
|
||||||
|
| </array>
|
||||||
|
|
||||||
|
""".trimMargin()
|
||||||
|
KonanTarget.MACBOOK -> ""
|
||||||
|
else -> error(target)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (target == KonanTarget.IPHONE) {
|
||||||
|
contents.append("""
|
||||||
|
| <key>UIRequiredDeviceCapabilities</key>
|
||||||
|
| <array>
|
||||||
|
| <string>arm64</string>
|
||||||
|
| </array>
|
||||||
|
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
contents.append("""
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
""".trimIndent())
|
||||||
|
|
||||||
|
// TODO: Xcode also add some number of DT* keys.
|
||||||
|
|
||||||
|
file.writeBytes(contents.toString().toByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ModuleDescriptor.guessMainPackage(): FqName {
|
||||||
|
val allPackages = this.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:
|
||||||
|
.maxBy { it.asString().length }!!
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-17
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||||
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
||||||
@@ -568,20 +569,3 @@ private inline fun buildStub(block: StubBuilder.() -> Unit) = StubBuilder().let
|
|||||||
private inline fun MutableCollection<Stub>.addBuiltBy(block: StubBuilder.() -> Unit) {
|
private inline fun MutableCollection<Stub>.addBuiltBy(block: StubBuilder.() -> Unit) {
|
||||||
this.add(buildStub(block))
|
this.add(buildStub(block))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> {
|
|
||||||
val result = mutableSetOf<FqName>()
|
|
||||||
|
|
||||||
fun getSubPackages(fqName: FqName) {
|
|
||||||
result.add(fqName)
|
|
||||||
module.getSubPackagesOf(fqName) { true }.forEach { getSubPackages(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
getSubPackages(FqName.ROOT)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ModuleDescriptor.getPackageFragments(): List<PackageFragmentDescriptor> =
|
|
||||||
getPackagesFqNames(this).flatMap {
|
|
||||||
getPackage(it).fragments.filter { it.module == this }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -73,4 +73,6 @@ class KonanProperties(val target: KonanTarget, val properties: Properties, val b
|
|||||||
// Use (equal) llvmHome fow now.
|
// Use (equal) llvmHome fow now.
|
||||||
return targetString("llvmHome")
|
return targetString("llvmHome")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val osVersionMin: String? get() = targetString("osVersionMin")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user