Skip to content

Commit

Permalink
Preliminary tvOS support.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbogolepov committed Sep 7, 2019
1 parent 7844dc5 commit 237b7ef
Show file tree
Hide file tree
Showing 23 changed files with 296 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class CStubsManager(private val target: KonanTarget) {
if (stubs.isEmpty()) return null

val compilerOptions = mutableListOf<String>()
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.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

Expand All @@ -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.

Expand Down Expand Up @@ -99,7 +96,9 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) {
private fun produceFrameworkSpecific(headerLines: List<String>) {
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)
}
Expand Down Expand Up @@ -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)
}

Expand All @@ -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)
}
Expand Down Expand Up @@ -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") {
" <integer>$it</integer>"
}
contents.append("""
| <key>MinimumOSVersion</key>
| <string>$minimumOsVersion</string>
| <key>UIDeviceFamily</key>
| <array>
| <integer>1</integer>
| <integer>2</integer>
|$xmlValues
| </array>
""".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("""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -213,8 +214,14 @@ fun localExecutor(project: Project) = { a: Action<in ExecSpec> -> 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)
Expand All @@ -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<in ExecSpec>): 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 }
}
}

Expand Down
46 changes: 33 additions & 13 deletions build-tools/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> {
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("""
Expand All @@ -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")
}

Expand All @@ -147,4 +167,4 @@ open class FrameworkTest : DefaultTask() {
""".trimMargin()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions build-tools/src/main/kotlin/org/jetbrains/kotlin/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ fun compileSwift(project: Project, target: KonanTarget, sources: List<String>, 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")
}
Expand Down
Loading

0 comments on commit 237b7ef

Please sign in to comment.