From 237b7ef182adc70b4b9f010cdbfc8fa6dfd2d2e4 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Wed, 15 May 2019 19:08:19 +0700 Subject: [PATCH] Preliminary tvOS support. --- .../kotlin/native/interop/gen/ObjCStubs.kt | 15 +++-- .../kotlin/backend/konan/CStubsManager.kt | 4 +- .../jetbrains/kotlin/backend/konan/Linker.kt | 5 +- .../backend/konan/OptimizationPipeline.kt | 4 +- .../kotlin/backend/konan/cgen/CBridgeGen.kt | 5 +- .../backend/konan/llvm/CodeGenerator.kt | 4 +- .../backend/konan/objcexport/ObjCExport.kt | 52 ++++++++++------ .../org/jetbrains/kotlin/ExecutorService.kt | 18 +++++- .../org/jetbrains/kotlin/FrameworkTest.kt | 46 +++++++++++---- .../org/jetbrains/kotlin/PlatformInfo.kt | 4 +- .../main/kotlin/org/jetbrains/kotlin/Utils.kt | 2 + .../org/jetbrains/kotlin/XcRunRuntimeUtils.kt | 59 +++++++++++++++++++ konan/konan.properties | 54 +++++++++++++++++ .../jetbrains/kotlin/konan/target/Apple.kt | 2 + .../kotlin/konan/target/ClangArgs.kt | 40 ++++++++++--- .../kotlin/konan/target/ConfigurablesImpl.kt | 5 +- .../kotlin/konan/target/KonanProperties.kt | 14 +---- .../jetbrains/kotlin/konan/target/Linker.kt | 36 +++++++---- .../jetbrains/kotlin/konan/target/Platform.kt | 4 +- .../jetbrains/kotlin/konan/target/Xcode.kt | 8 +++ .../experimental/internal/OutputKind.kt | 2 +- .../gradle/plugin/konan/KonanCompileConfig.kt | 2 +- tools/scripts/update_xcode.sh | 4 +- 23 files changed, 296 insertions(+), 93 deletions(-) create mode 100644 build-tools/src/main/kotlin/org/jetbrains/kotlin/XcRunRuntimeUtils.kt diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt index 6ae7a9cb46f..7b50ad92c3e 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt @@ -248,19 +248,24 @@ internal val ObjCContainer.classOrProtocol: ObjCClassOrProtocol internal fun Type.isStret(target: KonanTarget): Boolean { val unwrappedType = this.unwrapTypedefs() return when (target) { - KonanTarget.IOS_ARM64 -> + KonanTarget.IOS_ARM64, + KonanTarget.TVOS_ARM64 -> false // On aarch64 stret is never the case, since an implicit argument gets passed on x8. - KonanTarget.IOS_X64, KonanTarget.MACOS_X64 -> when (unwrappedType) { + KonanTarget.IOS_X64, + KonanTarget.MACOS_X64, + KonanTarget.WATCHOS_X64, + KonanTarget.TVOS_X64 -> when (unwrappedType) { is RecordType -> unwrappedType.decl.def!!.size > 16 || this.hasUnalignedMembers() else -> false } - KonanTarget.IOS_ARM32 -> { - when (unwrappedType) { + + KonanTarget.IOS_ARM32 -> when (unwrappedType) { is RecordType -> !this.isIntegerLikeType() else -> false } - } + + KonanTarget.WATCHOS_ARM64 -> TODO("") else -> error(target) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CStubsManager.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CStubsManager.kt index 8df89b2f131..589fd0ff31f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CStubsManager.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CStubsManager.kt @@ -21,8 +21,8 @@ class CStubsManager(private val target: KonanTarget) { if (stubs.isEmpty()) return null val compilerOptions = mutableListOf() - val sourceFileExtension = when (target.family) { - Family.OSX, Family.IOS -> { + val sourceFileExtension = when { + target.family.isAppleFamily -> { compilerOptions += "-fobjc-arc" ".m" // TODO: consider managing C and Objective-C stubs separately. } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt index ac88e124f21..276308640f7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.konan.KonanExternalToolFailure import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.LinkerOutputKind internal fun determineLinkerOutput(context: Context): LinkerOutputKind = @@ -66,7 +67,9 @@ internal class Linker(val context: Context) { val framework = File(context.config.outputFile) val dylibName = framework.name.removeSuffix(".framework") val dylibRelativePath = when (target.family) { - Family.IOS -> dylibName + Family.IOS, + Family.TVOS, + Family.WATCHOS -> dylibName Family.OSX -> "Versions/A/$dylibName" else -> error(target) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt index 76b51a98cd8..22502cd76bf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt @@ -51,6 +51,8 @@ private class LlvmPipelineConfiguration(context: Context) { KonanTarget.IOS_ARM32 -> "generic" KonanTarget.IOS_ARM64 -> "cyclone" KonanTarget.IOS_X64 -> "core2" + KonanTarget.TVOS_ARM64 -> "cyclone" + KonanTarget.TVOS_X64 -> "core2" KonanTarget.LINUX_X64 -> "x86-64" KonanTarget.MINGW_X86 -> "pentium4" KonanTarget.MINGW_X64 -> "x86-64" @@ -67,8 +69,6 @@ private class LlvmPipelineConfiguration(context: Context) { KonanTarget.WATCHOS_ARM32 -> TODO("implement me") KonanTarget.WATCHOS_X64 -> TODO("implement me") KonanTarget.WATCHOS_X86 -> TODO("implement me") - KonanTarget.TVOS_ARM64 -> TODO("implement me") - KonanTarget.TVOS_X64 -> TODO("implement me") KonanTarget.WASM32, is KonanTarget.ZEPHYR -> error("There is no support for ${target.name} target yet.") } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt index 3a9a1bfd8b4..5f6af44a994 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt @@ -634,8 +634,9 @@ private fun KotlinStubs.getNamedCStructType(kotlinClass: IrClass): CType? { } // TODO: rework Boolean support. +// TODO: What should be used on watchOS? private fun cBoolType(target: KonanTarget): CType? = when (target.family) { - Family.IOS -> CTypes.C99Bool + Family.IOS, Family.TVOS -> CTypes.C99Bool else -> CTypes.signedChar } @@ -809,7 +810,7 @@ private fun KotlinStubs.mapType( } private fun KotlinStubs.isObjCReferenceType(type: IrType): Boolean { - if (target.family != Family.OSX && target.family != Family.IOS) return false + if (!target.family.isAppleFamily) return false // Handle the same types as produced by [objCPointerMirror] in Interop/StubGenerator/.../Mappings.kt. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 88648f8b91a..487335823ec 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -90,8 +90,8 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { else LLVMCreateLocation(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.column, locationInfo.scope) - val objCDataGenerator = when (context.config.target.family) { - Family.IOS, Family.OSX -> ObjCDataGenerator(this) + val objCDataGenerator = when { + context.config.target.family.isAppleFamily -> ObjCDataGenerator(this) else -> null } 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 fca07ed1e01..94d97820672 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 @@ -19,10 +19,7 @@ import org.jetbrains.kotlin.ir.util.SymbolTable 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.AppleConfigurables -import org.jetbrains.kotlin.konan.target.CompilerOutputKind -import org.jetbrains.kotlin.konan.target.Family -import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.isSubpackageOf @@ -43,7 +40,7 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { private val codeSpec = exportedInterface?.createCodeSpec(symbolTable) private fun produceInterface(): ObjCExportedInterface? { - if (target.family != Family.IOS && target.family != Family.OSX) return null + if (!target.family.isAppleFamily) return null if (!context.config.produce.isNativeBinary) return null // TODO: emit RTTI to the same modules as classes belong to. @@ -70,7 +67,7 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { } internal fun generate(codegen: CodeGenerator) { - if (target.family != Family.IOS && target.family != Family.OSX) return + if (!target.family.isAppleFamily) return if (!context.config.produce.isNativeBinary) return // TODO: emit RTTI to the same modules as classes belong to. @@ -99,7 +96,9 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { private fun produceFrameworkSpecific(headerLines: List) { val framework = File(context.config.outputFile) val frameworkContents = when(target.family) { - Family.IOS -> framework + Family.IOS, + Family.WATCHOS, + Family.TVOS -> framework Family.OSX -> framework.child("Versions/A") else -> error(target) } @@ -137,9 +136,11 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { } private fun emitInfoPlist(frameworkContents: File, name: String) { - val directory = when { - target.family == Family.IOS -> frameworkContents - target == KonanTarget.MACOS_X64 -> frameworkContents.child("Resources").also { it.mkdirs() } + val directory = when (target.family) { + Family.IOS, + Family.WATCHOS, + Family.TVOS -> frameworkContents + Family.OSX -> frameworkContents.child("Resources").also { it.mkdirs() } else -> error(target) } @@ -150,6 +151,8 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { val platform = when (target) { KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64 -> "iPhoneOS" KonanTarget.IOS_X64 -> "iPhoneSimulator" + KonanTarget.TVOS_ARM64 -> "AppleTVOS" + KonanTarget.TVOS_X64 -> "AppleTVSimulator" KonanTarget.MACOS_X64 -> "MacOSX" else -> error(target) } @@ -183,21 +186,32 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { """.trimIndent()) - - contents.append(when (target.family) { - Family.IOS -> """ + fun addUiDeviceFamilies(vararg values: Int) { + val xmlValues = values.joinToString(separator = "\n") { + " $it" + } + contents.append(""" | MinimumOSVersion | $minimumOsVersion | UIDeviceFamily | - | 1 - | 2 + |$xmlValues | - """.trimMargin() - Family.OSX -> "" - else -> error(target) - }) + """.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(""" diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/ExecutorService.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/ExecutorService.kt index 195eab7d014..de77726ccd8 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/ExecutorService.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/ExecutorService.kt @@ -85,6 +85,7 @@ fun create(project: Project): ExecutorService { } KonanTarget.IOS_X64 -> simulator(project) + KonanTarget.TVOS_X64 -> simulator(project) else -> { if (project.hasProperty("remote")) sshExecutor(project) @@ -213,8 +214,14 @@ fun localExecutor(project: Project) = { a: Action -> project.exec(a */ private fun simulator(project: Project) : ExecutorService = object : ExecutorService { + private val target = project.testTarget + private val simctl by lazy { - val sdk = Xcode.current.iphonesimulatorSdk + val sdk = when (target) { + KonanTarget.TVOS_X64 -> Xcode.current.appletvsimulatorSdk + KonanTarget.IOS_X64 -> Xcode.current.iphonesimulatorSdk + else -> error("Unexpected simulation target: $target") + } val out = ByteArrayOutputStream() val result = project.exec { it.commandLine("/usr/bin/xcrun", "--find", "simctl", "--sdk", sdk) @@ -224,11 +231,16 @@ private fun simulator(project: Project) : ExecutorService = object : ExecutorSer out.toString("UTF-8").trim() } - private val iosDevice = project.findProperty("iosDevice")?.toString() ?: "iPhone 6" + private val device = project.findProperty("iosDevice")?.toString() ?: when (target) { + KonanTarget.TVOS_X64 -> "Apple TV 4K" + KonanTarget.IOS_X64 -> "iPhone 6" + else -> error("Unexpected simulation target: $target") + } override fun execute(action: Action): ExecResult? = project.exec { execSpec -> action.execute(execSpec) - with(execSpec) { commandLine = listOf(simctl, "spawn", iosDevice, executable) + args } + // Starting Xcode 11 `simctl spawn` requires explicit `--standalone` flag. + with(execSpec) { commandLine = listOf(simctl, "spawn", "--standalone", device, executable) + args } } } diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt index 78e18be7e7f..6cb341d10fa 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt @@ -86,31 +86,50 @@ open class FrameworkTest : DefaultTask() { runTest(testExecutable) } - private fun runTest(testExecutable: Path) { + /** + * Returns path to directory that contains `libswiftCore.dylib` for the current + * test target. + */ + private fun getSwiftLibsPathForTestTarget(): String { val target = project.testTarget val platform = project.platformManager.platform(target) val configs = platform.configurables as AppleConfigurables val swiftPlatform = when (target) { KonanTarget.IOS_X64 -> "iphonesimulator" KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64 -> "iphoneos" + KonanTarget.TVOS_X64 -> "appletvsimulator" + KonanTarget.TVOS_ARM64 -> "appletvos" KonanTarget.MACOS_X64 -> "macosx" else -> throw IllegalStateException("Test target $target is not supported") } - val libraryPath = configs.absoluteTargetToolchain + "/usr/lib/swift/$swiftPlatform" - val executor = (project.convention.plugins["executor"] as? ExecutorService) - ?: throw RuntimeException("Executor wasn't found") + return when (target) { + KonanTarget.TVOS_X64, + KonanTarget.IOS_X64, + KonanTarget.WATCHOS_X64 -> Xcode.current.getLatestSimulatorRuntimeFor(target, configs.osVersionMin)?.let { + "${it.bundlePath}/Contents/Resources/RuntimeRoot/usr/lib/swift" + } ?: error("Simulator runtime for $target ${configs.osVersionMin} is not available") + else -> configs.absoluteTargetToolchain + "/usr/lib/swift/$swiftPlatform" + } + } + + private fun buildEnvironment(): Map { + val target = project.testTarget // Hopefully, lexicographical comparison will work. val newMacos = System.getProperty("os.version").compareTo("10.14.4") >= 0 - val dyldLibraryPathKey = if (target == KonanTarget.IOS_X64) { - "SIMCTL_CHILD_DYLD_LIBRARY_PATH" - } else { - "DYLD_LIBRARY_PATH" + val dyldLibraryPathKey = when (target) { + KonanTarget.IOS_X64, KonanTarget.TVOS_X64 -> "SIMCTL_CHILD_DYLD_LIBRARY_PATH" + else -> "DYLD_LIBRARY_PATH" } - val environment = if (newMacos) emptyMap() else mapOf( - dyldLibraryPathKey to libraryPath + return if (newMacos && target == KonanTarget.MACOS_X64) emptyMap() else mapOf( + dyldLibraryPathKey to getSwiftLibsPathForTestTarget() ) + } + + private fun runTest(testExecutable: Path) { + val executor = (project.convention.plugins["executor"] as? ExecutorService) + ?: throw RuntimeException("Executor wasn't found") val (stdOut, stdErr, exitCode) = runProcess( - executor = executor.add(Action { it.environment = environment })::execute, + executor = executor.add(Action { it.environment = buildEnvironment() })::execute, executable = testExecutable.toString()) println(""" @@ -131,9 +150,10 @@ open class FrameworkTest : DefaultTask() { val bitcodeBuildTool = "${configurables.absoluteAdditionalToolsDir}/bin/bitcode-build-tool" val ldPath = "${configurables.absoluteTargetToolchain}/usr/bin/ld" val sdk = when (testTarget) { - KonanTarget.IOS_X64 -> return // bitcode-build-tool doesn't support iPhone Simulator. + KonanTarget.IOS_X64, KonanTarget.TVOS_X64 -> return // bitcode-build-tool doesn't support simulators. KonanTarget.IOS_ARM64, KonanTarget.IOS_ARM32 -> Xcode.current.iphoneosSdk KonanTarget.MACOS_X64 -> Xcode.current.macosxSdk + KonanTarget.TVOS_ARM64 -> Xcode.current.appletvosSdk else -> error("Cannot validate bitcode for test target $testTarget") } @@ -147,4 +167,4 @@ open class FrameworkTest : DefaultTask() { """.trimMargin() } } -} +} \ No newline at end of file diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/PlatformInfo.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/PlatformInfo.kt index 54e134c2c3c..67a5e16ee57 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/PlatformInfo.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/PlatformInfo.kt @@ -15,12 +15,12 @@ object PlatformInfo { @JvmStatic fun isAppleTarget(project: Project): Boolean { val target = getTarget(project) - return target.family == Family.IOS || target.family == Family.OSX + return target.family.isAppleFamily } @JvmStatic fun isAppleTarget(target: KonanTarget): Boolean { - return target.family == Family.IOS || target.family == Family.OSX + return target.family.isAppleFamily } @JvmStatic diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/Utils.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/Utils.kt index e203d232de6..f8526d75d2b 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/Utils.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/Utils.kt @@ -215,6 +215,8 @@ fun compileSwift(project: Project, target: KonanTarget, sources: List, o val swiftTarget = when (target) { KonanTarget.IOS_X64 -> "x86_64-apple-ios" + configs.osVersionMin KonanTarget.IOS_ARM64 -> "arm64_64-apple-ios" + configs.osVersionMin + KonanTarget.TVOS_X64 -> "x86_64-apple-tvos" + configs.osVersionMin + KonanTarget.TVOS_ARM64 -> "arm64_64-apple-tvos" + configs.osVersionMin KonanTarget.MACOS_X64 -> "x86_64-apple-macosx" + configs.osVersionMin else -> throw IllegalStateException("Test target $target is not supported") } diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/XcRunRuntimeUtils.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/XcRunRuntimeUtils.kt new file mode 100644 index 00000000000..e565735d156 --- /dev/null +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/XcRunRuntimeUtils.kt @@ -0,0 +1,59 @@ +package org.jetbrains.kotlin + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.target.Xcode +import kotlin.math.min + +/** + * Compares two strings assuming that both are representing numeric version strings. + * Examples of numeric version strings: "12.4.1.2", "9", "0.5". + */ +private fun compareStringsAsVersions(version1: String, version2: String): Int { + val version1 = version1.split('.').map { it.toInt() } + val version2 = version2.split('.').map { it.toInt() } + val minimalLength = min(version1.size, version2.size) + for (index in 0 until minimalLength) { + if (version1[index] < version2[index]) return -1 + if (version1[index] > version2[index]) return 1 + } + return version1.size.compareTo(version2.size) +} + +/** + * Returns parsed output of `xcrun simctl list runtimes -j`. + */ +private fun Xcode.getSimulatorRuntimeDescriptors(): List = + Json.nonstrict.parse(ListRuntimesReport.serializer(), this.simulatorRuntimes).runtimes + +/** + * Returns first available simulator runtime for [target] with at least [osMinVersion] OS version. + * */ +fun Xcode.getLatestSimulatorRuntimeFor(target: KonanTarget, osMinVersion: String): SimulatorRuntimeDescriptor? { + val osName = when (target) { + KonanTarget.IOS_X64 -> "iOS" + KonanTarget.WATCHOS_X64 -> "watchOS" + KonanTarget.TVOS_X64 -> "tvOS" + else -> error("Unexpected simulator target: $target") + } + return getSimulatorRuntimeDescriptors().firstOrNull { + it.isAvailable && it.name.startsWith(osName) && compareStringsAsVersions(it.version, osMinVersion) >= 0 + } +} + +// Result of `xcrun simctl list runtimes -j`. +@Serializable +data class ListRuntimesReport( + val runtimes: List +) + +@Serializable +data class SimulatorRuntimeDescriptor( + val version: String, + val bundlePath: String, + val isAvailable: Boolean, + val name: String, + val identifier: String, + val buildversion: String +) diff --git a/konan/konan.properties b/konan/konan.properties index 6a8839b7d5a..10ee660506d 100644 --- a/konan/konan.properties +++ b/konan/konan.properties @@ -151,6 +151,60 @@ osVersionMinFlagLd.ios_x64 = -ios_simulator_version_min osVersionMinFlagClang.ios_x64 = -mios-simulator-version-min osVersionMin.ios_x64 = 9.0 +# Apple's tvOS simulator. +targetToolchain.macos_x64-tvos_x64 = target-toolchain-9-macos_x64 +dependencies.macos_x64-tvos_x64 = \ + libffi-3.2.1-3-darwin-macos \ + clang-llvm-6.0.1-darwin-macos + +target-sysroot-9-tvos_x64.default = \ + remote:internal + +arch.tvos_x64 = x86_64 +entrySelector.tvos_x64 = -alias _Konan_main _main +targetSysRoot.tvos_x64 = target-sysroot-9-tvos_x64 + +clangFlags.tvos_x64 = -cc1 -emit-obj -disable-llvm-passes -x ir +clangNooptFlags.tvos_x64 = -O1 +clangOptFlags.tvos_x64 = -O3 +clangDebugFlags.tvos_x64 = -O0 +clangDynamicFlags.tvos_x64 = + +linkerKonanFlags.tvos_x64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 11.2 +linkerOptimizationFlags.tvos_x64 = -dead_strip +linkerNoDebugFlags.tvos_x64 = -S +linkerDynamicFlags.tvos_x64 = -dylib +osVersionMinFlagLd.tvos_x64 = -tvos_simulator_version_min +osVersionMinFlagClang.tvos_x64 = -mtvos-simulator-version-min +osVersionMin.tvos_x64 = 9.0 + +# Apple's 64-bit tvOS. +targetToolchain.macos_x64-tvos_arm64 = target-toolchain-9-macos_x64 +dependencies.macos_x64-tvos_arm64 = \ + libffi-3.2.1-3-darwin-macos \ + clang-llvm-6.0.1-darwin-macos + +target-sysroot-9-tvos_arm64.default = \ + remote:internal + +arch.tvos_arm64 = arm64 +entrySelector.tvos_arm64 = -alias _Konan_main _main +targetSysRoot.tvos_arm64 = target-sysroot-9-tvos_arm64 + +clangFlags.tvos_arm64 = -cc1 -emit-obj -disable-llvm-passes -x ir +clangNooptFlags.tvos_arm64 = -O1 +clangOptFlags.tvos_arm64 = -O3 +clangDebugFlags.tvos_arm64 = -O0 +clangDynamicFlags.tvos_arm64 = + +linkerNoDebugFlags.tvos_arm64 = -S +linkerDynamicFlags.tvos_arm64 = -dylib +linkerKonanFlags.tvos_arm64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 11.2 +linkerOptimizationFlags.tvos_arm64 = -dead_strip +osVersionMinFlagLd.tvos_arm64 = -tvos_version_min +osVersionMinFlagClang.tvos_arm64 = -mtvos-version-min +osVersionMin.tvos_arm64 = 9.0 + # Linux x86-64. llvmHome.linux_x64 = clang-llvm-6.0.1-linux-x86-64 libffiDir.linux_x64 = libffi-3.2.1-2-linux-x86-64 diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Apple.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Apple.kt index d1868ea733e..220dd126db0 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Apple.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Apple.kt @@ -35,6 +35,8 @@ class AppleConfigurablesImpl( KonanTarget.MACOS_X64 -> xcodePartsProvider.xcode.macosxSdk KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64 -> xcodePartsProvider.xcode.iphoneosSdk KonanTarget.IOS_X64 -> xcodePartsProvider.xcode.iphonesimulatorSdk + KonanTarget.TVOS_ARM64 -> xcodePartsProvider.xcode.appletvosSdk + KonanTarget.TVOS_X64 -> xcodePartsProvider.xcode.appletvsimulatorSdk else -> error(target) } XcodePartsProvider.InternalServer -> absolute(sdkDependency) diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt index 1f8d6bf8676..4ac105d9053 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt @@ -37,6 +37,12 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con configurables.targetArg else null + private val osVersionMin: String + get() { + require(configurables is AppleConfigurables) + return configurables.osVersionMin + } + val specificClangArgs: List get() { val result = when (target) { @@ -74,16 +80,22 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con listOf("-target", targetArg!!, "--sysroot=$absoluteTargetSysRoot", "-Xclang", "-flto-visibility-public-std") KonanTarget.MACOS_X64 -> - listOf("--sysroot=$absoluteTargetSysRoot", "-mmacosx-version-min=10.11") + listOf("--sysroot=$absoluteTargetSysRoot", "-mmacosx-version-min=$osVersionMin") KonanTarget.IOS_ARM32 -> - listOf("-stdlib=libc++", "-arch", "armv7", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=9.0.0") + listOf("-stdlib=libc++", "-arch", "armv7", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=$osVersionMin") KonanTarget.IOS_ARM64 -> - listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=9.0.0") + listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=$osVersionMin") + + KonanTarget.TVOS_ARM64 -> + listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", absoluteTargetSysRoot, "-mtvos-version-min=$osVersionMin") + + KonanTarget.TVOS_X64 -> + listOf("-stdlib=libc++", "-isysroot", absoluteTargetSysRoot, "-mtvos-simulator-version-min=$osVersionMin") KonanTarget.IOS_X64 -> - listOf("-stdlib=libc++", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=9.0.0") + listOf("-stdlib=libc++", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=$osVersionMin") KonanTarget.ANDROID_ARM32, KonanTarget.ANDROID_ARM64, KonanTarget.ANDROID_X86, KonanTarget.ANDROID_X64 -> { @@ -118,8 +130,6 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con KonanTarget.WATCHOS_ARM32 -> TODO("implement me") KonanTarget.WATCHOS_X64 -> TODO("implement me") KonanTarget.WATCHOS_X86 -> TODO("implement me") - KonanTarget.TVOS_ARM64 -> TODO("implement me") - KonanTarget.TVOS_X64 -> TODO("implement me") is KonanTarget.ZEPHYR -> listOf("-target", targetArg!!, @@ -233,6 +243,21 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con "-DKONAN_CORE_SYMBOLICATION=1", "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") + KonanTarget.TVOS_ARM64 -> + listOf("-DKONAN_OBJC_INTEROP=1", + "-DKONAN_IOS=1", + "-DKONAN_ARM64=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1", + "-DKONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1", + "-DMACHSIZE=64") + + KonanTarget.TVOS_X64 -> + listOf("-DKONAN_OBJC_INTEROP=1", + "-DKONAN_IOS=1", + "-DKONAN_X64=1", + "-DKONAN_CORE_SYMBOLICATION=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") + KonanTarget.ANDROID_ARM32 -> listOf("-D__ANDROID__", "-DUSE_GCC_UNWIND=1", @@ -284,9 +309,6 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con KonanTarget.WATCHOS_ARM32 -> TODO("implement me") KonanTarget.WATCHOS_X64 -> TODO("implement me") KonanTarget.WATCHOS_X86 -> TODO("implement me") - KonanTarget.TVOS_ARM64 -> TODO("implement me") - KonanTarget.TVOS_X64 -> TODO("implement me") - is KonanTarget.ZEPHYR -> listOf( "-DKONAN_ZEPHYR=1", diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt index c7386ccdb3b..0f4cdec1a97 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt @@ -42,7 +42,9 @@ fun loadConfigurables(target: KonanTarget, properties: Properties, baseDir: Stri LinuxConfigurablesImpl(target, properties, baseDir) KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 -> LinuxMIPSConfigurablesImpl(target, properties, baseDir) - KonanTarget.MACOS_X64, KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 -> + KonanTarget.MACOS_X64, + KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64, + KonanTarget.TVOS_ARM64, KonanTarget.TVOS_X64 -> AppleConfigurablesImpl(target, properties, baseDir) KonanTarget.ANDROID_ARM32, KonanTarget.ANDROID_ARM64, KonanTarget.ANDROID_X86, KonanTarget.ANDROID_X64 -> @@ -52,7 +54,6 @@ fun loadConfigurables(target: KonanTarget, properties: Properties, baseDir: Stri KonanTarget.WASM32 -> WasmConfigurablesImpl(target, properties, baseDir) KonanTarget.WATCHOS_ARM64, KonanTarget.WATCHOS_ARM32, KonanTarget.WATCHOS_X64, KonanTarget.WATCHOS_X86-> TODO("unimplemented: $target") - KonanTarget.TVOS_ARM64, KonanTarget.TVOS_X64 -> TODO("unimplemented: $target") is KonanTarget.ZEPHYR -> ZephyrConfigurablesImpl(target, properties, baseDir) } 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 f172291f3b2..f33c991f3b8 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 @@ -58,16 +58,4 @@ abstract class KonanPropertiesLoader(override val target: KonanTarget, private val dependencyProcessor by lazy { baseDir?.let { DependencyProcessor(java.io.File(it), this) } } -} - -fun Properties.keepOnlyDefaultProfiles() { - val DEPENDENCY_PROFILES_KEY = "dependencyProfiles" - val dependencyProfiles = this.getProperty(DEPENDENCY_PROFILES_KEY) - if (dependencyProfiles != "default alt") - error("unexpected $DEPENDENCY_PROFILES_KEY value: expected 'default alt', got '$dependencyProfiles'") - - // Force build to use only 'default' profile: - this.setProperty(DEPENDENCY_PROFILES_KEY, "default") - // TODO: it actually affects only resolution made in :dependencies, - // that's why we assume that 'default' profile comes first (and check this above). -} +} \ No newline at end of file diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt index af158c1b80c..60d60cf8c09 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt @@ -145,20 +145,28 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables) private val strip = "$absoluteTargetToolchain/usr/bin/strip" private val dsymutil = "$absoluteLlvmHome/bin/llvm-dsymutil" + private val KonanTarget.isSimulator: Boolean + get() = this == KonanTarget.TVOS_X64 || this == KonanTarget.IOS_X64 || + this == KonanTarget.WATCHOS_X86 || this == KonanTarget.WATCHOS_X64 + private fun provideCompilerRtLibrary(libraryName: String): String? { - val suffix = if (libraryName.isNotEmpty() && target == KonanTarget.IOS_X64) { - "iossim" + val prefix = when (target.family) { + Family.IOS -> "ios" + Family.WATCHOS -> "watchos" + Family.TVOS -> "tvos" + Family.OSX -> "osx" + else -> error("Target $target is unsupported") + } + val suffix = if (libraryName.isNotEmpty() && target.isSimulator) { + "sim" } else { - when (val family = configurables.target.family) { - Family.OSX -> "osx" - Family.IOS -> "ios" - else -> error("Family $family is unsupported") - } + "" } + val dir = File("$absoluteTargetToolchain/usr/lib/clang/").listFiles.firstOrNull()?.absolutePath - val mangledName = if (libraryName.isEmpty()) "" else "${libraryName}_" + val mangledLibraryName = if (libraryName.isEmpty()) "" else "${libraryName}_" - return if (dir != null) "$dir/lib/darwin/libclang_rt.$mangledName$suffix.a" else null + return if (dir != null) "$dir/lib/darwin/libclang_rt.$mangledLibraryName$prefix$suffix.a" else null } private val compilerRtLibrary: String? by lazy { @@ -222,7 +230,9 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables) private fun rpath(dynamic: Boolean): List = listOfNotNull( when (target.family) { Family.OSX -> "@executable_path/../Frameworks" - Family.IOS -> "@executable_path/Frameworks" + Family.IOS, + Family.WATCHOS, + Family.TVOS -> "@executable_path/Frameworks" else -> error(target) }, "@loader_path/Frameworks".takeIf { dynamic } @@ -437,7 +447,9 @@ fun linker(configurables: Configurables): LinkerFlags = LinuxBasedLinker(configurables as LinuxConfigurables) KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 -> LinuxBasedLinker(configurables as LinuxMIPSConfigurables) - KonanTarget.MACOS_X64, KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 -> + KonanTarget.MACOS_X64, + KonanTarget.TVOS_X64, KonanTarget.TVOS_ARM64, + KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 -> MacOSBasedLinker(configurables as AppleConfigurables) KonanTarget.ANDROID_ARM32, KonanTarget.ANDROID_ARM64, KonanTarget.ANDROID_X86, KonanTarget.ANDROID_X64 -> @@ -450,8 +462,6 @@ fun linker(configurables: Configurables): LinkerFlags = KonanTarget.WATCHOS_ARM32 -> TODO("implement me") KonanTarget.WATCHOS_X64 -> TODO("implement me") KonanTarget.WATCHOS_X86 -> TODO("implement me") - KonanTarget.TVOS_ARM64 -> TODO("implement me") - KonanTarget.TVOS_X64 -> TODO("implement me") is KonanTarget.ZEPHYR -> ZephyrLinker(configurables as ZephyrConfigurables) } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Platform.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Platform.kt index 461bd695a56..6aeb83882db 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Platform.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Platform.kt @@ -50,7 +50,7 @@ class PlatformManager(distribution: Distribution = Distribution(), experimental: */ val filteredOutEnabledButNotSupported get() = enabled.filterNot { it == KonanTarget.WATCHOS_X64 || - it == KonanTarget.WATCHOS_X86|| it == KonanTarget.WATCHOS_ARM64 || it == KonanTarget.WATCHOS_ARM32 || - it == KonanTarget.TVOS_X64 || it == KonanTarget.TVOS_ARM64} + it == KonanTarget.WATCHOS_X86|| it == KonanTarget.WATCHOS_ARM64 || it == KonanTarget.WATCHOS_ARM32 + } } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Xcode.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Xcode.kt index ae8e267bd83..28c3c14c7db 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Xcode.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Xcode.kt @@ -25,8 +25,11 @@ interface Xcode { val iphoneosSdk: String val iphonesimulatorSdk: String val version: String + val appletvosSdk: String + val appletvsimulatorSdk: String // Xcode.app/Contents/Developer/usr val additionalTools: String + val simulatorRuntimes: String companion object { val current: Xcode by lazy { @@ -47,9 +50,14 @@ private object CurrentXcode : Xcode { File(bitcodeBuildToolPath).parentFile.parentFile.absolutePath } + override val simulatorRuntimes: String by lazy { + Command("/usr/bin/xcrun", "simctl", "list", "runtimes", "-j").getOutputLines().joinToString(separator = "\n") + } override val macosxSdk by lazy { getSdkPath("macosx") } override val iphoneosSdk by lazy { getSdkPath("iphoneos") } override val iphonesimulatorSdk by lazy { getSdkPath("iphonesimulator") } + override val appletvosSdk by lazy { getSdkPath("appletvos") } + override val appletvsimulatorSdk by lazy { getSdkPath("appletvsimulator") } override val version by lazy { xcrun("xcodebuild", "-version") diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt index 8116ae21e18..babaef6eff0 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt @@ -54,7 +54,7 @@ enum class OutputKind( false ) { override fun availableFor(target: KonanTarget) = - target.family == Family.OSX || target.family == Family.IOS + target.family.isAppleFamily }, DYNAMIC( CompilerOutputKind.DYNAMIC, diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanCompileConfig.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanCompileConfig.kt index 0ba242b82b3..925f555dd85 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanCompileConfig.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanCompileConfig.kt @@ -119,7 +119,7 @@ open class KonanFramework(name: String, get() = project.konanBinBaseDir override fun targetIsSupported(target: KonanTarget): Boolean = - target.family == Family.OSX || target.family == Family.IOS + target.family.isAppleFamily } open class KonanLibrary(name: String, diff --git a/tools/scripts/update_xcode.sh b/tools/scripts/update_xcode.sh index 00d58fae91c..a2eabf3497a 100755 --- a/tools/scripts/update_xcode.sh +++ b/tools/scripts/update_xcode.sh @@ -2,10 +2,12 @@ # "brew install coreutils" for grealpath. KONAN_TOOLCHAIN_VERSION=9 -SDKS="macosx iphoneos iphonesimulator" +SDKS="macosx iphoneos iphonesimulator appletvos appletvsimulator" TARBALL_macosx=target-sysroot-$KONAN_TOOLCHAIN_VERSION-macos_x64 TARBALL_iphoneos=target-sysroot-$KONAN_TOOLCHAIN_VERSION-ios_arm64 TARBALL_iphonesimulator=target-sysroot-$KONAN_TOOLCHAIN_VERSION-ios_x64 +TARBALL_appletvos=target-sysroot-$KONAN_TOOLCHAIN_VERSION-tvos_arm64 +TARBALL_appletvsimulator=target-sysroot-$KONAN_TOOLCHAIN_VERSION-tvos_x64 TARBALL_watchos=target-sysroot-$KONAN_TOOLCHAIN_VERSION-watchos_arm32 TARBALL_watchsimulator=target-sysroot-$KONAN_TOOLCHAIN_VERSION-watchos_x64 TARBALL_xcode=target-toolchain-$KONAN_TOOLCHAIN_VERSION-macos_x64