Unverified Commit c4e8d4dc authored by Clément de La Bourdonnaye's avatar Clément de La Bourdonnaye Committed by GitHub

SKIKO-1013: Migrate skiko android target to proper androidTarget using AGP (#1043)

This also fixes SKIKO-934 by making the dependency between
`merge*JniLibFolders` and `unzip*` tasks explicit (the patch actually
adds too much dependencies but this should be limited to android tasks).
After this patch the android sample works from my configuration (might
be worth checking if other issues are still relevant?).

Few things to consider for review:
- I've changed versions of Gradle / AGP / Kotlin until I got something
working on both samples and library side.
- If `ANDROID_SDK_ROOT` environment variable was specified on a CI
before, it should now be `ANDROID_HOME` to work with android gradle
plugin (internal utilities will work with both).
- I've introduce some lazyness in configurations (`matching { ... }`),
as android plugin and previous code would race (android seems to declare
it's configurations quite late). If there's a better way to express it,
please let me know!
parent 440e1459
...@@ -187,7 +187,7 @@ jobs: ...@@ -187,7 +187,7 @@ jobs:
run: | run: |
./gradlew --no-daemon --stacktrace --info -Pkotlin.native.cacheKind.linuxX64=none :skiko:publishToMavenLocal ./gradlew --no-daemon --stacktrace --info -Pkotlin.native.cacheKind.linuxX64=none :skiko:publishToMavenLocal
./gradlew --no-daemon --stacktrace --info :SkiaAwtSample:installDist ./gradlew --no-daemon --stacktrace --info :SkiaAwtSample:installDist
./gradlew --no-daemon -Pskiko.android.enabled=true :skiko:publishSkikoJvmRuntimeAndroidX64PublicationToMavenLocal :skiko:publishSkikoJvmRuntimeAndroidArm64PublicationToMavenLocal :skiko:publishAndroidPublicationToMavenLocal ./gradlew --no-daemon -Pskiko.android.enabled=true :skiko:publishSkikoJvmRuntimeAndroidX64PublicationToMavenLocal :skiko:publishSkikoJvmRuntimeAndroidArm64PublicationToMavenLocal :skiko:publishAndroidReleasePublicationToMavenLocal
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
name: 'Save test results as artifact' name: 'Save test results as artifact'
......
...@@ -6,7 +6,7 @@ buildscript { ...@@ -6,7 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:7.0.2") classpath("com.android.tools.build:gradle:7.4.2")
} }
} }
...@@ -18,8 +18,8 @@ repositories { ...@@ -18,8 +18,8 @@ repositories {
} }
plugins { plugins {
id("com.android.application") version "7.0.2" id("com.android.application") version "7.4.2"
kotlin("android") version "1.8.0" kotlin("android") version "1.9.21"
} }
val skikoNativeX64 by configurations.creating val skikoNativeX64 by configurations.creating
...@@ -38,9 +38,13 @@ val unzipTaskArm64 = tasks.register("unzipNativeArm64", Copy::class) { ...@@ -38,9 +38,13 @@ val unzipTaskArm64 = tasks.register("unzipNativeArm64", Copy::class) {
from(skikoNativeArm64.map { zipTree(it) }) from(skikoNativeArm64.map { zipTree(it) })
} }
kotlin {
jvmToolchain(11)
}
android { android {
compileSdk = 31 compileSdk = 31
namespace = "org.jetbrains.skiko.sample"
defaultConfig { defaultConfig {
minSdk = 27 minSdk = 27
targetSdk = 31 targetSdk = 31
...@@ -84,11 +88,20 @@ dependencies { ...@@ -84,11 +88,20 @@ dependencies {
skikoNativeArm64("org.jetbrains.skiko:skiko-android-runtime-arm64:$version") skikoNativeArm64("org.jetbrains.skiko:skiko-android-runtime-arm64:$version")
} }
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile>().configureEach { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile>().configureEach {
dependsOn(unzipTaskX64) dependsOn(unzipTaskX64)
dependsOn(unzipTaskArm64) dependsOn(unzipTaskArm64)
} }
// SKIKO-934: we need to unpack these libraries before these are collected from android
// TODO the tasks we're actually targetting are mergeDebugJniLibFolders and mergeReleaseJniLibFolders,
// this adds unncessary dependencies
tasks.withType<com.android.build.gradle.tasks.MergeSourceSetFolders>()
.configureEach {
dependsOn(unzipTaskX64)
dependsOn(unzipTaskArm64)
}
tasks.withType<Copy> { tasks.withType<Copy> {
// This line needs to properly merge MANIFEST files from jars into dex // This line needs to properly merge MANIFEST files from jars into dex
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
......
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
package="org.jetbrains.skiko.sample">
<application <application
android:allowBackup="true" android:allowBackup="true"
......
...@@ -5,6 +5,7 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl ...@@ -5,6 +5,7 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import tasks.configuration.* import tasks.configuration.*
import kotlin.collections.HashMap import kotlin.collections.HashMap
import declareSkiaTasks import declareSkiaTasks
import com.android.build.gradle.LibraryExtension
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
...@@ -14,6 +15,10 @@ plugins { ...@@ -14,6 +15,10 @@ plugins {
id("org.gradle.crypto.checksum") version "1.4.0" id("org.gradle.crypto.checksum") version "1.4.0"
} }
if (supportAndroid) {
apply<com.android.build.gradle.LibraryPlugin>()
}
apply<WasmImportsGeneratorCompilerPluginSupportPlugin>() apply<WasmImportsGeneratorCompilerPluginSupportPlugin>()
apply<WasmImportsGeneratorForTestCompilerPluginSupportPlugin>() apply<WasmImportsGeneratorForTestCompilerPluginSupportPlugin>()
...@@ -45,6 +50,7 @@ allprojects { ...@@ -45,6 +50,7 @@ allprojects {
repositories { repositories {
mavenCentral() mavenCentral()
google()
} }
kotlin { kotlin {
...@@ -60,17 +66,19 @@ kotlin { ...@@ -60,17 +66,19 @@ kotlin {
} }
if (supportAndroid) { if (supportAndroid) {
jvm("android") { androidTarget("android") {
withJava() // This line needs to add Java sources in src/androidMain/java publishLibraryVariants("release")
compilations.all { compilations.all {
kotlinOptions.jvmTarget = "1.8" kotlinOptions.jvmTarget = "1.8"
} }
// We need an additional attribute to distinguish between JVM variants.
// Keep the previously defined attribute that was used to distinguish JVM and android variant
attributes { attributes {
attributes.attribute(Attribute.of("ui", String::class.java), "android") attributes.attribute(Attribute.of("ui", String::class.java), "android")
} }
// TODO: seems incorrect. // TODO: seems incorrect.
generateVersion( OS.Android, Arch.Arm64, skiko) generateVersion( OS.Android, Arch.Arm64, skiko, "release")
} }
} }
...@@ -185,7 +193,6 @@ kotlin { ...@@ -185,7 +193,6 @@ kotlin {
val androidMain by getting { val androidMain by getting {
dependsOn(jvmMain) dependsOn(jvmMain)
dependencies { dependencies {
compileOnly(files(androidJar()))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
} }
} }
...@@ -206,7 +213,7 @@ kotlin { ...@@ -206,7 +213,7 @@ kotlin {
} }
if (supportAndroid) { if (supportAndroid) {
val androidTest by getting { val androidUnitTest by getting {
dependsOn(jvmTest) dependsOn(jvmTest)
} }
} }
...@@ -394,19 +401,37 @@ kotlin { ...@@ -394,19 +401,37 @@ kotlin {
} }
if (supportAndroid) { if (supportAndroid) {
// Android configuration, when available
configure<LibraryExtension> {
compileSdk = 31
namespace = "org.jetbrains.skiko"
defaultConfig.minSdk = 24
defaultConfig.targetSdk = 24
defaultConfig.javaCompileOptions
compileOptions.sourceCompatibility = JavaVersion.VERSION_1_8
compileOptions.targetCompatibility = JavaVersion.VERSION_1_8
sourceSets.named("main") {
java.srcDirs("src/androidMain/java")
res.srcDirs("src/androidMain/res")
}
}
val os = OS.Android val os = OS.Android
val skikoAndroidJar by project.tasks.registering(Jar::class) { val skikoAndroidJar by project.tasks.registering(Jar::class) {
archiveBaseName.set("skiko-android") archiveBaseName.set("skiko-android")
from(kotlin.jvm("android").compilations["main"].output.allOutputs) from(kotlin.androidTarget("android").compilations["release"].output.allOutputs)
} }
for (arch in arrayOf(Arch.X64, Arch.Arm64)) { for (arch in arrayOf(Arch.X64, Arch.Arm64)) {
skikoProjectContext.createSkikoJvmJarTask(os, arch, skikoAndroidJar) skikoProjectContext.createSkikoJvmJarTask(os, arch, skikoAndroidJar)
} }
tasks.getByName("publishAndroidPublicationToMavenLocal") { tasks.matching { name == "publishAndroidReleasePublicationToMavenLocal" }.configureEach {
// It needs to be compatible with Gradle 8.1 // It needs to be compatible with Gradle 8.1
dependsOn(skikoAndroidJar) dependsOn(skikoAndroidJar)
} }
tasks.getByName("generateMetadataFileForAndroidPublication") { tasks.matching { name == "generateMetadataFileForAndroidReleasePublication" }.configureEach {
// It needs to be compatible with Gradle 8.1 // It needs to be compatible with Gradle 8.1
dependsOn(skikoAndroidJar) dependsOn(skikoAndroidJar)
} }
...@@ -556,6 +581,10 @@ publishing { ...@@ -556,6 +581,10 @@ publishing {
} }
} }
if (supportAndroid) {
pomNameForPublication["androidRelease"] = "Skiko Android Runtime"
}
val publicationsWithoutPomNames = publications.filter { it.name !in pomNameForPublication } val publicationsWithoutPomNames = publications.filter { it.name !in pomNameForPublication }
if (publicationsWithoutPomNames.isNotEmpty()) { if (publicationsWithoutPomNames.isNotEmpty()) {
error("Publications with unknown POM names: ${publicationsWithoutPomNames.joinToString { "'$it'" }}") error("Publications with unknown POM names: ${publicationsWithoutPomNames.joinToString { "'$it'" }}")
......
...@@ -161,7 +161,8 @@ fun Project.configureSignAndPublishDependencies() { ...@@ -161,7 +161,8 @@ fun Project.configureSignAndPublishDependencies() {
fun KotlinTarget.generateVersion( fun KotlinTarget.generateVersion(
targetOs: OS, targetOs: OS,
targetArch: Arch, targetArch: Arch,
skikoProperties: SkikoProperties skikoProperties: SkikoProperties,
compilationName: String = "main"
) { ) {
val targetName = this.name val targetName = this.name
val isUikitSim = isUikitSimulator() val isUikitSim = isUikitSimulator()
...@@ -194,9 +195,11 @@ fun KotlinTarget.generateVersion( ...@@ -194,9 +195,11 @@ fun KotlinTarget.generateVersion(
} }
} }
val compilation = compilations["main"] ?: error("Could not find 'main' compilation for target '$this'") // Needs to be lazily loaded as android compilations are not available right away
compilation.compileKotlinTaskProvider.configure { compilations.matching { it.name == compilationName }.configureEach {
compileTaskProvider.configure {
dependsOn(generateVersionTask) dependsOn(generateVersionTask)
(this as KotlinCompileTool).source(generatedDir.get().asFile) (this as KotlinCompileTool).source(generatedDir.get().asFile)
} }
}
} }
\ No newline at end of file
...@@ -515,8 +515,13 @@ fun SkikoProjectContext.setupJvmTestTask(skikoAwtJarForTests: TaskProvider<Jar>, ...@@ -515,8 +515,13 @@ fun SkikoProjectContext.setupJvmTestTask(skikoAwtJarForTests: TaskProvider<Jar>,
} }
fun Project.androidHomePath(): Provider<String> { fun Project.androidHomePath(): Provider<String> {
val androidHomeFromSdkRoot: Provider<String> = val androidHomeFromSdkHome: Provider<String> =
project.providers.environmentVariable("ANDROID_HOME")
// ANDROID_SDK_ROOT name is deprecated in favor of ANDROID_HOME
val deprecatedAndroidHomeFromSdkRoot: Provider<String> =
project.providers.environmentVariable("ANDROID_SDK_ROOT") project.providers.environmentVariable("ANDROID_SDK_ROOT")
val androidHomeFromUserHome: Provider<String> = val androidHomeFromUserHome: Provider<String> =
project.providers.systemProperty("user.home") project.providers.systemProperty("user.home")
.map { userHome -> .map { userHome ->
...@@ -525,26 +530,7 @@ fun Project.androidHomePath(): Provider<String> { ...@@ -525,26 +530,7 @@ fun Project.androidHomePath(): Provider<String> {
.firstOrNull { File(it).exists() } .firstOrNull { File(it).exists() }
?: error("Define Android SDK via ANDROID_SDK_ROOT") ?: error("Define Android SDK via ANDROID_SDK_ROOT")
} }
return androidHomeFromSdkRoot return androidHomeFromSdkHome
.orElse(deprecatedAndroidHomeFromSdkRoot)
.orElse(androidHomeFromUserHome) .orElse(androidHomeFromUserHome)
} }
fun Project.androidJar(askedVersion: String = ""): Provider<File> =
androidHomePath().map { androidHomePath ->
val androidHome = File(androidHomePath)
val version = if (askedVersion.isEmpty()) {
val platformsDir = androidHome.resolve("platforms")
val versions = platformsDir.list().orEmpty()
versions.maxByOrNull { name -> // possible name: "android-32", "android-33-ext4"
name.split("-").getOrNull(1)?.toIntOrNull() ?: 0
} ?: error(
buildString {
appendLine("'$platformsDir' does not contain any directories matching expected 'android-NUMBER' format: ${versions}")
}
)
} else {
"android-$askedVersion"
}
androidHome.resolve("platforms/$version/android.jar").also {
println("Skiko task androidJar uses android SDK in $it")
}
}
\ No newline at end of file
...@@ -2,20 +2,28 @@ pluginManagement { ...@@ -2,20 +2,28 @@ pluginManagement {
repositories { repositories {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
google()
} }
buildscript { buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/internal") maven("https://maven.pkg.jetbrains.space/public/p/compose/internal")
maven("https://maven.pkg.jetbrains.space/public/p/space/maven") maven("https://maven.pkg.jetbrains.space/public/p/space/maven")
google()
} }
dependencies { dependencies {
// TODO Removing this makes publishing module below crash android internal plugin
classpath("org.ow2.asm:asm:9.6")
// TODO https://youtrack.jetbrains.com/issue/SKIKO-1003/Unify-Maven-publication-of-Skiko-with-Compose // TODO https://youtrack.jetbrains.com/issue/SKIKO-1003/Unify-Maven-publication-of-Skiko-with-Compose
classpath("org.jetbrains.compose.internal.build-helpers:publishing:0.1.3") classpath("org.jetbrains.compose.internal.build-helpers:publishing:0.1.3")
// used by org.jetbrains.compose.internal.build-helpers:publishing because of https://youtrack.jetbrains.com/issue/CMP-7603/Fix-Maven-Central-publication // used by org.jetbrains.compose.internal.build-helpers:publishing because of https://youtrack.jetbrains.com/issue/CMP-7603/Fix-Maven-Central-publication
classpath("org.jetbrains:space-sdk-jvm:2024.3-185883") classpath("org.jetbrains:space-sdk-jvm:2024.3-185883")
classpath("org.kohsuke:github-api:1.116") classpath("org.kohsuke:github-api:1.116")
// Added dependency for Android Gradle plugin
classpath("com.android.tools.build:gradle:7.4.2")
} }
} }
...@@ -23,6 +31,7 @@ pluginManagement { ...@@ -23,6 +31,7 @@ pluginManagement {
val kotlinVersion = extra["kotlin.version"] as String val kotlinVersion = extra["kotlin.version"] as String
kotlin("jvm").version(kotlinVersion) kotlin("jvm").version(kotlinVersion)
kotlin("multiplatform").version(kotlinVersion) kotlin("multiplatform").version(kotlinVersion)
id("com.android.library").version("7.4.2") apply false
} }
} }
rootProject.name = "skiko" rootProject.name = "skiko"
......
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