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:
run: |
./gradlew --no-daemon --stacktrace --info -Pkotlin.native.cacheKind.linuxX64=none :skiko:publishToMavenLocal
./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
name: 'Save test results as artifact'
......
......@@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath("com.android.tools.build:gradle:7.0.2")
classpath("com.android.tools.build:gradle:7.4.2")
}
}
......@@ -18,8 +18,8 @@ repositories {
}
plugins {
id("com.android.application") version "7.0.2"
kotlin("android") version "1.8.0"
id("com.android.application") version "7.4.2"
kotlin("android") version "1.9.21"
}
val skikoNativeX64 by configurations.creating
......@@ -38,9 +38,13 @@ val unzipTaskArm64 = tasks.register("unzipNativeArm64", Copy::class) {
from(skikoNativeArm64.map { zipTree(it) })
}
kotlin {
jvmToolchain(11)
}
android {
compileSdk = 31
namespace = "org.jetbrains.skiko.sample"
defaultConfig {
minSdk = 27
targetSdk = 31
......@@ -67,9 +71,9 @@ android {
}
var version = if (project.hasProperty("skiko.version")) {
project.properties["skiko.version"] as String
project.properties["skiko.version"] as String
} else {
"0.0.0-SNAPSHOT"
"0.0.0-SNAPSHOT"
}
// ./gradlew -Pskiko.android.enabled=true \
......@@ -84,11 +88,20 @@ dependencies {
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(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> {
// This line needs to properly merge MANIFEST files from jars into dex
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
......
distributionBase=GRADLE_USER_HOME
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
zipStorePath=wrapper/dists
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.jetbrains.skiko.sample">
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
......
......@@ -5,6 +5,7 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import tasks.configuration.*
import kotlin.collections.HashMap
import declareSkiaTasks
import com.android.build.gradle.LibraryExtension
plugins {
kotlin("multiplatform")
......@@ -14,6 +15,10 @@ plugins {
id("org.gradle.crypto.checksum") version "1.4.0"
}
if (supportAndroid) {
apply<com.android.build.gradle.LibraryPlugin>()
}
apply<WasmImportsGeneratorCompilerPluginSupportPlugin>()
apply<WasmImportsGeneratorForTestCompilerPluginSupportPlugin>()
......@@ -45,6 +50,7 @@ allprojects {
repositories {
mavenCentral()
google()
}
kotlin {
......@@ -60,17 +66,19 @@ kotlin {
}
if (supportAndroid) {
jvm("android") {
withJava() // This line needs to add Java sources in src/androidMain/java
androidTarget("android") {
publishLibraryVariants("release")
compilations.all {
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.attribute(Attribute.of("ui", String::class.java), "android")
}
// TODO: seems incorrect.
generateVersion( OS.Android, Arch.Arm64, skiko)
generateVersion( OS.Android, Arch.Arm64, skiko, "release")
}
}
......@@ -185,7 +193,6 @@ kotlin {
val androidMain by getting {
dependsOn(jvmMain)
dependencies {
compileOnly(files(androidJar()))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
}
}
......@@ -206,7 +213,7 @@ kotlin {
}
if (supportAndroid) {
val androidTest by getting {
val androidUnitTest by getting {
dependsOn(jvmTest)
}
}
......@@ -394,19 +401,37 @@ kotlin {
}
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 skikoAndroidJar by project.tasks.registering(Jar::class) {
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)) {
skikoProjectContext.createSkikoJvmJarTask(os, arch, skikoAndroidJar)
}
tasks.getByName("publishAndroidPublicationToMavenLocal") {
tasks.matching { name == "publishAndroidReleasePublicationToMavenLocal" }.configureEach {
// It needs to be compatible with Gradle 8.1
dependsOn(skikoAndroidJar)
}
tasks.getByName("generateMetadataFileForAndroidPublication") {
tasks.matching { name == "generateMetadataFileForAndroidReleasePublication" }.configureEach {
// It needs to be compatible with Gradle 8.1
dependsOn(skikoAndroidJar)
}
......@@ -556,6 +581,10 @@ publishing {
}
}
if (supportAndroid) {
pomNameForPublication["androidRelease"] = "Skiko Android Runtime"
}
val publicationsWithoutPomNames = publications.filter { it.name !in pomNameForPublication }
if (publicationsWithoutPomNames.isNotEmpty()) {
error("Publications with unknown POM names: ${publicationsWithoutPomNames.joinToString { "'$it'" }}")
......
......@@ -161,7 +161,8 @@ fun Project.configureSignAndPublishDependencies() {
fun KotlinTarget.generateVersion(
targetOs: OS,
targetArch: Arch,
skikoProperties: SkikoProperties
skikoProperties: SkikoProperties,
compilationName: String = "main"
) {
val targetName = this.name
val isUikitSim = isUikitSimulator()
......@@ -194,9 +195,11 @@ fun KotlinTarget.generateVersion(
}
}
val compilation = compilations["main"] ?: error("Could not find 'main' compilation for target '$this'")
compilation.compileKotlinTaskProvider.configure {
dependsOn(generateVersionTask)
(this as KotlinCompileTool).source(generatedDir.get().asFile)
// Needs to be lazily loaded as android compilations are not available right away
compilations.matching { it.name == compilationName }.configureEach {
compileTaskProvider.configure {
dependsOn(generateVersionTask)
(this as KotlinCompileTool).source(generatedDir.get().asFile)
}
}
}
\ No newline at end of file
......@@ -515,8 +515,13 @@ fun SkikoProjectContext.setupJvmTestTask(skikoAwtJarForTests: TaskProvider<Jar>,
}
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")
val androidHomeFromUserHome: Provider<String> =
project.providers.systemProperty("user.home")
.map { userHome ->
......@@ -525,26 +530,7 @@ fun Project.androidHomePath(): Provider<String> {
.firstOrNull { File(it).exists() }
?: error("Define Android SDK via ANDROID_SDK_ROOT")
}
return androidHomeFromSdkRoot
return androidHomeFromSdkHome
.orElse(deprecatedAndroidHomeFromSdkRoot)
.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 {
repositories {
mavenCentral()
gradlePluginPortal()
google()
}
buildscript {
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/internal")
maven("https://maven.pkg.jetbrains.space/public/p/space/maven")
google()
}
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
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
classpath("org.jetbrains:space-sdk-jvm:2024.3-185883")
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 {
val kotlinVersion = extra["kotlin.version"] as String
kotlin("jvm").version(kotlinVersion)
kotlin("multiplatform").version(kotlinVersion)
id("com.android.library").version("7.4.2") apply false
}
}
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