diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index 085bc63ce08..7f0ba61bca9 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -107,7 +107,7 @@ internal open class MacOSBasedPlatform(distribution: Distribution) open val osVersionMin by lazy { listOf( propertyTargetString("osVersionMinFlagLd"), - propertyTargetString("osVersionMin") + ".0") + properties.osVersionMin!! + ".0") } override fun filterStaticLibraries(binaries: List) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 5df1a733c4a..9ec2c4d3d0f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -35,10 +35,7 @@ import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.OverridingUtil -import org.jetbrains.kotlin.resolve.descriptorUtil.classId -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.descriptorUtil.* import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType 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)!! + +private fun getPackagesFqNames(module: ModuleDescriptor): Set { + val result = mutableSetOf() + + fun getSubPackages(fqName: FqName) { + result.add(fqName) + module.getSubPackagesOf(fqName) { true }.forEach { getSubPackages(it) } + } + + getSubPackages(FqName.ROOT) + return result +} + +fun ModuleDescriptor.getPackageFragments(): List = + getPackagesFqNames(this).flatMap { + getPackage(it).fragments.filter { it.module == this } + } 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 573606cf9e2..91717bb9173 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 @@ -17,12 +17,21 @@ package org.jetbrains.kotlin.backend.konan.objcexport 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.objcexport.ObjCExportCodeGenerator +import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.konan.file.File 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) { + + private val target get() = context.config.targetManager.target + internal fun produceObjCFramework() { if (context.config.produce != CompilerOutputKind.FRAMEWORK) return @@ -55,7 +64,108 @@ internal class ObjCExport(val context: Context) { modules.child("module.modulemap").writeBytes(moduleMap.toByteArray()) + emitInfoPlist(framework, frameworkName) + val objCCodeGenerator = ObjCExportCodeGenerator(CodeGenerator(context), namer, mapper) 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(""" + + + + + CFBundleExecutable + $bundleExecutable + CFBundleIdentifier + $bundleId + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $name + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + + $platform + + CFBundleVersion + 1 + + """.trimIndent()) + + + contents.append(when (target) { + KonanTarget.IPHONE, + KonanTarget.IPHONE_SIM -> """ + | MinimumOSVersion + | $minimumOsVersion + | UIDeviceFamily + | + | 1 + | 2 + | + + """.trimMargin() + KonanTarget.MACBOOK -> "" + else -> error(target) + }) + + if (target == KonanTarget.IPHONE) { + contents.append(""" + | UIRequiredDeviceCapabilities + | + | arm64 + | + + """.trimMargin() + ) + } + + contents.append(""" + + + """.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 }!! } 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 7d2b1015dc8..50756873f30 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 @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport 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.builtins.getReceiverTypeFromFunctionType import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType @@ -568,20 +569,3 @@ private inline fun buildStub(block: StubBuilder.() -> Unit) = StubBuilder().let private inline fun MutableCollection.addBuiltBy(block: StubBuilder.() -> Unit) { this.add(buildStub(block)) } - -private fun getPackagesFqNames(module: ModuleDescriptor): Set { - val result = mutableSetOf() - - fun getSubPackages(fqName: FqName) { - result.add(fqName) - module.getSubPackagesOf(fqName) { true }.forEach { getSubPackages(it) } - } - - getSubPackages(FqName.ROOT) - return result -} - -private fun ModuleDescriptor.getPackageFragments(): List = - getPackagesFqNames(this).flatMap { - getPackage(it).fragments.filter { it.module == this } - } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanProperties.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanProperties.kt index 8267920745b..2df7a8aa47d 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanProperties.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanProperties.kt @@ -73,4 +73,6 @@ class KonanProperties(val target: KonanTarget, val properties: Properties, val b // Use (equal) llvmHome fow now. return targetString("llvmHome") } + + val osVersionMin: String? get() = targetString("osVersionMin") }