Unverified Commit 44f2d511 authored by Ilya Ryzhenkov's avatar Ilya Ryzhenkov Committed by GitHub

Support tvOS (#719)

parent 5058bbc1
......@@ -46,7 +46,7 @@ jobs:
path: ./skiko/build/reports/tests
retention-days: 5
ios:
iOS:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
......@@ -99,6 +99,29 @@ jobs:
path: ./skiko/build/reports/tests
retention-days: 5
tvOS:
# TVOS requires macos 13
runs-on: macos-13
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
- shell: bash
run: |
./gradlew --stacktrace --info -Pskiko.native.enabled=true -Pskiko.test.onci=true :skiko:tvosX64Test
# tvosSimulatorArm64Test will build the binary but the tests will be skipped due to X64 host machine
./gradlew --stacktrace --info -Pskiko.native.enabled=true -Pskiko.test.onci=true :skiko:tvosSimulatorArm64Test
- uses: actions/upload-artifact@v2
if: always()
with:
name: test-reports-macos
path: ./skiko/build/reports/tests
retention-days: 5
linux:
runs-on: ubuntu-20.04
steps:
......
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.*
buildscript {
repositories {
......@@ -82,11 +82,19 @@ kotlin {
macosArm64() {
configureToLaunchFromXcode()
}
ios() {
iosSimulatorArm64() {
configureToLaunchFromAppCode()
configureToLaunchFromXcode()
}
iosSimulatorArm64() {
tvosX64() {
configureToLaunchFromAppCode()
configureToLaunchFromXcode()
}
tvosArm64() {
configureToLaunchFromAppCode()
configureToLaunchFromXcode()
}
tvosSimulatorArm64() {
configureToLaunchFromAppCode()
configureToLaunchFromXcode()
}
......@@ -165,12 +173,27 @@ kotlin {
val macosArm64Main by getting {
dependsOn(macosMain)
}
val iosMain by getting {
val uikitMain by creating {
dependsOn(darwinMain)
}
val iosMain by creating {
dependsOn(uikitMain)
}
val iosSimulatorArm64Main by getting {
dependsOn(iosMain)
}
val tvosMain by creating {
dependsOn(uikitMain)
}
val tvosX64Main by getting {
dependsOn(tvosMain)
}
val tvosArm64Main by getting {
dependsOn(tvosMain)
}
val tvosSimulatorArm64Main by getting {
dependsOn(tvosMain)
}
}
}
}
......@@ -213,7 +236,7 @@ if (hostOs == "macos") {
}
project.tasks.register<JavaExec>("runAwt") {
val kotlinTask = project.tasks.named("compileKotlinAwt")
val kotlinTask = project.tasks.named("compileKotlinAwt")
dependsOn(kotlinTask)
systemProperty("skiko.fps.enabled", "true")
systemProperty("skiko.linux.autodpi", "true")
......@@ -237,8 +260,14 @@ tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile>().configureEach
}
enum class Target(val simulator: Boolean, val key: String) {
WATCHOS_X86(true, "watchos"), WATCHOS_ARM64(false, "watchos"),
IOS_X64(true, "iosX64"), IOS_ARM64(false, "iosArm64"), IOS_SIMULATOR_ARM64(true, "iosSimulatorArm64")
WATCHOS_X86(true, "watchos"),
WATCHOS_ARM64(false, "watchos"),
IOS_X64(true, "iosX64"),
IOS_ARM64(false, "iosArm64"),
IOS_SIMULATOR_ARM64(true, "iosSimulatorArm64"),
TVOS_X64(true, "tvosX64"),
TVOS_ARM64(true, "tvosArm64"),
TVOS_SIMULATOR_ARM64(true, "tvosSimulatorArm64"),
}
......@@ -246,9 +275,16 @@ if (hostOs == "macos") {
// Create Xcode integration tasks.
val sdkName: String? = System.getenv("SDK_NAME")
println("Configuring XCode for $sdkName")
val target = sdkName.orEmpty().let {
when {
it.startsWith("iphoneos") -> Target.IOS_ARM64
it.startsWith("appletvsimulator") -> when (host) {
"macos-x64" -> Target.TVOS_X64
"macos-arm64" -> Target.TVOS_SIMULATOR_ARM64
else -> throw GradleException("Host OS is not supported")
}
it.startsWith("appletvos") -> Target.TVOS_ARM64
it.startsWith("watchos") -> Target.WATCHOS_ARM64
it.startsWith("watchsimulator") -> Target.WATCHOS_X86
else -> when (host) {
......@@ -279,6 +315,8 @@ if (hostOs == "macos") {
// Otherwise copy the executable into the Xcode output directory.
tasks.create("packForXCode", Copy::class.java) {
dependsOn(kotlinBinary.linkTask)
println("Packing for XCode: ${kotlinBinary.target}")
destinationDir = file(targetBuildDir)
......
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UILaunchStoryboardName</key>
<string></string>
</dict>
</plist>
name: SkikoSample
name: Skiko Sample
options:
bundleIdPrefix: org.jetbrains
settings:
DEVELOPMENT_TEAM: N462MKSJ7M
CODE_SIGN_IDENTITY: "iPhone Developer"
CODE_SIGN_IDENTITY: "Apple Development"
CODE_SIGN_STYLE: Automatic
MARKETING_VERSION: "1.0"
CURRENT_PROJECT_VERSION: "4"
SDKROOT: iphoneos
targets:
SkikoSample:
Skiko Sample iOS:
type: application
platform: iOS
deploymentTarget: "12.0"
......@@ -32,3 +32,27 @@ targets:
ENABLE_BITCODE: "YES"
ONLY_ACTIVE_ARCH: "NO"
VALID_ARCHS: "arm64"
Skiko Sample tvOS:
type: application
platform: tvOS
deploymentTarget: "14.0"
prebuildScripts:
- script: cd "$SRCROOT" && ./gradlew -p . packForXCode
name: GradleCompile
info:
path: plists/tvOS/Info.plist
properties:
UILaunchStoryboardName: ""
sources:
- path: "src/"
excludes:
- "android*/**"
- "awt*/**"
- "iosApp*/**"
- "js*/**"
settings:
LIBRARY_SEARCH_PATHS: "$(inherited)"
ENABLE_BITCODE: "YES"
ONLY_ACTIVE_ARCH: "NO"
VALID_ARCHS: "arm64"
......@@ -140,6 +140,15 @@ kotlin {
if (supportNativeIosX64) {
skikoProjectContext.configureNativeTarget(OS.IOS, Arch.X64, iosX64())
}
if (supportNativeTvosArm64) {
skikoProjectContext.configureNativeTarget(OS.TVOS, Arch.Arm64, tvosArm64())
}
if (supportNativeTvosSimulatorArm64) {
skikoProjectContext.configureNativeTarget(OS.TVOS, Arch.Arm64, tvosSimulatorArm64())
}
if (supportNativeTvosX64) {
skikoProjectContext.configureNativeTarget(OS.TVOS, Arch.X64, tvosX64())
}
sourceSets {
val commonMain by getting {
......@@ -299,35 +308,76 @@ kotlin {
dependsOn(macosTest)
}
}
if (supportAnyNativeIos) {
val iosMain by creating {
if (supportAnyNativeIos || supportAllNativeTvos) {
val uikitMain by creating {
dependsOn(darwinMain)
}
val iosTest by creating {
val uikitTest by creating {
dependsOn(darwinTest)
}
if (supportNativeIosArm64) {
val iosArm64Main by getting {
dependsOn(iosMain)
if (supportAnyNativeIos) {
val iosMain by creating {
dependsOn(uikitMain)
}
val iosArm64Test by getting {
dependsOn(iosTest)
val iosTest by creating {
dependsOn(uikitTest)
}
}
if (supportNativeIosSimulatorArm64) {
val iosSimulatorArm64Main by getting {
dependsOn(iosMain)
if (supportNativeIosArm64) {
val iosArm64Main by getting {
dependsOn(iosMain)
}
val iosArm64Test by getting {
dependsOn(iosTest)
}
}
val iosSimulatorArm64Test by getting {
dependsOn(iosTest)
if (supportNativeIosSimulatorArm64) {
val iosSimulatorArm64Main by getting {
dependsOn(iosMain)
}
val iosSimulatorArm64Test by getting {
dependsOn(iosTest)
}
}
if (supportNativeIosX64) {
val iosX64Main by getting {
dependsOn(iosMain)
}
val iosX64Test by getting {
dependsOn(iosTest)
}
}
}
if (supportNativeIosX64) {
val iosX64Main by getting {
dependsOn(iosMain)
if (supportAnyNativeTvos) {
val tvosMain by creating {
dependsOn(uikitMain)
}
val tvosTest by creating {
dependsOn(uikitTest)
}
if (supportNativeTvosArm64) {
val tvosArm64Main by getting {
dependsOn(tvosMain)
}
val tvosArm64Test by getting {
dependsOn(tvosTest)
}
}
if (supportNativeTvosSimulatorArm64) {
val tvosSimulatorArm64Main by getting {
dependsOn(tvosMain)
}
val tvosSimulatorArm64Test by getting {
dependsOn(tvosTest)
}
}
val iosX64Test by getting {
dependsOn(iosTest)
if (supportNativeTvosX64) {
val tvosX64Main by getting {
dependsOn(tvosMain)
}
val tvosX64Test by getting {
dependsOn(tvosTest)
}
}
}
}
......
......@@ -30,10 +30,10 @@ class SkikoProjectContext(
* (tasks' registration during other task's registration is prohibited)
*/
fun SkikoProjectContext.registerOrGetSkiaDirProvider(
os: OS, arch: Arch, isIosSim: Boolean = false
os: OS, arch: Arch, isUikitSim: Boolean = false
): Provider<File> = with(this.project) {
val taskNameSuffix = joinToTitleCamelCase(buildType.id, os.idWithSuffix(isIosSim = isIosSim), arch.id)
val skiaRelease = skiko.skiaReleaseFor(os, arch, buildType, isIosSim)
val taskNameSuffix = joinToTitleCamelCase(buildType.id, os.idWithSuffix(isUikitSim = isUikitSim), arch.id)
val skiaRelease = skiko.skiaReleaseFor(os, arch, buildType, isUikitSim)
val downloadSkia = tasks.registerOrGetTask<Download>("downloadSkia$taskNameSuffix") {
onlyIf { !dest.exists() }
onlyIfModified(true)
......@@ -89,6 +89,21 @@ val Project.supportNativeIosX64: Boolean
val Project.supportAnyNativeIos: Boolean
get() = supportAllNativeIos || supportNativeIosArm64 || supportNativeIosSimulatorArm64 || supportNativeIosX64
val Project.supportAllNativeTvos: Boolean
get() = supportAllNative || findProperty("skiko.native.tvos.enabled") == "true" || isInIdea
val Project.supportNativeTvosArm64: Boolean
get() = supportAllNativeTvos || findProperty("skiko.native.tvos.arm64.enabled") == "true" || isInIdea
val Project.supportNativeTvosSimulatorArm64: Boolean
get() = supportAllNativeTvos || findProperty("skiko.native.tvos.simulatorArm64.enabled") == "true" || isInIdea
val Project.supportNativeTvosX64: Boolean
get() = supportAllNativeTvos || findProperty("skiko.native.tvos.x64.enabled") == "true" || isInIdea
val Project.supportAnyNativeTvos: Boolean
get() = supportAllNativeTvos || supportNativeTvosArm64 || supportNativeTvosSimulatorArm64 || supportNativeTvosX64
val Project.supportNativeMac: Boolean
get() = supportAllNative || findProperty("skiko.native.mac.enabled") == "true" || isInIdea
......
......@@ -10,7 +10,8 @@ enum class OS(
Windows("windows", arrayOf()),
MacOS("macos", arrayOf("-mmacosx-version-min=10.13")),
Wasm("wasm", arrayOf()),
IOS("ios", arrayOf())
IOS("ios", arrayOf()),
TVOS("tvos", arrayOf())
;
val isWindows
......@@ -19,8 +20,8 @@ enum class OS(
val isMacOs
get() = this == MacOS
fun idWithSuffix(isIosSim: Boolean = false): String {
return id + if (isIosSim) "Sim" else ""
fun idWithSuffix(isUikitSim: Boolean = false): String {
return id + if (isUikitSim) "Sim" else ""
}
}
......@@ -28,7 +29,7 @@ val OS.isCompatibleWithHost: Boolean
get() = when (this) {
OS.Linux -> hostOs == OS.Linux
OS.Windows -> hostOs == OS.Windows
OS.MacOS, OS.IOS -> hostOs == OS.MacOS
OS.MacOS, OS.IOS, OS.TVOS -> hostOs == OS.MacOS
OS.Wasm -> true
OS.Android -> true
}
......@@ -42,7 +43,7 @@ fun compilerForTarget(os: OS, arch: Arch): String =
}
OS.Android -> "clang++"
OS.Windows -> "cl.exe"
OS.MacOS, OS.IOS -> "clang++"
OS.MacOS, OS.IOS, OS.TVOS -> "clang++"
OS.Wasm -> "emcc"
}
......@@ -53,7 +54,7 @@ val OS.dynamicLibExt: String
get() = when (this) {
OS.Linux, OS.Android -> ".so"
OS.Windows -> ".dll"
OS.MacOS, OS.IOS -> ".dylib"
OS.MacOS, OS.IOS, OS.TVOS -> ".dylib"
OS.Wasm -> ".wasm"
}
......@@ -141,9 +142,9 @@ class SkikoProperties(private val myProject: Project) {
get() = !isRelease
fun skiaReleaseFor(os: OS, arch: Arch, buildType: SkiaBuildType, isIosSim: Boolean = false): String {
val target = "${os.idWithSuffix(isIosSim = isIosSim)}-${arch.id}"
val target = "${os.idWithSuffix(isUikitSim = isIosSim)}-${arch.id}"
val tag = myProject.property("dependencies.skia.$target") as String
return "${tag}/Skia-${tag}-${os.idWithSuffix(isIosSim = isIosSim)}-${buildType.id}-${arch.id}"
return "${tag}/Skia-${tag}-${os.idWithSuffix(isUikitSim = isIosSim)}-${buildType.id}-${arch.id}"
}
val releaseGithubVersion: String
......@@ -208,6 +209,6 @@ object SkikoArtifacts {
// does not seem possible (at least without adding a dash to a target's tasks),
// so we're using the default naming pattern instead.
// See https://youtrack.jetbrains.com/issue/KT-50001.
fun nativeArtifactIdFor(os: OS, arch: Arch, isIosSim: Boolean = false) =
"skiko-${os.id + if (isIosSim) "simulator" else ""}${arch.id}"
fun nativeArtifactIdFor(os: OS, arch: Arch, isUikitSim: Boolean = false) =
"skiko-${os.id + if (isUikitSim) "simulator" else ""}${arch.id}"
}
......@@ -75,6 +75,12 @@ fun skiaPreprocessorFlags(os: OS, buildType: SkiaBuildType): Array<String> {
"-DSK_SHAPER_CORETEXT_AVAILABLE",
"-DSK_METAL"
)
OS.TVOS -> listOf(
"-DSK_BUILD_FOR_IOS",
"-DSK_BUILD_FOR_TVOS",
"-DSK_SHAPER_CORETEXT_AVAILABLE",
"-DSK_METAL"
)
OS.Windows -> listOf(
"-DSK_BUILD_FOR_WIN",
"-D_CRT_SECURE_NO_WARNINGS",
......@@ -155,10 +161,10 @@ fun KotlinTarget.generateVersion(
skikoProperties: SkikoProperties
) {
val targetName = this.name
val isArm64Simulator = isIosSimArm64()
val isUikitSim = isUikitSimulator()
val generatedDir = project.layout.buildDirectory.dir("generated/$targetName")
val generateVersionTask = project.registerSkikoTask<DefaultTask>(
"generateVersion${toTitleCase(platformType.name)}".withSuffix(isIosSim = isArm64Simulator),
"generateVersion${toTitleCase(platformType.name)}".withSuffix(isUikitSim = isUikitSim),
targetOs,
targetArch
) {
......
......@@ -119,7 +119,7 @@ fun SkikoProjectContext.createCompileJvmBindingsTask(
"-fPIC"
)
}
OS.Wasm, OS.IOS -> error("Should not reach here")
OS.Wasm, OS.IOS, OS.TVOS -> error("Should not reach here")
}
flags.set(
......@@ -322,7 +322,7 @@ fun SkikoProjectContext.createLinkJvmBindings(
)
linker.set(project.androidClangFor(targetArch))
}
OS.Wasm, OS.IOS -> {
OS.Wasm, OS.IOS, OS.TVOS -> {
throw GradleException("This task shalln't be used with $targetOs")
}
}
......
......@@ -5,43 +5,38 @@ import CompileSkikoCppTask
import OS
import SkiaBuildType
import SkikoProjectContext
import SkikoProperties
import WriteCInteropDefFile
import compilerForTarget
import isCompatibleWithHost
import joinToTitleCamelCase
import listOfFrameworks
import mutableListOfLinkerOptions
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.getByName
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTool
import projectDirs
import registerOrGetSkiaDirProvider
import registerSkikoTask
import toTitleCase
import java.io.File
fun String.withSuffix(isIosSim: Boolean = false) =
this + if (isIosSim) "Sim" else ""
fun String.withSuffix(isUikitSim: Boolean = false) =
this + if (isUikitSim) "Sim" else ""
fun KotlinTarget.isIosSimArm64() =
name.contains("iosSimulatorArm64", ignoreCase = true)
fun KotlinTarget.isUikitSimulator() =
name.contains("Simulator", ignoreCase = true) || name == "tvosX64" // x64 tvOS is implicitly a simulator
fun SkikoProjectContext.compileNativeBridgesTask(
os: OS, arch: Arch, isArm64Simulator: Boolean
os: OS, arch: Arch, isUikitSim: Boolean
): TaskProvider<CompileSkikoCppTask> = with (this.project) {
val skiaNativeDir = registerOrGetSkiaDirProvider(os, arch, isIosSim = isArm64Simulator)
val skiaNativeDir = registerOrGetSkiaDirProvider(os, arch, isUikitSim = isUikitSim)
val actionName = "compileNativeBridges".withSuffix(isIosSim = isArm64Simulator)
val actionName = "compileNativeBridges".withSuffix(isUikitSim = isUikitSim)
return project.registerSkikoTask<CompileSkikoCppTask>(actionName, os, arch) {
dependsOn(skiaNativeDir)
......@@ -49,7 +44,7 @@ fun SkikoProjectContext.compileNativeBridgesTask(
compiler.set(compilerForTarget(os, arch))
buildTargetOS.set(os)
if (isArm64Simulator) {
if (isUikitSim) {
buildSuffix.set("sim")
}
buildTargetArch.set(arch)
......@@ -62,8 +57,8 @@ fun SkikoProjectContext.compileNativeBridgesTask(
val iphoneSimSdk = "$sdkRoot/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk"
val iosArchFlags = when (arch) {
Arch.Arm64 -> arrayOf(
"-target", if (isArm64Simulator) "arm64-apple-ios-simulator" else "arm64-apple-ios",
"-isysroot", if (isArm64Simulator) iphoneSimSdk else iphoneOsSdk,
"-target", if (isUikitSim) "arm64-apple-ios-simulator" else "arm64-apple-ios",
"-isysroot", if (isUikitSim) iphoneSimSdk else iphoneOsSdk,
"-miphoneos-version-min=12.0"
)
Arch.X64 -> arrayOf(
......@@ -80,6 +75,30 @@ fun SkikoProjectContext.compileNativeBridgesTask(
*skiaPreprocessorFlags(OS.IOS, buildType),
))
}
OS.TVOS -> {
val sdkRoot = "/Applications/Xcode.app/Contents/Developer/Platforms"
val tvOsSdk = "$sdkRoot/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk"
val tvSimSdk = "$sdkRoot/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk"
val tvosArchFlags = when (arch) {
Arch.Arm64 -> arrayOf(
"-target", if (isUikitSim) "arm64-apple-tvos-simulator" else "arm64-apple-tvos",
if (isUikitSim) "-mappletvsimulator-version-min=12.0" else "-mappletvos-version-min=12.0" ,
"-isysroot", if (isUikitSim) tvSimSdk else tvOsSdk,
)
Arch.X64 -> arrayOf(
"-target", "x86_64-apple-tvos-simulator",
"-mappletvsimulator-version-min=12.0",
"-isysroot", tvSimSdk
)
else -> throw GradleException("Unsupported arch: $arch")
}
flags.set(listOf(
*tvosArchFlags,
*buildType.clangFlags,
"-stdlib=libc++",
*skiaPreprocessorFlags(OS.TVOS, buildType),
))
}
OS.MacOS -> {
flags.set(listOf(
*buildType.clangFlags,
......@@ -120,7 +139,7 @@ fun configureCinterop(
linkerOpts: List<String>,
) {
val tasks = target.project.tasks
val taskNameSuffix = joinToTitleCamelCase(os.idWithSuffix(isIosSim = target.isIosSimArm64()), arch.id)
val taskNameSuffix = joinToTitleCamelCase(os.idWithSuffix(isUikitSim = target.isUikitSimulator()), arch.id)
val writeCInteropDef = tasks.register("writeCInteropDef$taskNameSuffix", WriteCInteropDefFile::class.java) {
this.linkerOpts.set(linkerOpts)
outputFile.set(project.layout.buildDirectory.file("cinterop/$targetString/skiko.def"))
......@@ -167,11 +186,11 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
if (!os.isCompatibleWithHost) return
target.generateVersion(os, arch, skiko)
val isArm64Simulator = target.isIosSimArm64()
val isUikitSim = target.isUikitSimulator()
val targetString = "${os.idWithSuffix(isIosSim = isArm64Simulator)}-${arch.id}"
val targetString = "${os.idWithSuffix(isUikitSim = isUikitSim)}-${arch.id}"
val unzipper = registerOrGetSkiaDirProvider(os, arch, isArm64Simulator)
val unzipper = registerOrGetSkiaDirProvider(os, arch, isUikitSim)
val unpackedSkia = unzipper.get()
val skiaDir = unpackedSkia.absolutePath
......@@ -194,6 +213,11 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
configureCinterop("uikit", os, arch, target, targetString, iosFrameworks)
mutableListOfLinkerOptions(iosFrameworks)
}
OS.TVOS -> {
val tvosFrameworks = listOfFrameworks("Metal", "CoreGraphics", "CoreText", "UIKit")
configureCinterop("uikit", os, arch, target, targetString, tvosFrameworks)
mutableListOfLinkerOptions(tvosFrameworks)
}
OS.Linux -> mutableListOfLinkerOptions(
"-L/usr/lib/x86_64-linux-gnu",
"-lfontconfig",
......@@ -226,10 +250,10 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
}
}
val crossCompileTask = compileNativeBridgesTask(os, arch, isArm64Simulator = isArm64Simulator)
val crossCompileTask = compileNativeBridgesTask(os, arch, isUikitSim = isUikitSim)
// TODO: move to LinkSkikoTask.
val actionName = "linkNativeBridges".withSuffix(isIosSim = isArm64Simulator)
val actionName = "linkNativeBridges".withSuffix(isUikitSim = isUikitSim)
val linkTask = project.registerSkikoTask<Exec>(actionName, os, arch) {
dependsOn(crossCompileTask)
val objectFilesDir = crossCompileTask.map { it.outDir.get() }
......@@ -245,7 +269,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
executable = "ar"
argumentProviders.add { listOf("-crs", staticLib) }
}
OS.MacOS, OS.IOS -> {
OS.MacOS, OS.IOS, OS.TVOS -> {
executable = "libtool"
argumentProviders.add { listOf("-static", "-o", staticLib) }
}
......
......@@ -23,7 +23,10 @@ val skikoArtifactIds: List<String> =
SkikoArtifacts.nativeArtifactIdFor(OS.MacOS, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.Arm64, isIosSim = true),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.Arm64, isUikitSim = true),
SkikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.Arm64, isUikitSim = true),
)
val downloadSkikoArtifactsFromComposeDev by tasks.registering(DownloadFromSpaceMavenRepoTask::class) {
......
......@@ -14,6 +14,10 @@ dependencies.skia.iosSim-arm64=m116-47d3027-1
dependencies.skia.iosSim-x64=m116-47d3027-1
dependencies.skia.android-x64=m116-47d3027-1
dependencies.skia.android-arm64=m116-47d3027-1
dependencies.skia.tvos-x64=m116-47d3027-1
dependencies.skia.tvos-arm64=m116-47d3027-1
dependencies.skia.tvosSim-arm64=m116-47d3027-1
dependencies.skia.tvosSim-x64=m116-47d3027-1
org.gradle.jvmargs=-Xmx3G -XX:MaxMetaspaceSize=512m
......
......@@ -38,7 +38,7 @@ internal actual fun makeDefaultRenderFactory(): RenderFactory =
GraphicsApi.SOFTWARE_FAST -> LinuxSoftwareRedrawer(layer, analytics, properties)
else -> LinuxOpenGLRedrawer(layer, analytics, properties)
}
OS.Android, OS.JS, OS.Ios, OS.Unknown -> throw UnsupportedOperationException("The AWT target doesn't support $hostOs")
OS.Android, OS.JS, OS.Ios, OS.Tvos, OS.Unknown -> throw UnsupportedOperationException("The awt target doesn't support $hostOs")
}
}
......@@ -51,7 +51,7 @@ internal actual fun URIHandler_openUri(uri: String) {
URI(uri) // Validate URI for exception behavior consistent with the Desktop.browse() case (throwing URISyntaxException)
Runtime.getRuntime().exec(arrayOf("xdg-open", URL(uri).toString()))
}
OS.Android, OS.Windows, OS.MacOS, OS.Ios, OS.JS, OS.Unknown -> {
OS.Android, OS.Windows, OS.MacOS, OS.Ios, OS.Tvos, OS.JS, OS.Unknown -> {
throw UnsupportedOperationException("AWT does not support the BROWSE action on this platform")
}
}
......
......@@ -125,7 +125,7 @@ internal val platformOperations: PlatformOperations by lazy {
}
}
OS.Android -> TODO()
OS.JS, OS.Ios, OS.Unknown -> {
OS.JS, OS.Ios, OS.Tvos, OS.Unknown -> {
TODO("Commonize me")
}
}
......
......@@ -6,6 +6,7 @@ enum class OS(val id: String) {
Windows("windows"),
MacOS("macos"),
Ios("ios"),
Tvos("tvos"),
@Deprecated("JS is invalid host OS name. Consider using enum KotlinBackend to detect JS.")
JS("js"),
......
......@@ -17,7 +17,7 @@ class BreakIteratorTests {
fun breakIteratorWordInstanceTest() {
// Wasm and iOS builds of Skia do not include required data to implement those iterators,
// see `third_party/externals/icu/flutter/README.md`.
if (hostOs == OS.Ios)
if (hostOs == OS.Ios || hostOs == OS.Tvos)
return
val boundary = BreakIterator.makeWordInstance()
......
package org.jetbrains.skiko
import platform.Foundation.NSURL.Companion.URLWithString
import platform.UIKit.UIApplication
import platform.UIKit.UIPasteboard
internal actual fun URIHandler_openUri(uri: String) {
UIApplication.sharedApplication.openURL(URLWithString(uri)!!)
}
import platform.UIKit.*
internal actual fun ClipboardManager_setText(text: String) {
UIPasteboard.generalPasteboard.string = text
......@@ -17,17 +11,7 @@ internal actual fun ClipboardManager_getText(): String? {
internal actual fun ClipboardManager_hasText(): Boolean = UIPasteboard.generalPasteboard.hasStrings()
// TODO: not sure if correct.
actual typealias Cursor = Any
internal actual fun CursorManager_setCursor(component: Any, cursor: Cursor) {}
internal actual fun CursorManager_getCursor(component: Any): Cursor? = null
internal actual fun getCursorById(id: PredefinedCursorsId): Cursor =
when (id) {
PredefinedCursorsId.DEFAULT -> Any()
PredefinedCursorsId.CROSSHAIR -> Any()
PredefinedCursorsId.HAND -> Any()
PredefinedCursorsId.TEXT -> Any()
}
\ No newline at end of file
internal actual fun UIView.skikoInitializeUIView() {
multipleTouchEnabled = true
userInteractionEnabled = true
}
package org.jetbrains.skiko
interface TextActions {
/**
* Copy action. If null, then copy is not possible in current context
*/
val copy: (() -> Unit)?
/**
* Paste action. If null, then paste is not possible in current context
*/
val paste: (() -> Unit)?
/**
* Cut action. If null, then cut is not possible in current context
*/
val cut: (() -> Unit)?
/**
* SelectAll action. If null, then select all is not possible in current context
*/
val selectAll: (() -> Unit)?
}
......@@ -96,7 +96,7 @@ object SkikoProperties {
OS.Linux -> return GraphicsApi.OPENGL
OS.Windows -> return GraphicsApi.DIRECT3D
OS.Android -> return GraphicsApi.OPENGL
OS.JS, OS.Ios, OS.Unknown -> TODO("commonize me")
OS.JS, OS.Ios, OS.Tvos, OS.Unknown -> TODO("commonize me")
}
}
......@@ -110,7 +110,7 @@ object SkikoProperties {
else -> listOf(GraphicsApi.DIRECT3D, GraphicsApi.OPENGL, GraphicsApi.SOFTWARE_FAST, GraphicsApi.SOFTWARE_COMPAT)
}
OS.Android -> return listOf(GraphicsApi.OPENGL)
OS.JS, OS.Ios, OS.Unknown -> TODO("commonize me")
OS.JS, OS.Ios, OS.Tvos, OS.Unknown -> TODO("commonize me")
}
val indexOfInitialApi = fallbackApis.indexOf(initialApi)
......
......@@ -9,6 +9,7 @@ actual val hostOs: OS by lazy {
OsFamily.LINUX -> OS.Linux
OsFamily.WINDOWS -> OS.Windows
OsFamily.IOS -> OS.Ios
OsFamily.TVOS -> OS.Tvos
else -> throw Error("Unsupported OS ${Platform.osFamily}")
}
}
......
package org.jetbrains.skiko
import platform.UIKit.*
// tvOS doesn't have support for clipboard
internal actual fun ClipboardManager_setText(text: String) {
}
internal actual fun ClipboardManager_getText(): String? {
return null
}
internal actual fun ClipboardManager_hasText(): Boolean = false
internal actual fun UIView.skikoInitializeUIView() {
userInteractionEnabled = true
}
package org.jetbrains.skiko
import platform.Foundation.NSURL.Companion.URLWithString
import platform.UIKit.UIApplication
internal actual fun URIHandler_openUri(uri: String) {
UIApplication.sharedApplication.openURL(URLWithString(uri)!!)
}
// TODO: not sure if correct.
actual typealias Cursor = Any
internal actual fun CursorManager_setCursor(component: Any, cursor: Cursor) {}
internal actual fun CursorManager_getCursor(component: Any): Cursor? = null
internal actual fun getCursorById(id: PredefinedCursorsId): Cursor =
when (id) {
PredefinedCursorsId.DEFAULT -> Any()
PredefinedCursorsId.CROSSHAIR -> Any()
PredefinedCursorsId.HAND -> Any()
PredefinedCursorsId.TEXT -> Any()
}
\ No newline at end of file
package org.jetbrains.skiko
import kotlinx.cinterop.*
import org.jetbrains.skia.Point
import org.jetbrains.skia.Rect
import org.jetbrains.skiko.ios.SkikoUITextInputTraits
import org.jetbrains.skiko.redrawer.MetalRedrawer
import org.jetbrains.skiko.redrawer.*
import platform.CoreGraphics.*
import platform.Foundation.*
import platform.Metal.MTLCreateSystemDefaultDevice
import platform.Metal.MTLDeviceProtocol
import platform.Metal.MTLPixelFormatBGRA8Unorm
import platform.QuartzCore.CAMetalLayer
import platform.Metal.*
import platform.QuartzCore.*
import platform.UIKit.*
import platform.darwin.NSInteger
import kotlin.math.max
import kotlin.math.min
import kotlin.native.ref.WeakReference
import kotlin.native.ref.*
internal expect fun UIView.skikoInitializeUIView(): Unit
@Suppress("CONFLICTING_OVERLOADS")
@ExportObjCClass
......@@ -37,7 +31,7 @@ class SkikoUIView : UIView {
private lateinit var _redrawer: MetalRedrawer
init {
multipleTouchEnabled = true
skikoInitializeUIView()
opaque = false // For UIKit interop through a "Hole"
_metalLayer.also {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment