Unverified Commit 360053b2 authored by Hubert Błaszczyk's avatar Hubert Błaszczyk Committed by GitHub

Generalize Skiko build configuration (#1204)

Prepare Skiko config to allow easier registration of
new modules

It consists of:

- A custom DSL for registering Skia archives and linker flags by: Env
(jvm/native/wasm), OS and Arch
- Extracting Skia library declarations and some core-specific linker
flags from environment configuration tasks
- Parameterizing `SkikoArtifacts` to reuse the publication config

Fixes [SKIKO-1141](https://youtrack.jetbrains.com/issue/SKIKO-1141)
parent ec6eec94
...@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile ...@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import tasks.configuration.* import tasks.configuration.*
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import dsl.SkikoDependencyScope
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
...@@ -34,22 +35,166 @@ val skiko = SkikoProperties(rootProject) ...@@ -34,22 +35,166 @@ val skiko = SkikoProperties(rootProject)
val buildType = skiko.buildType val buildType = skiko.buildType
val targetOs = hostOs val targetOs = hostOs
val targetArch = skiko.targetArch val targetArch = skiko.targetArch
val skikoArtifacts = SkikoArtifacts()
val coreDependencies: SkikoDependencyScope.() -> Unit = {
targets {
all {
staticSkiaLibs(
"skia",
"skia_ganesh_ext",
"svg",
"skparagraph",
"skshaper",
"skunicode_core",
"skunicode_icu",
"icu",
"harfbuzz",
"skresources",
"png",
"jpeg",
"webp",
"webp_sse41",
"zlib",
"expat",
"skottie",
"sksg",
"jsonreader"
)
}
jvm {
macos {
staticSkiaLibs("piex", "dng_sdk")
linkFlags("-lobjc")
frameworks(
"AppKit",
"CoreFoundation",
"CoreGraphics",
"CoreServices",
"CoreText",
"Foundation",
"IOKit",
"Metal",
"OpenGL",
"QuartzCore", // for CoreAnimation
)
}
windows {
staticSkiaLibs("d3d12allocator")
}
linux {
// Hack to fix problem with linker not always finding certain declarations.
directStaticSkiaLibs(
"sksg",
"skia",
"skia_ganesh_ext",
"skunicode_core",
"skunicode_icu",
"skshaper",
"jsonreader"
)
dynamicSystemLibs("GL", "X11", "fontconfig")
arm64 { dynamicSystemLibs("EGL") }
}
android {
// Hack to fix problem with linker not always finding certain declarations.
directStaticSkiaLibs("skia", "skia_ganesh_ext")
dynamicSystemLibs("GLESv3", "EGL")
}
}
native {
staticSkiaLibs(
"piex",
"dng_sdk",
)
linux {
// Hack to fix problem with linker not always finding certain declarations.
directStaticSkiaLibs(
"skottie",
"jsonreader",
"sksg",
"skshaper",
"skunicode_core",
"skunicode_icu",
"skia",
"skia_ganesh_ext"
)
dynamicSystemLibs("fontconfig", "GL")
arm64 { dynamicSystemLibs("EGL") }
}
macos {
frameworks(
"Metal",
"CoreGraphics",
"CoreText",
"CoreServices",
)
}
ios {
frameworks(
"Metal",
"CoreGraphics",
"CoreText",
"UIKit",
)
}
tvos {
frameworks(
"Metal",
"CoreGraphics",
"CoreText",
"UIKit",
)
}
}
wasm {
staticSkiaLibs(
"bentleyottmann",
"freetype2",
"jpeg12",
"jpeg16",
"wuffs",
"skcms",
"brotli",
)
linkFlags(
"-l", "GL",
"-s", "MAX_WEBGL_VERSION=2",
"-s", "MIN_WEBGL_VERSION=2",
"-s", "MODULARIZE=1",
"-s", "EXPORT_NAME=loadSkikoWASM",
"-s", "EXPORTED_RUNTIME_METHODS=\"[GL, wasmExports]\"",
"--bind",
)
}
}
}
val skikoProjectContext = SkikoProjectContext( val skikoProjectContext = SkikoProjectContext(
project = project, project = project,
skiko = skiko, skiko = skiko,
kotlin = kotlin, kotlin = kotlin,
kind = SkikoModuleKind.CORE,
artifacts = skikoArtifacts,
windowsSdkPathProvider = { windowsSdkPathProvider = {
findWindowsSdkPaths(gradle, targetArch) findWindowsSdkPaths(gradle, targetArch)
}, },
createChecksumsTask = { targetOs: OS, targetArch: Arch, fileToChecksum: Provider<File> -> createChecksumsTask = { targetOs: OS, targetArch: Arch, fileToChecksum: Provider<File> ->
createChecksumsTask(targetOs, targetArch, fileToChecksum) createChecksumsTask(targetOs, targetArch, fileToChecksum)
}, },
additionalRuntimeLibraries = project.registerAdditionalLibraries(targetOs, targetArch, skiko) additionalRuntimeLibraries = project.registerAdditionalLibraries(targetOs, targetArch, skiko, skikoArtifacts),
configureDependencies = coreDependencies
) )
allprojects { allprojects {
group = SkikoArtifacts.groupId group = SkikoArtifacts.DEFAULT_GROUP_ID
version = skiko.deployVersion version = skiko.deployVersion
} }
......
...@@ -3,7 +3,8 @@ import org.gradle.api.Project ...@@ -3,7 +3,8 @@ import org.gradle.api.Project
fun Project.registerAdditionalLibraries( fun Project.registerAdditionalLibraries(
targetOs: OS, targetOs: OS,
targetArch: Arch, targetArch: Arch,
skikoProperties: SkikoProperties skikoProperties: SkikoProperties,
artifacts: SkikoArtifacts,
): List<AdditionalRuntimeLibrary> { ): List<AdditionalRuntimeLibrary> {
val angleTag = property("dependencies.angle") as String val angleTag = property("dependencies.angle") as String
return listOfNotNull( return listOfNotNull(
...@@ -12,6 +13,7 @@ fun Project.registerAdditionalLibraries( ...@@ -12,6 +13,7 @@ fun Project.registerAdditionalLibraries(
targetOs = targetOs, targetOs = targetOs,
targetArch = targetArch, targetArch = targetArch,
skikoProperties = skikoProperties, skikoProperties = skikoProperties,
artifacts = artifacts,
name = "angle", name = "angle",
archiveUrl = "https://github.com/JetBrains/angle-pack/releases/download/$angleTag/Angle-$angleTag-${targetOs.id}-Release-${targetArch.id}.zip", archiveUrl = "https://github.com/JetBrains/angle-pack/releases/download/$angleTag/Angle-$angleTag-${targetOs.id}-Release-${targetArch.id}.zip",
filesToInclude = listOf( filesToInclude = listOf(
......
import SkikoArtifacts.jvmAdditionalRuntimeArtifactIdFor
import de.undercouch.gradle.tasks.download.Download import de.undercouch.gradle.tasks.download.Download
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.DuplicatesStrategy
...@@ -27,6 +26,7 @@ fun Project.registerAdditionalRuntimeLibrary( ...@@ -27,6 +26,7 @@ fun Project.registerAdditionalRuntimeLibrary(
targetOs: OS, targetOs: OS,
targetArch: Arch, targetArch: Arch,
skikoProperties: SkikoProperties, skikoProperties: SkikoProperties,
artifacts: SkikoArtifacts,
name: String, name: String,
archiveUrl: String, archiveUrl: String,
filesToInclude: List<String>, filesToInclude: List<String>,
...@@ -70,7 +70,7 @@ fun Project.registerAdditionalRuntimeLibrary( ...@@ -70,7 +70,7 @@ fun Project.registerAdditionalRuntimeLibrary(
group = visibleName group = visibleName
dependsOn(unzipTask) dependsOn(unzipTask)
dependsOn(checksumTask) dependsOn(checksumTask)
archiveBaseName.set("skiko-$name") archiveBaseName.set("${artifacts.artifactIdPrefix}-$name")
archiveClassifier.set(targetId) archiveClassifier.set(targetId)
from(unzipTask) from(unzipTask)
from(checksumTask) from(checksumTask)
...@@ -85,8 +85,8 @@ fun Project.registerAdditionalRuntimeLibrary( ...@@ -85,8 +85,8 @@ fun Project.registerAdditionalRuntimeLibrary(
pomNameForPublication: MutableMap<String, String> pomNameForPublication: MutableMap<String, String>
) { ) {
container.create("skikoJvmRuntime$taskSuffix", MavenPublication::class.java) { container.create("skikoJvmRuntime$taskSuffix", MavenPublication::class.java) {
pomNameForPublication[this.name] = "Skiko $visibleName for ${targetOs.id} ${targetArch.id}" pomNameForPublication[this.name] = "${artifacts.displayName} $visibleName for ${targetOs.id} ${targetArch.id}"
artifactId = jvmAdditionalRuntimeArtifactIdFor(name, targetOs, targetArch) artifactId = artifacts.jvmAdditionalRuntimeArtifactIdFor(name, targetOs, targetArch)
afterEvaluate { afterEvaluate {
artifact(jarTask.map { it.archiveFile.get() }) artifact(jarTask.map { it.archiveFile.get() })
artifact(emptySourcesJar) artifact(emptySourcesJar)
......
...@@ -9,15 +9,26 @@ import org.gradle.kotlin.dsl.register ...@@ -9,15 +9,26 @@ import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.withType import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import java.io.File import java.io.File
import dsl.*
enum class SkikoModuleKind { CORE, EXTENSION }
class SkikoProjectContext( class SkikoProjectContext(
val project: Project, val project: Project,
val skiko: SkikoProperties, val skiko: SkikoProperties,
val kind: SkikoModuleKind,
val artifacts: SkikoArtifacts,
val kotlin: KotlinMultiplatformExtension, val kotlin: KotlinMultiplatformExtension,
val windowsSdkPathProvider: () -> WindowsSdkPaths, val windowsSdkPathProvider: () -> WindowsSdkPaths,
val createChecksumsTask: (OS, Arch, Provider<File>) -> TaskProvider<*>, val createChecksumsTask: (OS, Arch, Provider<File>) -> TaskProvider<*>,
val additionalRuntimeLibraries: List<AdditionalRuntimeLibrary>, val additionalRuntimeLibraries: List<AdditionalRuntimeLibrary>,
configureDependencies: (SkikoDependencyScope.() -> Unit)
) { ) {
val dependencyRegistry = BinaryRegistry()
init {
SkikoDependencyScope(dependencyRegistry).configureDependencies()
}
val buildType = skiko.buildType val buildType = skiko.buildType
...@@ -26,6 +37,51 @@ class SkikoProjectContext( ...@@ -26,6 +37,51 @@ class SkikoProjectContext(
} }
val allJvmRuntimeJars = mutableMapOf<Pair<OS, Arch>, TaskProvider<Jar>>() val allJvmRuntimeJars = mutableMapOf<Pair<OS, Arch>, TaskProvider<Jar>>()
val projectPath: String = if (kind == SkikoModuleKind.CORE) ":" else ":${project.name}"
val libBaseName: String = artifacts.artifactIdPrefix
val nativeBridgesLibPrefix: String = "${artifacts.artifactIdPrefix}-native-bridges"
val cinteropName: String = libBaseName
fun staticLibBaseNames(os: OS, arch: Arch, env: TargetEnv): List<String> =
dependencyRegistry.getLibs(os, arch, env, Linkage.STATIC)
fun directStaticLibBaseNames(os: OS, arch: Arch, env: TargetEnv): List<String> =
dependencyRegistry.getLibs(os, arch, env, Linkage.DIRECT_STATIC)
private fun archivePaths(os: OS, libBaseNames: List<String>, skiaBinDir: String): List<String> {
val prefix = if (os == OS.Windows) "" else "lib"
val suffix = when (os) {
OS.Windows -> ".lib"
OS.Wasm -> ".wasm.a"
else -> ".a"
}
return libBaseNames.map { baseName ->
"$skiaBinDir/$prefix$baseName$suffix"
}
}
fun staticArchivePaths(os: OS, arch: Arch, env: TargetEnv, skiaBinDir: String): List<String> =
archivePaths(os, staticLibBaseNames(os, arch, env), skiaBinDir)
fun directStaticArchivePaths(os: OS, arch: Arch, env: TargetEnv, skiaBinDir: String): List<String> =
archivePaths(os, directStaticLibBaseNames(os, arch, env), skiaBinDir)
fun dynamicLibNames(os: OS, arch: Arch, env: TargetEnv): List<String> =
dependencyRegistry.getLibs(os, arch, env, Linkage.DYNAMIC)
fun resolveBinaryInputs(os: OS, arch: Arch, env: TargetEnv, skiaBinDir: String): ResolvedBinaryConfiguration {
return ResolvedBinaryConfiguration(
staticLibBaseNames = staticLibBaseNames(os, arch, env),
staticArchivePaths = staticArchivePaths(os, arch, env, skiaBinDir),
directStaticLibBaseNames = directStaticLibBaseNames(os, arch, env),
directStaticArchivePaths = directStaticArchivePaths(os, arch, env, skiaBinDir),
dynamicLibNames = dynamicLibNames(os, arch, env),
frameworks = dependencyRegistry.getFrameworks(os, arch, env),
linkFlags = dependencyRegistry.getLinkFlags(os, arch, env)
)
}
} }
fun SkikoProjectContext.declareSkiaTasks() { fun SkikoProjectContext.declareSkiaTasks() {
...@@ -70,8 +126,19 @@ fun SkikoProjectContext.declareSkiaTasks() { ...@@ -70,8 +126,19 @@ fun SkikoProjectContext.declareSkiaTasks() {
description = "unzips to $outputDir" description = "unzips to $outputDir"
dependsOn(downloadSkiaTask) dependsOn(downloadSkiaTask)
from(project.zipTree(downloadSkiaTask.get().dest)) from(project.zipTree(downloadSkiaTask.get().dest)) {
// On Windows, some of the skia libraries have lib prefix and some do not
// Let's standardize it
if (config == "windows") {
rename { name ->
if (name.startsWith("lib") && name.endsWith(".lib")) {
name.removePrefix("lib")
} else {
name
}
}
}
}
into(outputDir) into(outputDir)
} }
} }
......
package dsl
import Arch
import OS
enum class TargetEnv { JVM, NATIVE, WASM }
enum class Linkage { STATIC, DIRECT_STATIC, DYNAMIC }
data class ResolvedBinaryConfiguration(
val staticLibBaseNames: List<String> = emptyList(),
val staticArchivePaths: List<String> = emptyList(),
val directStaticLibBaseNames: List<String> = emptyList(),
val directStaticArchivePaths: List<String> = emptyList(),
val dynamicLibNames: List<String> = emptyList(),
val frameworks: List<String> = emptyList(),
val linkFlags: List<String> = emptyList(),
)
@DslMarker
annotation class SkikoBinaryDsl
val OS.validEnvs: List<TargetEnv>
get() = when (this) {
OS.MacOS, OS.Linux -> listOf(TargetEnv.JVM, TargetEnv.NATIVE)
OS.Windows, OS.Android -> listOf(TargetEnv.JVM)
OS.IOS, OS.TVOS -> listOf(TargetEnv.NATIVE)
OS.Wasm -> listOf(TargetEnv.WASM)
}
class BinaryRegistry {
private enum class RuleKind {
STATIC_LIBS,
DIRECT_STATIC_LIBS,
DYNAMIC_LIBS,
LINK_FLAGS,
FRAMEWORKS,
}
private data class Rule(
val os: OS,
val arch: Arch?,
val env: TargetEnv,
val kind: RuleKind,
val values: List<String>,
) {
fun matches(os: OS, arch: Arch, env: TargetEnv, kind: RuleKind): Boolean =
this.os == os && (this.arch == null || this.arch == arch) && this.env == env && this.kind == kind
}
private val rules = mutableListOf<Rule>()
fun on(
os: OS,
env: TargetEnv,
linkage: Linkage,
vararg libs: String,
arch: Arch? = null
) {
rules.add(Rule(os, arch, env, linkage.ruleKind, libs.toList()))
}
fun linkFlags(os: OS, env: TargetEnv, vararg flags: String, arch: Arch? = null) {
rules.add(Rule(os, arch, env, RuleKind.LINK_FLAGS, flags.toList()))
}
fun frameworks(os: OS, env: TargetEnv, vararg frameworks: String, arch: Arch? = null) {
rules.add(Rule(os, arch, env, RuleKind.FRAMEWORKS, frameworks.toList()))
}
fun getLibs(os: OS, arch: Arch, env: TargetEnv, linkage: Linkage): List<String> =
valuesFor(os, arch, env, linkage.ruleKind)
fun getLinkFlags(os: OS, arch: Arch, env: TargetEnv): List<String> =
valuesFor(os, arch, env, RuleKind.LINK_FLAGS)
fun getFrameworks(os: OS, arch: Arch, env: TargetEnv): List<String> =
valuesFor(os, arch, env, RuleKind.FRAMEWORKS).flatMap { listOf("-framework", it) }
private fun valuesFor(os: OS, arch: Arch, env: TargetEnv, kind: RuleKind): List<String> =
rules
.filter { it.matches(os, arch, env, kind) }
.flatMap { it.values }
private val Linkage.ruleKind: RuleKind
get() = when (this) {
Linkage.STATIC -> RuleKind.STATIC_LIBS
Linkage.DIRECT_STATIC -> RuleKind.DIRECT_STATIC_LIBS
Linkage.DYNAMIC -> RuleKind.DYNAMIC_LIBS
}
}
@SkikoBinaryDsl
interface ActionScope {
fun staticSkiaLibs(vararg libs: String)
fun directStaticSkiaLibs(vararg libs: String)
fun dynamicSystemLibs(vararg libs: String)
fun linkFlags(vararg flags: String)
fun frameworks(vararg frameworks: String)
}
@SkikoBinaryDsl
class ArchScope internal constructor(
private val registry: BinaryRegistry,
private val env: TargetEnv,
private val os: OS,
private val arch: Arch
) : ActionScope {
override fun staticSkiaLibs(vararg libs: String) =
registry.on(os, env, Linkage.STATIC, *libs, arch = arch)
override fun directStaticSkiaLibs(vararg libs: String) =
registry.on(os, env, Linkage.DIRECT_STATIC, *libs, arch = arch)
override fun dynamicSystemLibs(vararg libs: String) =
registry.on(os, env, Linkage.DYNAMIC, *libs, arch = arch)
override fun linkFlags(vararg flags: String) =
registry.linkFlags(os, env, *flags, arch = arch)
override fun frameworks(vararg frameworks: String) =
registry.frameworks(os, env, *frameworks, arch = arch)
}
@SkikoBinaryDsl
class OSScope internal constructor(
private val registry: BinaryRegistry,
private val env: TargetEnv,
private val os: OS
) : ActionScope {
fun x64(configure: ArchScope.() -> Unit) = ArchScope(registry, env, os, Arch.X64).configure()
fun arm64(configure: ArchScope.() -> Unit) = ArchScope(registry, env, os, Arch.Arm64).configure()
fun wasm(configure: ArchScope.() -> Unit) = ArchScope(registry, env, os, Arch.Wasm).configure()
override fun staticSkiaLibs(vararg libs: String) =
registry.on(os, env, Linkage.STATIC, *libs, arch = null)
override fun directStaticSkiaLibs(vararg libs: String) =
registry.on(os, env, Linkage.DIRECT_STATIC, *libs, arch = null)
override fun dynamicSystemLibs(vararg libs: String) =
registry.on(os, env, Linkage.DYNAMIC, *libs, arch = null)
override fun linkFlags(vararg flags: String) =
registry.linkFlags(os, env, *flags, arch = null)
override fun frameworks(vararg frameworks: String) =
registry.frameworks(os, env, *frameworks, arch = null)
}
@SkikoBinaryDsl
class EnvScope internal constructor(
private val registry: BinaryRegistry,
private val env: TargetEnv
) : ActionScope {
// Silently ignores combinations that don't make sense (e.g., jvm { ios { } })
fun linux(configure: OSScope.() -> Unit) = applyIfValid(OS.Linux, configure)
fun macos(configure: OSScope.() -> Unit) = applyIfValid(OS.MacOS, configure)
fun windows(configure: OSScope.() -> Unit) = applyIfValid(OS.Windows, configure)
fun android(configure: OSScope.() -> Unit) = applyIfValid(OS.Android, configure)
fun ios(configure: OSScope.() -> Unit) = applyIfValid(OS.IOS, configure)
fun tvos(configure: OSScope.() -> Unit) = applyIfValid(OS.TVOS, configure)
fun wasm(configure: OSScope.() -> Unit) = applyIfValid(OS.Wasm, configure)
private fun applyIfValid(os: OS, configure: OSScope.() -> Unit) {
if (env in os.validEnvs) {
OSScope(registry, env, os).configure()
}
}
private fun forEachValidOs(action: (OS) -> Unit) {
OS.values().filter { env in it.validEnvs }.forEach(action)
}
// ActionScope implementation (applies to all valid OSs for this Env, arch = null)
override fun staticSkiaLibs(vararg libs: String) = forEachValidOs { os ->
registry.on(os, env, Linkage.STATIC, *libs, arch = null)
}
override fun directStaticSkiaLibs(vararg libs: String) = forEachValidOs { os ->
registry.on(os, env, Linkage.DIRECT_STATIC, *libs, arch = null)
}
override fun dynamicSystemLibs(vararg libs: String) = forEachValidOs { os ->
registry.on(os, env, Linkage.DYNAMIC, *libs, arch = null)
}
override fun linkFlags(vararg flags: String) = forEachValidOs { os ->
registry.linkFlags(os, env, *flags, arch = null)
}
override fun frameworks(vararg frameworks: String) = forEachValidOs { os ->
registry.frameworks(os, env, *frameworks, arch = null)
}
}
@SkikoBinaryDsl
class TargetScope internal constructor(
private val registry: BinaryRegistry
) {
fun all(configure: EnvScope.() -> Unit) {
TargetEnv.values().forEach { env -> EnvScope(registry, env).configure() }
}
fun jvm(configure: EnvScope.() -> Unit) = EnvScope(registry, TargetEnv.JVM).configure()
fun native(configure: EnvScope.() -> Unit) = EnvScope(registry, TargetEnv.NATIVE).configure()
fun wasm(configure: EnvScope.() -> Unit) = EnvScope(registry, TargetEnv.WASM).configure()
}
@SkikoBinaryDsl
class SkikoDependencyScope internal constructor(
private val registry: BinaryRegistry
) {
internal var dependsOnCore: Boolean = false
private set
fun dependsOnCore() {
dependsOnCore = true
}
fun targets(configure: TargetScope.() -> Unit) {
TargetScope(registry).configure()
}
fun binary(configure: BinaryRegistry.() -> Unit) {
registry.configure()
}
}
...@@ -270,29 +270,37 @@ object SkikoGradleProperties { ...@@ -270,29 +270,37 @@ object SkikoGradleProperties {
const val NATIVE_LINUX = "skiko.native.linux.enabled" const val NATIVE_LINUX = "skiko.native.linux.enabled"
} }
object SkikoArtifacts { class SkikoArtifacts(
val groupId = "org.jetbrains.skiko" val artifactIdPrefix: String = DEFAULT_ARTIFACT_ID_PREFIX,
val displayName: String = "Skiko",
val pomDescription: String = "Kotlin Skia bindings",
) {
// names are also used in samples, e.g. samples/SkijaInjectSample/build.gradle // names are also used in samples, e.g. samples/SkijaInjectSample/build.gradle
val commonArtifactId = "skiko" val commonArtifactId = artifactIdPrefix
val jvmArtifactId = "skiko-awt" val jvmArtifactId = "$artifactIdPrefix-awt"
val jvmRuntimeArtifactId = "skiko-awt-runtime" val jvmRuntimeArtifactId = "$artifactIdPrefix-awt-runtime"
// an artifact (klib) for k/js targets // an artifact (klib) for k/js targets
val jsArtifactId = "skiko-js" val jsArtifactId = "$artifactIdPrefix-js"
// an artifact (klib) for k/wasm targets // an artifact (klib) for k/wasm targets
val wasmArtifactId = "skiko-wasm-js" val wasmArtifactId = "$artifactIdPrefix-wasm-js"
// an artifact with skiko.wasm and supporting js code - jar // an artifact with skiko.wasm and supporting js code - jar
val jsWasmArtifactId = "skiko-js-wasm-runtime" val jsWasmArtifactId = "$artifactIdPrefix-js-wasm-runtime"
fun jvmRuntimeArtifactIdFor(os: OS, arch: Arch) = fun jvmRuntimeArtifactIdFor(os: OS, arch: Arch) =
if (os == OS.Android) if (os == OS.Android)
"skiko-android-runtime-${arch.id}" "$artifactIdPrefix-android-runtime-${arch.id}"
else else
"skiko-awt-runtime-${targetId(os, arch)}" "$artifactIdPrefix-awt-runtime-${targetId(os, arch)}"
fun jvmAdditionalRuntimeArtifactIdFor(name: String, os: OS, arch: Arch) = fun jvmAdditionalRuntimeArtifactIdFor(name: String, os: OS, arch: Arch) =
"skiko-awt-runtime-$name-${os.id}-${arch.id}" "$artifactIdPrefix-awt-runtime-$name-${os.id}-${arch.id}"
// Using custom name like skiko-<Os>-<Arch> (with a dash) // Using a custom name like skiko-<Os>-<Arch> (with a dash)
// does not seem possible (at least without adding a dash to a target's tasks), // does not seem possible (at least without adding a dash to a target's tasks),
// so we're using the default naming pattern instead. // so we're using the default naming pattern instead.
// See https://youtrack.jetbrains.com/issue/KT-50001. // See https://youtrack.jetbrains.com/issue/KT-50001.
fun nativeArtifactIdFor(os: OS, arch: Arch, isUikitSim: Boolean = false) = fun nativeArtifactIdFor(os: OS, arch: Arch, isUikitSim: Boolean = false) =
"skiko-${os.id + if (isUikitSim) "simulator" else ""}${arch.id}" "$artifactIdPrefix-${os.id + if (isUikitSim) "simulator" else ""}${arch.id}"
companion object {
const val DEFAULT_ARTIFACT_ID_PREFIX = "skiko"
const val DEFAULT_GROUP_ID = "org.jetbrains.skiko"
}
} }
...@@ -30,6 +30,7 @@ private class SkikoPublishingContext( ...@@ -30,6 +30,7 @@ private class SkikoPublishingContext(
val project = projectContext.project val project = projectContext.project
val kotlin = projectContext.kotlin val kotlin = projectContext.kotlin
val skiko = projectContext.skiko val skiko = projectContext.skiko
val skikoArtifacts = projectContext.artifacts
val additionalRuntimeLibraries = projectContext.additionalRuntimeLibraries val additionalRuntimeLibraries = projectContext.additionalRuntimeLibraries
val pomNameForPublication: MutableMap<String, String> = HashMap() val pomNameForPublication: MutableMap<String, String> = HashMap()
...@@ -94,21 +95,21 @@ private fun SkikoPublishingContext.configurePublishingRepositories() { ...@@ -94,21 +95,21 @@ private fun SkikoPublishingContext.configurePublishingRepositories() {
} }
private fun SkikoPublishingContext.configurePublicationDefaults() { private fun SkikoPublishingContext.configurePublicationDefaults() {
pomNameForPublication["kotlinMultiplatform"] = "Skiko KMP" pomNameForPublication["kotlinMultiplatform"] = "${skikoArtifacts.displayName} KMP"
kotlin.targets.forEach { kotlin.targets.forEach {
pomNameForPublication[it.name] = "Skiko ${toTitleCase(it.name)}" pomNameForPublication[it.name] = "${skikoArtifacts.displayName} ${toTitleCase(it.name)}"
} }
publishing { publishing {
publications.configureEach { publications.configureEach {
this as MavenPublication this as MavenPublication
groupId = SkikoArtifacts.groupId groupId = SkikoArtifacts.DEFAULT_GROUP_ID
// Necessary for publishing to Maven Central // Necessary for publishing to Maven Central
artifact(emptyJavadocJar) artifact(emptyJavadocJar)
pom { pom {
description.set("Kotlin Skia bindings") description.set(skikoArtifacts.pomDescription)
licenses { licenses {
license { license {
name.set("The Apache License, Version 2.0") name.set("The Apache License, Version 2.0")
...@@ -140,8 +141,8 @@ private fun SkikoPublishingContext.configureAllJvmRuntimeJarPublications() = pub ...@@ -140,8 +141,8 @@ private fun SkikoPublishingContext.configureAllJvmRuntimeJarPublications() = pub
val os = entry.key.first val os = entry.key.first
val arch = entry.key.second val arch = entry.key.second
create("skikoJvmRuntime${toTitleCase(os.id)}${toTitleCase(arch.id)}", MavenPublication::class.java) { create("skikoJvmRuntime${toTitleCase(os.id)}${toTitleCase(arch.id)}", MavenPublication::class.java) {
pomNameForPublication[name] = "Skiko JVM Runtime for ${os.name} ${arch.name}" pomNameForPublication[name] = "${skikoArtifacts.displayName} JVM Runtime for ${os.name} ${arch.name}"
artifactId = SkikoArtifacts.jvmRuntimeArtifactIdFor(os, arch) artifactId = skikoArtifacts.jvmRuntimeArtifactIdFor(os, arch)
project.afterEvaluate { project.afterEvaluate {
artifact(entry.value.map { it.archiveFile.get() }) artifact(entry.value.map { it.archiveFile.get() })
artifact(emptySourcesJar) artifact(emptySourcesJar)
...@@ -149,8 +150,8 @@ private fun SkikoPublishingContext.configureAllJvmRuntimeJarPublications() = pub ...@@ -149,8 +150,8 @@ private fun SkikoPublishingContext.configureAllJvmRuntimeJarPublications() = pub
pom.withXml { pom.withXml {
asNode().appendNode("dependencies") asNode().appendNode("dependencies")
.appendNode("dependency").apply { .appendNode("dependency").apply {
appendNode("groupId", SkikoArtifacts.groupId) appendNode("groupId", SkikoArtifacts.DEFAULT_GROUP_ID)
appendNode("artifactId", SkikoArtifacts.jvmArtifactId) appendNode("artifactId", skikoArtifacts.jvmArtifactId)
appendNode("version", "[${skiko.deployVersion}]") appendNode("version", "[${skiko.deployVersion}]")
appendNode("scope", "compile") appendNode("scope", "compile")
} }
...@@ -235,8 +236,8 @@ private fun SkikoPublishingContext.configureAwtRuntimeJarPublication() { ...@@ -235,8 +236,8 @@ private fun SkikoPublishingContext.configureAwtRuntimeJarPublication() {
*/ */
dependencies.add( dependencies.add(
project.dependencies.create( project.dependencies.create(
SkikoArtifacts.groupId, SkikoArtifacts.DEFAULT_GROUP_ID,
SkikoArtifacts.jvmRuntimeArtifactIdFor(os, arch), skikoArtifacts.jvmRuntimeArtifactIdFor(os, arch),
skiko.deployVersion skiko.deployVersion
) )
) )
...@@ -255,9 +256,9 @@ private fun SkikoPublishingContext.configureAwtRuntimeJarPublication() { ...@@ -255,9 +256,9 @@ private fun SkikoPublishingContext.configureAwtRuntimeJarPublication() {
publications { publications {
create("awtRuntimeElements", MavenPublication::class.java) { create("awtRuntimeElements", MavenPublication::class.java) {
from(component) from(component)
pomNameForPublication[name] = "Skiko JVM Runtime" pomNameForPublication[name] = "${skikoArtifacts.displayName} JVM Runtime"
groupId = SkikoArtifacts.groupId groupId = SkikoArtifacts.DEFAULT_GROUP_ID
artifactId = SkikoArtifacts.jvmRuntimeArtifactId artifactId = skikoArtifacts.jvmRuntimeArtifactId
version = skiko.deployVersion version = skiko.deployVersion
/* /*
...@@ -295,7 +296,7 @@ private fun SkikoPublishingContext.configureAwtPublicationConstraints() { ...@@ -295,7 +296,7 @@ private fun SkikoPublishingContext.configureAwtPublicationConstraints() {
// Add constraint for the uber runtime artifact // Add constraint for the uber runtime artifact
config.dependencyConstraints.add( config.dependencyConstraints.add(
project.dependencies.constraints.create( project.dependencies.constraints.create(
"${SkikoArtifacts.groupId}:${SkikoArtifacts.jvmRuntimeArtifactId}:${skiko.deployVersion}!!" "${SkikoArtifacts.DEFAULT_GROUP_ID}:${skikoArtifacts.jvmRuntimeArtifactId}:${skiko.deployVersion}!!"
) )
) )
...@@ -303,7 +304,7 @@ private fun SkikoPublishingContext.configureAwtPublicationConstraints() { ...@@ -303,7 +304,7 @@ private fun SkikoPublishingContext.configureAwtPublicationConstraints() {
awtRuntimeTargets.forEach { (os, arch) -> awtRuntimeTargets.forEach { (os, arch) ->
config.dependencyConstraints.add( config.dependencyConstraints.add(
project.dependencies.constraints.create( project.dependencies.constraints.create(
"${SkikoArtifacts.groupId}:${SkikoArtifacts.jvmRuntimeArtifactIdFor(os, arch)}:${skiko.deployVersion}!!" "${SkikoArtifacts.DEFAULT_GROUP_ID}:${skikoArtifacts.jvmRuntimeArtifactIdFor(os, arch)}:${skiko.deployVersion}!!"
) )
) )
} }
...@@ -320,8 +321,8 @@ private fun SkikoPublishingContext.configureAdditionalRuntimeLibrariesPublicatio ...@@ -320,8 +321,8 @@ private fun SkikoPublishingContext.configureAdditionalRuntimeLibrariesPublicatio
private fun SkikoPublishingContext.configureWebPublication() = publications { private fun SkikoPublishingContext.configureWebPublication() = publications {
if (!project.supportWeb) return@publications if (!project.supportWeb) return@publications
create("skikoWasmRuntime", MavenPublication::class.java) { create("skikoWasmRuntime", MavenPublication::class.java) {
pomNameForPublication[name] = "Skiko WASM Runtime" pomNameForPublication[name] = "${skikoArtifacts.displayName} WASM Runtime"
artifactId = SkikoArtifacts.jsWasmArtifactId artifactId = skikoArtifacts.jsWasmArtifactId
artifact(project.tasks.named("skikoWasmJar").get()) artifact(project.tasks.named("skikoWasmJar").get())
artifact(emptySourcesJar) artifact(emptySourcesJar)
} }
...@@ -329,7 +330,7 @@ private fun SkikoPublishingContext.configureWebPublication() = publications { ...@@ -329,7 +330,7 @@ private fun SkikoPublishingContext.configureWebPublication() = publications {
private fun SkikoPublishingContext.configureAndroidPublication() = publications { private fun SkikoPublishingContext.configureAndroidPublication() = publications {
if (!project.supportAndroid) return@publications if (!project.supportAndroid) return@publications
pomNameForPublication["androidRelease"] = "Skiko Android Runtime" pomNameForPublication["androidRelease"] = "${skikoArtifacts.displayName} Android Runtime"
} }
private fun SkikoPublishingContext.configurePomNames() = publications { private fun SkikoPublishingContext.configurePomNames() = publications {
......
...@@ -9,6 +9,7 @@ import OS ...@@ -9,6 +9,7 @@ import OS
import SealAndSignSharedLibraryTask import SealAndSignSharedLibraryTask
import SkiaBuildType import SkiaBuildType
import SkikoProjectContext import SkikoProjectContext
import dsl.TargetEnv
import compilerForTarget import compilerForTarget
import dynamicLibExt import dynamicLibExt
import hostArch import hostArch
...@@ -135,7 +136,7 @@ fun SkikoProjectContext.createCompileJvmBindingsTask( ...@@ -135,7 +136,7 @@ fun SkikoProjectContext.createCompileJvmBindingsTask(
flags.set( flags.set(
listOf( listOf(
*skiaPreprocessorFlags(targetOs, buildType), *skiaPreprocessorFlags(targetOs, buildType),
*osFlags *osFlags,
) )
) )
} }
...@@ -229,26 +230,16 @@ fun SkikoProjectContext.createLinkJvmBindings( ...@@ -229,26 +230,16 @@ fun SkikoProjectContext.createLinkJvmBindings(
val target = targetId(targetOs, targetArch) val target = targetId(targetOs, targetArch)
val skiaBinSubdir = "out/${buildType.id}-$target" val skiaBinSubdir = "out/${buildType.id}-$target"
val skiaBinDir = skiaJvmBindingsDir.get().absolutePath + "/" + skiaBinSubdir val skiaBinDir = skiaJvmBindingsDir.get().absolutePath + "/" + skiaBinSubdir
val resolvedBinaryInputs = resolveBinaryInputs(targetOs, targetArch, TargetEnv.JVM, skiaBinDir)
val osFlags: Array<String> val osFlags: Array<String>
libFiles = project.fileTree(skiaJvmBindingsDir.map { it.resolve(skiaBinSubdir) }) { libFiles = project.files((resolvedBinaryInputs.staticArchivePaths).distinct())
val fileExtension = if (targetOs.isWindows) "lib" else "a"
val filePrefix = if (targetOs.isWindows) "" else "lib"
val excludedLibs = listOf(
"skia_graphite_ext",
"skia_graphite_dawn_ext",
"dawn_combined"
)
include("*.$fileExtension")
exclude(excludedLibs.map { "$filePrefix$it.$fileExtension" })
}
dependsOn(compileTask) dependsOn(compileTask)
objectFiles = project.fileTree(compileTask.map { it.outDir.get() }) { objectFiles = project.fileTree(compileTask.map { it.outDir.get() }) {
include("**/*.o") include("**/*.o")
} }
val libNamePrefix = if (targetOs.isWindows) "skiko" else "libskiko" val libNamePrefix = if (targetOs.isWindows) libBaseName else "lib$libBaseName"
libOutputFileName.set("$libNamePrefix-${targetOs.id}-${targetArch.id}${targetOs.dynamicLibExt}") libOutputFileName.set("$libNamePrefix-${targetOs.id}-${targetArch.id}${targetOs.dynamicLibExt}")
buildTargetOS.set(targetOs) buildTargetOS.set(targetOs)
buildSuffix.set("jvm") buildSuffix.set("jvm")
...@@ -267,19 +258,10 @@ fun SkikoProjectContext.createLinkJvmBindings( ...@@ -267,19 +258,10 @@ fun SkikoProjectContext.createLinkJvmBindings(
"-arch", if (targetArch == Arch.Arm64) "arm64" else "x86_64", "-arch", if (targetArch == Arch.Arm64) "arm64" else "x86_64",
"-shared", "-shared",
"-dead_strip", "-dead_strip",
"-lobjc",
"-install_name", "./${libOutputFileName.get()}", "-install_name", "./${libOutputFileName.get()}",
"-current_version", skiko.planeDeployVersion, "-current_version", skiko.planeDeployVersion,
"-framework", "AppKit", *resolvedBinaryInputs.linkFlags.toTypedArray(),
"-framework", "CoreFoundation", *resolvedBinaryInputs.frameworks.toTypedArray()
"-framework", "CoreGraphics",
"-framework", "CoreServices",
"-framework", "CoreText",
"-framework", "Foundation",
"-framework", "IOKit",
"-framework", "Metal",
"-framework", "OpenGL",
"-framework", "QuartzCore" // for CoreAnimation
) )
} }
OS.Linux -> { OS.Linux -> {
...@@ -290,25 +272,14 @@ fun SkikoProjectContext.createLinkJvmBindings( ...@@ -290,25 +272,14 @@ fun SkikoProjectContext.createLinkJvmBindings(
// `libstdc++.so.6.*` binaries are forward-compatible and used from GCC 3.4 to 16+, // `libstdc++.so.6.*` binaries are forward-compatible and used from GCC 3.4 to 16+,
// so do not use `-static-libstdc++` to avoid issues with complex setup. // so do not use `-static-libstdc++` to avoid issues with complex setup.
"-static-libgcc", "-static-libgcc",
"-lGL",
"-lX11",
"-lfontconfig",
// Enforce immediate symbol resolution at library load time to prevent // Enforce immediate symbol resolution at library load time to prevent
// lazy-binding issues and make GOT read-only afterwards. // lazy-binding issues and make GOT read-only afterwards.
"-Wl,-z,relro,-z,now", "-Wl,-z,relro,-z,now",
// Hack to fix problem with linker not always finding certain declarations.
"$skiaBinDir/libsksg.a",
"$skiaBinDir/libskia.a",
"$skiaBinDir/libskia_ganesh_ext.a",
"$skiaBinDir/libskunicode_core.a",
"$skiaBinDir/libskunicode_icu.a",
"$skiaBinDir/libskshaper.a",
"$skiaBinDir/libjsonreader.a"
) )
) )
if (targetArch == Arch.Arm64) { addAll(resolvedBinaryInputs.dynamicLibNames.map { "-l$it" })
add("-lEGL") addAll(resolvedBinaryInputs.directStaticArchivePaths)
} addAll(resolvedBinaryInputs.linkFlags)
}.toTypedArray() }.toTypedArray()
} }
OS.Windows -> { OS.Windows -> {
...@@ -337,21 +308,22 @@ fun SkikoProjectContext.createLinkJvmBindings( ...@@ -337,21 +308,22 @@ fun SkikoProjectContext.createLinkJvmBindings(
) )
) )
if (buildType == SkiaBuildType.DEBUG) add("dxgi.lib") if (buildType == SkiaBuildType.DEBUG) add("dxgi.lib")
addAll(resolvedBinaryInputs.dynamicLibNames.map { "$it.lib" })
addAll(resolvedBinaryInputs.linkFlags)
}.toTypedArray() }.toTypedArray()
} }
OS.Android -> { OS.Android -> {
osFlags = arrayOf( val androidFlags = mutableListOf(
"-shared", "-shared",
"-static-libstdc++", "-static-libstdc++",
"-lGLESv3",
"-lEGL",
"-llog", "-llog",
"-landroid", "-landroid",
"-latomic", "-latomic",
// Hack to fix problem with linker not always finding certain declarations.
"$skiaBinDir/libskia.a",
"$skiaBinDir/libskia_ganesh_ext.a"
) )
androidFlags.addAll(resolvedBinaryInputs.dynamicLibNames.map { "-l$it" })
androidFlags.addAll(resolvedBinaryInputs.directStaticArchivePaths)
androidFlags.addAll(resolvedBinaryInputs.linkFlags)
osFlags = androidFlags.toTypedArray()
linker.set(project.androidClangFor(targetArch)) linker.set(project.androidClangFor(targetArch))
} }
OS.Wasm, OS.IOS, OS.TVOS -> { OS.Wasm, OS.IOS, OS.TVOS -> {
......
...@@ -8,10 +8,10 @@ import SkiaBuildType ...@@ -8,10 +8,10 @@ import SkiaBuildType
import SkikoProjectContext import SkikoProjectContext
import WriteCInteropDefFile import WriteCInteropDefFile
import compilerForTarget import compilerForTarget
import dsl.TargetEnv
import hostArch import hostArch
import isCompatibleWithHost import isCompatibleWithHost
import joinToTitleCamelCase import joinToTitleCamelCase
import listOfFrameworks
import mutableListOfLinkerOptions import mutableListOfLinkerOptions
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.Project import org.gradle.api.Project
...@@ -176,7 +176,7 @@ fun configureCinterop( ...@@ -176,7 +176,7 @@ fun configureCinterop(
val taskNameSuffix = joinToTitleCamelCase(os.idWithSuffix(isUikitSim = target.isUikitSimulator()), arch.id) val taskNameSuffix = joinToTitleCamelCase(os.idWithSuffix(isUikitSim = target.isUikitSimulator()), arch.id)
val writeCInteropDef = tasks.register("writeCInteropDef$taskNameSuffix", WriteCInteropDefFile::class.java) { val writeCInteropDef = tasks.register("writeCInteropDef$taskNameSuffix", WriteCInteropDefFile::class.java) {
this.linkerOpts.set(linkerOpts) this.linkerOpts.set(linkerOpts)
outputFile.set(project.layout.buildDirectory.file("cinterop/$targetString/skiko.def")) outputFile.set(project.layout.buildDirectory.file("cinterop/$targetString/$cinteropName.def"))
} }
tasks.withType(CInteropProcess::class.java).configureEach { tasks.withType(CInteropProcess::class.java).configureEach {
if (konanTarget == target.konanTarget) { if (konanTarget == target.konanTarget) {
...@@ -190,35 +190,6 @@ fun configureCinterop( ...@@ -190,35 +190,6 @@ fun configureCinterop(
} }
} }
fun skiaStaticLibraries(skiaDir: String, targetString: String, buildType: SkiaBuildType): List<String> {
val skiaBinSubdir = "$skiaDir/out/${buildType.id}-$targetString"
return listOf(
"libskresources.a",
"libskparagraph.a",
"libskia.a",
"libskia_ganesh_ext.a",
"libicu.a",
"libjsonreader.a",
"libskottie.a",
"libsvg.a",
"libpng.a",
"libwebp_sse41.a",
"libsksg.a",
"libskunicode_core.a",
"libskunicode_icu.a",
"libwebp.a",
"libdng_sdk.a",
"libpiex.a",
"libharfbuzz.a",
"libexpat.a",
"libzlib.a",
"libjpeg.a",
"libskshaper.a"
).map {
"$skiaBinSubdir/$it"
}
}
fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: KotlinNativeTarget) = with(this.project) { fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: KotlinNativeTarget) = with(this.project) {
if (!os.isCompatibleWithHost) return if (!os.isCompatibleWithHost) return
...@@ -231,7 +202,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin ...@@ -231,7 +202,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
val unpackedSkia = unzipper.get() val unpackedSkia = unzipper.get()
val skiaDir = unpackedSkia.absolutePath val skiaDir = unpackedSkia.absolutePath
val bridgesLibrary = layout.buildDirectory.file("nativeBridges/static/$targetString/skiko-native-bridges-$targetString.a") val bridgesLibrary = layout.buildDirectory.file("nativeBridges/static/$targetString/$nativeBridgesLibPrefix-$targetString.a")
val bridgesLibraryPath = bridgesLibrary.get().asFile.absolutePath val bridgesLibraryPath = bridgesLibrary.get().asFile.absolutePath
// For iOS/tvOS we patch every library so that public Skia symbols are // For iOS/tvOS we patch every library so that public Skia symbols are
...@@ -242,54 +213,42 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin ...@@ -242,54 +213,42 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
val requiresSymbolPatching = os == OS.IOS || os == OS.TVOS val requiresSymbolPatching = os == OS.IOS || os == OS.TVOS
val patchedLibsDir = layout.buildDirectory.dir("nativeBridges/patched/$targetString").get().asFile val patchedLibsDir = layout.buildDirectory.dir("nativeBridges/patched/$targetString").get().asFile
val skiaBinDir = "$skiaDir/out/${buildType.id}-$targetString"
val resolvedBinaryInputs = resolveBinaryInputs(os, arch, TargetEnv.NATIVE, skiaBinDir)
val nativeArchives = resolvedBinaryInputs.staticArchivePaths.distinct()
val allLibraries = if (requiresSymbolPatching) { val allLibraries = if (requiresSymbolPatching) {
skiaStaticLibraries(skiaDir, targetString, buildType).map { lib -> nativeArchives.map { lib ->
"${patchedLibsDir.absolutePath}/${File(lib).name}" "${patchedLibsDir.absolutePath}/${File(lib).name}"
} + "${patchedLibsDir.absolutePath}/skiko-native-bridges-$targetString.a" } + "${patchedLibsDir.absolutePath}/$nativeBridgesLibPrefix-$targetString.a"
} else { } else {
skiaStaticLibraries(skiaDir, targetString, buildType) + bridgesLibraryPath nativeArchives + bridgesLibraryPath
} }
val skiaBinDir = "$skiaDir/out/${buildType.id}-$targetString"
val linkerFlags = when (os) { val linkerFlags = when (os) {
OS.MacOS -> { OS.MacOS -> {
val macFrameworks = listOfFrameworks("Metal", "CoreGraphics", "CoreText", "CoreServices") configureCinterop(cinteropName, os, arch, target, targetString, resolvedBinaryInputs.frameworks)
configureCinterop("skiko", os, arch, target, targetString, macFrameworks) mutableListOfLinkerOptions(resolvedBinaryInputs.frameworks + resolvedBinaryInputs.linkFlags)
mutableListOfLinkerOptions(macFrameworks)
} }
OS.IOS -> { OS.IOS -> {
val iosFrameworks = listOfFrameworks("Metal", "CoreGraphics", "CoreText", "UIKit")
// list of linker options to be included into klib, which are needed for skiko consumers // list of linker options to be included into klib, which are needed for skiko consumers
// https://github.com/JetBrains/compose-multiplatform/issues/3178 // https://github.com/JetBrains/compose-multiplatform/issues/3178
// Important! Removing or renaming cinterop-uikit publication might cause compile error // Important! Removing or renaming cinterop-uikit publication might cause compile error
// for projects depending on older Compose/Skiko transitively https://youtrack.jetbrains.com/issue/KT-60399 // for projects depending on older Compose/Skiko transitively https://youtrack.jetbrains.com/issue/KT-60399
configureCinterop("uikit", os, arch, target, targetString, iosFrameworks) configureCinterop("uikit", os, arch, target, targetString, resolvedBinaryInputs.frameworks)
mutableListOfLinkerOptions(iosFrameworks) mutableListOfLinkerOptions(resolvedBinaryInputs.frameworks + resolvedBinaryInputs.linkFlags)
} }
OS.TVOS -> { OS.TVOS -> {
val tvosFrameworks = listOfFrameworks("Metal", "CoreGraphics", "CoreText", "UIKit") configureCinterop("uikit", os, arch, target, targetString, resolvedBinaryInputs.frameworks)
configureCinterop("uikit", os, arch, target, targetString, tvosFrameworks) mutableListOfLinkerOptions(resolvedBinaryInputs.frameworks + resolvedBinaryInputs.linkFlags)
mutableListOfLinkerOptions(tvosFrameworks)
} }
OS.Linux -> { OS.Linux -> {
val options = mutableListOf( val options = mutableListOf(
"-L/usr/lib64", "-L/usr/lib64",
"-L/usr/lib/${if (arch == Arch.Arm64) "aarch64" else "x86_64"}-linux-gnu", "-L/usr/lib/${if (arch == Arch.Arm64) "aarch64" else "x86_64"}-linux-gnu",
"-lfontconfig",
"-lGL",
// TODO: an ugly hack, Linux linker searches only unresolved symbols.
"$skiaBinDir/libskottie.a",
"$skiaBinDir/libjsonreader.a",
"$skiaBinDir/libsksg.a",
"$skiaBinDir/libskshaper.a",
"$skiaBinDir/libskunicode_core.a",
"$skiaBinDir/libskunicode_icu.a",
"$skiaBinDir/libskia.a",
"$skiaBinDir/libskia_ganesh_ext.a"
) )
if (arch == Arch.Arm64) { options.addAll(resolvedBinaryInputs.directStaticArchivePaths)
options.add("-lEGL") options.addAll(resolvedBinaryInputs.dynamicLibNames.map { "-l$it" })
} options.addAll(resolvedBinaryInputs.linkFlags)
// When cross-compiling for ARM64 from x64, use the ARM toolchain sysroot // When cross-compiling for ARM64 from x64, use the ARM toolchain sysroot
if (arch == Arch.Arm64 && hostArch != Arch.Arm64) { if (arch == Arch.Arm64 && hostArch != Arch.Arm64) {
// ARM GNU toolchain sysroot paths // ARM GNU toolchain sysroot paths
...@@ -336,7 +295,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin ...@@ -336,7 +295,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
} }
inputs.files(objectFiles) inputs.files(objectFiles)
val outDir = layout.buildDirectory.dir("nativeBridges/static/$targetString").get().asFile val outDir = layout.buildDirectory.dir("nativeBridges/static/$targetString").get().asFile
val staticLib = "skiko-native-bridges-$targetString.a" val staticLib = "$nativeBridgesLibPrefix-$targetString.a"
workingDir = outDir workingDir = outDir
when (os) { when (os) {
OS.Linux -> { OS.Linux -> {
...@@ -360,7 +319,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin ...@@ -360,7 +319,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
project.registerSkikoTask<PatchSkiaSymbolsTask>(patchActionName, os, arch) { project.registerSkikoTask<PatchSkiaSymbolsTask>(patchActionName, os, arch) {
dependsOn(unzipper) dependsOn(unzipper)
dependsOn(linkTask) dependsOn(linkTask)
skiaLibs.set(skiaStaticLibraries(skiaDir, targetString, buildType).map { File(it) }) skiaLibs.set(nativeArchives.map { File(it) })
skikoBridge.set(File(bridgesLibraryPath)) skikoBridge.set(File(bridgesLibraryPath))
outputDir.set(patchedLibsDir) outputDir.set(patchedLibsDir)
} }
......
...@@ -7,6 +7,7 @@ import LinkSkikoWasmTask ...@@ -7,6 +7,7 @@ import LinkSkikoWasmTask
import OS import OS
import SkikoProjectContext import SkikoProjectContext
import compilerForTarget import compilerForTarget
import dsl.TargetEnv
import linkerForTarget import linkerForTarget
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
...@@ -41,7 +42,6 @@ fun SkikoProjectContext.declareWasmTasks() { ...@@ -41,7 +42,6 @@ fun SkikoProjectContext.declareWasmTasks() {
val skiaWasmDir = registerOrGetSkiaDirProvider(OS.Wasm, Arch.Wasm, false) val skiaWasmDir = registerOrGetSkiaDirProvider(OS.Wasm, Arch.Wasm, false)
val compileWasm by project.tasks.registering(CompileSkikoCppTask::class) { val compileWasm by project.tasks.registering(CompileSkikoCppTask::class) {
dependsOn(skiaWasmDir) dependsOn(skiaWasmDir)
compiler.set(compilerForTarget(OS.Wasm, Arch.Wasm)) compiler.set(compilerForTarget(OS.Wasm, Arch.Wasm))
buildTargetOS.set(OS.Wasm) buildTargetOS.set(OS.Wasm)
buildTargetArch.set(Arch.Wasm) buildTargetArch.set(Arch.Wasm)
...@@ -70,17 +70,15 @@ fun SkikoProjectContext.declareWasmTasks() { ...@@ -70,17 +70,15 @@ fun SkikoProjectContext.declareWasmTasks() {
fun LinkSkikoWasmTask.configureCommon(prefixPath: String) { fun LinkSkikoWasmTask.configureCommon(prefixPath: String) {
dependsOn(compileWasm) dependsOn(compileWasm)
dependsOn(skiaWasmDir) dependsOn(skiaWasmDir)
val skiaBinDir = skiaWasmDir.get().resolve("out/${buildType.id}-wasm-wasm").absolutePath
val resolvedBinaryInputs = resolveBinaryInputs(OS.Wasm, Arch.Wasm, TargetEnv.WASM, skiaBinDir)
linker.set(linkerForTarget(OS.Wasm, Arch.Wasm)) linker.set(linkerForTarget(OS.Wasm, Arch.Wasm))
buildTargetOS.set(OS.Wasm) buildTargetOS.set(OS.Wasm)
buildTargetArch.set(Arch.Wasm) buildTargetArch.set(Arch.Wasm)
buildVariant.set(buildType) buildVariant.set(buildType)
libFiles = project.fileTree(skiaWasmDir.get()) { libFiles = project.files(resolvedBinaryInputs.staticArchivePaths.distinct())
include("**/*.wasm.a")
exclude("**/libskia_graphite_ext.wasm.a")
exclude("**/libskia_graphite_dawn_ext.wasm.a")
}
objectFiles = project.fileTree(compileWasm.map { it.outDir.get() }) { objectFiles = project.fileTree(compileWasm.map { it.outDir.get() }) {
include("**/*.o") include("**/*.o")
} }
...@@ -94,17 +92,10 @@ fun SkikoProjectContext.declareWasmTasks() { ...@@ -94,17 +92,10 @@ fun SkikoProjectContext.declareWasmTasks() {
flags.addAll(buildList { flags.addAll(buildList {
addAll( addAll(
listOf( listOf(
"-l", "GL",
"-s", "MAX_WEBGL_VERSION=2",
"-s", "MIN_WEBGL_VERSION=2",
"-s", "OFFSCREEN_FRAMEBUFFER=1", "-s", "OFFSCREEN_FRAMEBUFFER=1",
"-s", "ALLOW_MEMORY_GROWTH=1", // TODO: Is there a better way? Should we use `-s INITIAL_MEMORY=X`? "-s", "ALLOW_MEMORY_GROWTH=1", // TODO: Is there a better way? Should we use `-s INITIAL_MEMORY=X`?
"-s", "EXPORT_ES6=1", "-s", "EXPORT_ES6=1",
"-s", "MODULARIZE=1",
"-s", "EXPORT_NAME=loadSkikoWASM",
"-s", "EXPORTED_RUNTIME_METHODS=\"[GL, wasmExports]\"",
"-s", "SUPPORT_LONGJMP=wasm", "-s", "SUPPORT_LONGJMP=wasm",
"--bind",
// -O2 saves 800kB for the output file, and ~100kB for transferred size. // -O2 saves 800kB for the output file, and ~100kB for transferred size.
// -O3 breaks the exports in js/mjs files. skiko.wasm size is the same though // -O3 breaks the exports in js/mjs files. skiko.wasm size is the same though
"-O2" "-O2"
...@@ -112,6 +103,7 @@ fun SkikoProjectContext.declareWasmTasks() { ...@@ -112,6 +103,7 @@ fun SkikoProjectContext.declareWasmTasks() {
) )
if (skiko.isWasmBuildWithProfiling) add("--profiling") if (skiko.isWasmBuildWithProfiling) add("--profiling")
addAll(resolvedBinaryInputs.linkFlags)
}) })
doLast { doLast {
...@@ -212,7 +204,7 @@ abstract class AbstractImportGeneratorCompilerPluginSupportPlugin( ...@@ -212,7 +204,7 @@ abstract class AbstractImportGeneratorCompilerPluginSupportPlugin(
override fun getCompilerPluginId() = "org.jetbrains.skiko.imports.generator" override fun getCompilerPluginId() = "org.jetbrains.skiko.imports.generator"
override fun getPluginArtifact(): SubpluginArtifact = override fun getPluginArtifact(): SubpluginArtifact =
SubpluginArtifact(SkikoArtifacts.groupId, IMPORT_GENERATOR) SubpluginArtifact(SkikoArtifacts.DEFAULT_GROUP_ID, IMPORT_GENERATOR)
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean { override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean {
return ((kotlinCompilation.platformType == KotlinPlatformType.wasm) || (kotlinCompilation.platformType == KotlinPlatformType.js)) return ((kotlinCompilation.platformType == KotlinPlatformType.wasm) || (kotlinCompilation.platformType == KotlinPlatformType.js))
...@@ -250,7 +242,7 @@ fun KotlinJsTargetDsl.setupImportsGeneratorPlugin() { ...@@ -250,7 +242,7 @@ fun KotlinJsTargetDsl.setupImportsGeneratorPlugin() {
// By default, it will try to use the same version as kotlin, because we use version=null in getPluginArtifact. // By default, it will try to use the same version as kotlin, because we use version=null in getPluginArtifact.
// But we don't publish the artifact, therefore we substitute it for project dependency. // But we don't publish the artifact, therefore we substitute it for project dependency.
it.configurations.pluginConfiguration.resolutionStrategy.dependencySubstitution { it.configurations.pluginConfiguration.resolutionStrategy.dependencySubstitution {
substitute(module("${SkikoArtifacts.groupId}:$IMPORT_GENERATOR")) substitute(module("${SkikoArtifacts.DEFAULT_GROUP_ID}:$IMPORT_GENERATOR"))
.using(project(":import-generator")) .using(project(":import-generator"))
} }
} }
......
...@@ -45,9 +45,6 @@ fun Task.projectDirs(vararg relativePaths: String): List<Directory> { ...@@ -45,9 +45,6 @@ fun Task.projectDirs(vararg relativePaths: String): List<Directory> {
return relativePaths.map { path -> projectDir.dir(path) } return relativePaths.map { path -> projectDir.dir(path) }
} }
fun listOfFrameworks(vararg frameworks: String): List<String> =
frameworks.toList().addElementBeforeEach("-framework")
fun mutableListOfLinkerOptions(options: Collection<String>): MutableList<String> = fun mutableListOfLinkerOptions(options: Collection<String>): MutableList<String> =
options.addElementBeforeEach("-linker-option") options.addElementBeforeEach("-linker-option")
......
...@@ -4,32 +4,33 @@ import org.jetbrains.compose.internal.publishing.* ...@@ -4,32 +4,33 @@ import org.jetbrains.compose.internal.publishing.*
val skiko = SkikoProperties(project) val skiko = SkikoProperties(project)
val mavenCentral = MavenCentralProperties(project) val mavenCentral = MavenCentralProperties(project)
val GITHUB_REPO = "JetBrains/skiko" val GITHUB_REPO = "JetBrains/skiko"
val skikoArtifacts = SkikoArtifacts()
val skikoArtifactIds: List<String> = val skikoArtifactIds: List<String> =
listOf( listOf(
SkikoArtifacts.commonArtifactId, skikoArtifacts.commonArtifactId,
SkikoArtifacts.jvmArtifactId, skikoArtifacts.jvmArtifactId,
SkikoArtifacts.jvmRuntimeArtifactIdFor(OS.Windows, Arch.X64), skikoArtifacts.jvmRuntimeArtifactIdFor(OS.Windows, Arch.X64),
SkikoArtifacts.jvmRuntimeArtifactIdFor(OS.Windows, Arch.Arm64), skikoArtifacts.jvmRuntimeArtifactIdFor(OS.Windows, Arch.Arm64),
SkikoArtifacts.jvmRuntimeArtifactIdFor(OS.Linux, Arch.X64), skikoArtifacts.jvmRuntimeArtifactIdFor(OS.Linux, Arch.X64),
SkikoArtifacts.jvmRuntimeArtifactIdFor(OS.Linux, Arch.Arm64), skikoArtifacts.jvmRuntimeArtifactIdFor(OS.Linux, Arch.Arm64),
SkikoArtifacts.jvmRuntimeArtifactIdFor(OS.MacOS, Arch.X64), skikoArtifacts.jvmRuntimeArtifactIdFor(OS.MacOS, Arch.X64),
SkikoArtifacts.jvmRuntimeArtifactIdFor(OS.MacOS, Arch.Arm64), skikoArtifacts.jvmRuntimeArtifactIdFor(OS.MacOS, Arch.Arm64),
SkikoArtifacts.jvmAdditionalRuntimeArtifactIdFor("angle", OS.Windows, Arch.X64), skikoArtifacts.jvmAdditionalRuntimeArtifactIdFor("angle", OS.Windows, Arch.X64),
SkikoArtifacts.jvmAdditionalRuntimeArtifactIdFor("angle", OS.Windows, Arch.Arm64), skikoArtifacts.jvmAdditionalRuntimeArtifactIdFor("angle", OS.Windows, Arch.Arm64),
SkikoArtifacts.jsWasmArtifactId, skikoArtifacts.jsWasmArtifactId,
SkikoArtifacts.jsArtifactId, skikoArtifacts.jsArtifactId,
SkikoArtifacts.wasmArtifactId, skikoArtifacts.wasmArtifactId,
SkikoArtifacts.nativeArtifactIdFor(OS.Linux, Arch.X64), skikoArtifacts.nativeArtifactIdFor(OS.Linux, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.Linux, Arch.Arm64), skikoArtifacts.nativeArtifactIdFor(OS.Linux, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.MacOS, Arch.Arm64), skikoArtifacts.nativeArtifactIdFor(OS.MacOS, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.MacOS, Arch.X64), skikoArtifacts.nativeArtifactIdFor(OS.MacOS, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.X64), skikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.Arm64), skikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.Arm64, isUikitSim = true), skikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.Arm64, isUikitSim = true),
SkikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.X64), skikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.Arm64), skikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.Arm64, isUikitSim = true), skikoArtifacts.nativeArtifactIdFor(OS.TVOS, Arch.Arm64, isUikitSim = true),
) )
val downloadSkikoArtifactsFromComposeDev by tasks.registering(DownloadFromSpaceMavenRepoTask::class) { val downloadSkikoArtifactsFromComposeDev by tasks.registering(DownloadFromSpaceMavenRepoTask::class) {
...@@ -94,7 +95,7 @@ fun Project.skikoMavenModules(version: String): Provider<List<ModuleToUpload>> = ...@@ -94,7 +95,7 @@ fun Project.skikoMavenModules(version: String): Provider<List<ModuleToUpload>> =
val artifactsDir = layout.buildDirectory.dir("skiko-artifacts").get().asFile val artifactsDir = layout.buildDirectory.dir("skiko-artifacts").get().asFile
skikoArtifactIds.map { artifactId -> skikoArtifactIds.map { artifactId ->
val skikoGroupId = "org.jetbrains.skiko" val skikoGroupId = SkikoArtifacts.DEFAULT_GROUP_ID
ModuleToUpload( ModuleToUpload(
groupId = skikoGroupId, groupId = skikoGroupId,
artifactId = artifactId, artifactId = artifactId,
......
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