diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ac50ef120..eec0257d5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,6 +21,12 @@ name: release # resources.srcDir(nativeResourcesRoot) on jvmMain picks them all # up into the published JAR. # +# The publish job also builds and publishes the skainet-backend-jni-cpu AAR +# (Android JNI kernel provider). Its native .so's are cross-compiled by the +# NDK during `./gradlew publish`, so the publish runner sets up the Android +# SDK + a pinned NDK (see the publish job) — independent of the build-native +# matrix above, which only produces the FFM shared libs. +# # Linux ARM64 is intentionally absent: Kotlin/Native plugin 2.3.21 # doesn't support `linux aarch64` as a HOST target ("Unknown host # target" — see SKaiNET PR #577). Linux ARM64 consumers fall back @@ -139,6 +145,20 @@ jobs: distribution: 'zulu' java-version: 25 + # `./gradlew publish` now includes skainet-backend-jni-cpu (an Android + # library whose AAR carries NDK-built .so's). The publish runner therefore + # needs the Android SDK + a pinned NDK; without it the AAR's native build + # fails and the release ships no JNI artifact. The NDK version must match + # gradle/libs.versions.toml `android-ndk`. + - name: Set up Android SDK + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 + + - name: Install pinned NDK + run: | + NDK_VERSION="$(grep -E '^android-ndk[[:space:]]*=' gradle/libs.versions.toml | sed -E 's/.*"([^"]+)".*/\1/')" + echo "Installing NDK ${NDK_VERSION}" + yes | sdkmanager "ndk;${NDK_VERSION}" >/dev/null + - name: Validate signing configuration run: | if ! grep -Eq '^[[:space:]]*signAllPublications[[:space:]]*=[[:space:]]*true[[:space:]]*$' gradle.properties; then diff --git a/CHANGELOG.md b/CHANGELOG.md index d62631748..ade5b228b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +### CI + +- **Release workflow publishes the `skainet-backend-jni-cpu` AAR.** `./gradlew publish` now + includes the Android JNI kernel module, whose AAR carries NDK-cross-built `.so`s; the publish + job gains Android SDK + a pinned NDK setup so the native build succeeds on the release runner + (previously the release would ship no JNI artifact, or fail). The NDK version is pinned in + `gradle/libs.versions.toml` (`android-ndk`) and referenced by the module's `ndkVersion` and the + workflow, so local and CI builds are reproducible. (#946) + ### Added - **NEON body for the Q4_0 matmul kernel.** `skainet_q4_0_matmul` was the only priority diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bc27cc76f..252e11063 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,10 @@ kotlinxCoroutines = "1.11.0" kotlinBrowser = "0.5.0" android-minSdk = "24" android-compileSdk = "36" +# NDK for skainet-backend-jni-cpu's externalNativeBuild. Pinned so the AAR's +# .so's build reproducibly on dev machines and the release runner. r28+ links +# 16 KB-page-aligned .so's by default (Android 15+ requirement). +android-ndk = "28.2.13676358" kotlinxSerializationJson = "1.11.0" ktorClientCore = "3.5.2" ktorClientPlugins = "3.1.1" diff --git a/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts b/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts index 37d9500ab..d7c71a44e 100644 --- a/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts +++ b/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts @@ -23,6 +23,9 @@ plugins { android { namespace = "sk.ainet.exec.kernel.jni" compileSdk = libs.versions.android.compileSdk.get().toInt() + // Pinned NDK so the AAR's .so's build reproducibly locally and on the + // release runner (AGP provisions it via sdkmanager when absent). + ndkVersion = libs.versions.android.ndk.get() defaultConfig { minSdk = libs.versions.android.minSdk.get().toInt()