From f997ee7abf421e118e8d39adeadfdd1ad343f4c5 Mon Sep 17 00:00:00 2001 From: Ryosuke Kamesawa Date: Sat, 6 Jan 2024 22:22:48 +0900 Subject: [PATCH 01/12] Fix CI tests --- .github/workflows/bump.yml | 2 +- .github/workflows/check.yml | 88 ++++++++++++++----- .github/workflows/release.yml | 4 +- .../com/nishtahir/NdkVersionTest.groovy | 16 +++- .../com/nishtahir/SimpleCargoProject.groovy | 18 +++- 5 files changed, 97 insertions(+), 31 deletions(-) diff --git a/.github/workflows/bump.yml b/.github/workflows/bump.yml index c4b25efd..4f6d0682 100644 --- a/.github/workflows/bump.yml +++ b/.github/workflows/bump.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # Everything here was cribbed from or inspired by # https://github.com/oflynned/android-version-bump/blob/b9f6de7f8bdf25de3f695843265debf7c3919272. diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 299d9326..f7a02c8c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,14 +20,14 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Gradle test run: | ./gradlew -p plugin generateTestTasksJson - id: setup-matrix - run: echo "::set-output name=matrix::$(cat plugin/build/build-resources/androidTestTasks.json)" + run: echo "matrix=$(cat plugin/build/build-resources/androidTestTasks.json)" >> $GITHUB_OUTPUT - name: debug run: echo ${{ steps.setup-matrix.outputs.matrix }} @@ -49,17 +49,31 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Setup Rust + - name: Setup Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android + + - name: Setup Rust 1.67 + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.67 + targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android + + - name: Setup NDK + env: + SDKMANAGER: /cmdline-tools/latest/bin/sdkmanager run: | - rustup toolchain install stable - rustup target add x86_64-linux-android - rustup target add x86_64-unknown-linux-gnu - rustup target add aarch64-linux-android + if [[ "${{ runner.os }}" == "Windows" ]]; then + SDKMANAGER=$(echo $SDKMANAGER | tr '/' '\').bat + fi + ${ANDROID_HOME}${SDKMANAGER} --install 'ndk;21.4.7075529' 'ndk;23.1.7779620' + shell: bash - name: Setup Java 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 11 @@ -92,18 +106,32 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + + - name: Setup Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - - name: Setup Rust + - name: Setup Rust 1.67 + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.67 + targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android + + - name: Setup NDK + env: + SDKMANAGER: /cmdline-tools/latest/bin/sdkmanager run: | - rustup toolchain install stable - rustup target add x86_64-linux-android - rustup target add x86_64-unknown-linux-gnu - rustup target add aarch64-linux-android + if [[ "${{ runner.os }}" == "Windows" ]]; then + SDKMANAGER=$(echo $SDKMANAGER | tr '/' '\').bat + fi + ${ANDROID_HOME}${SDKMANAGER} --install 'ndk;21.4.7075529' 'ndk;23.1.7779620' + shell: bash # Use Java 8 - name: Setup Java 8 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 8 @@ -140,18 +168,32 @@ jobs: # 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/checkout@v4 - - name: Setup Rust + - name: Setup NDK + env: + SDKMANAGER: /cmdline-tools/latest/bin/sdkmanager run: | - rustup toolchain install stable - rustup target add x86_64-linux-android - rustup target add x86_64-unknown-linux-gnu - rustup target add aarch64-linux-android + if [[ "${{ runner.os }}" == "Windows" ]]; then + SDKMANAGER=$(echo $SDKMANAGER | tr '/' '\').bat + fi + ${ANDROID_HOME}${SDKMANAGER} --install 'ndk;21.4.7075529' 'ndk;23.1.7779620' + shell: bash + + - name: Setup Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android + + - name: Setup Rust 1.67 + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.67 + targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android # Use Java 8 - name: Setup Java 8 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 8 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0db5c6fb..0b8bef00 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,10 +9,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 8 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '8' distribution: 'temurin' diff --git a/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy b/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy index b409b408..c1ca0f7c 100644 --- a/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy +++ b/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy @@ -13,6 +13,10 @@ class NdkVersionTest extends AbstractTest { def androidVersion = TestVersions.latestAndroidVersionForCurrentJDK() def target = "x86_64" def location = "android/x86_64/librust.so" + def ndkVersionMajor = ndkVersion.split('\\.')[0] as int + // Toolchain 1.68 or later versions are not compatible to old NDK prior to r23 + // https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html + def channel = ndkVersionMajor >= 23 ? "stable" : "1.67" SimpleAndroidApp.builder(temporaryFolder.root, cacheDir) .withAndroidVersion(androidVersion) @@ -24,6 +28,7 @@ class NdkVersionTest extends AbstractTest { SimpleCargoProject.builder(temporaryFolder.root) .withTargets([target]) + .withChannel(channel) .build() .writeProject() @@ -55,10 +60,15 @@ class NdkVersionTest extends AbstractTest { where: ndkVersion << [ - // Partial list of NDK versions supported by Github Actions, per - // https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md + // Old LTS NDKs need to be installed manually + "21.4.7075529", + "23.1.7779620", "26.3.11579264", - "27.2.12479018", + // Partial list of NDK versions supported by Github Actions, per + // https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2604-Readme.md#android + "27.3.13750724", + "28.2.13676358", + "29.0.14206865", ] } } diff --git a/plugin/src/test/groovy/com/nishtahir/SimpleCargoProject.groovy b/plugin/src/test/groovy/com/nishtahir/SimpleCargoProject.groovy index 14475d1d..5afc1ebf 100644 --- a/plugin/src/test/groovy/com/nishtahir/SimpleCargoProject.groovy +++ b/plugin/src/test/groovy/com/nishtahir/SimpleCargoProject.groovy @@ -3,15 +3,18 @@ package com.nishtahir class SimpleCargoProject { File projectDir List targets + String channel - SimpleCargoProject(File projectDir, List targets) { + SimpleCargoProject(File projectDir, List targets, String channel) { this.projectDir = projectDir this.targets = targets + this.channel = channel } static class Builder { File projectDir List targets + String channel Builder(File projectDir) { this.projectDir = projectDir @@ -22,11 +25,16 @@ class SimpleCargoProject { return this } + def withChannel(channel) { + this.channel = channel + return this + } + def build() { if (targets.isEmpty()) { throw new IllegalStateException("No targets provided") } - return new SimpleCargoProject(this.projectDir, this.targets) + return new SimpleCargoProject(this.projectDir, this.targets, this.channel) } } @@ -70,5 +78,11 @@ class SimpleCargoProject { libname = "rust" } """.stripIndent() + + if (channel != null) { + file('local.properties') << """ + rust.rustupChannel=${channel} + """.stripIndent() + } } } From 97c46321a9437cc2b7e7538eaadd15d69794f99c Mon Sep 17 00:00:00 2001 From: Ryosuke Kamesawa Date: Mon, 8 Jan 2024 19:54:23 +0900 Subject: [PATCH 02/12] Fix unspecified NDK version --- .../test/groovy/com/nishtahir/CargoTargetTest.groovy | 7 +++++++ .../test/groovy/com/nishtahir/SimpleAndroidApp.groovy | 11 +++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy b/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy index b44f749f..cb0c5b38 100644 --- a/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy +++ b/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy @@ -11,9 +11,15 @@ class CargoTargetTest extends AbstractTest { def "cargoBuild produces #location for target #target"() { given: def androidVersion = TestVersions.latestAndroidVersionForCurrentJDK() + def ndkVersion = "21.4.7075529" + def ndkVersionMajor = ndkVersion.split('\\.')[0] as int + // Toolchain 1.68 or later versions are not compatible to old NDK prior to r23 + // https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html + def channel = ndkVersionMajor >= 23 ? "stable" : "1.67" SimpleAndroidApp.builder(temporaryFolder.root, cacheDir) .withAndroidVersion(androidVersion) + .withNdkVersion(ndkVersion) .withKotlinDisabled() // TODO: .withCargo(...) .build() @@ -21,6 +27,7 @@ class CargoTargetTest extends AbstractTest { SimpleCargoProject.builder(temporaryFolder.root) .withTargets([target]) + .withChannel(channel) .build() .writeProject() diff --git a/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy b/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy index c539083e..b39c807c 100644 --- a/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy +++ b/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy @@ -156,8 +156,7 @@ class SimpleAndroidApp { } private String getMaybeNdkVersion() { - def isAndroid34x = androidVersion >= android("3.4.0") - if (isAndroid34x) { + if (ndkVersion != null) { return """ndkVersion '${ndkVersion}'""" } else { return "" @@ -275,7 +274,7 @@ class SimpleAndroidApp { boolean kaptWorkersEnabled = true VersionNumber androidVersion = Versions.latestAndroidVersion() - VersionNumber ndkVersion = Versions.latestAndroidVersion() >= android("3.4.0") ? VersionNumber.parse("26.3.11579264") : null + VersionNumber ndkVersion = null VersionNumber kotlinVersion = VersionNumber.parse("1.3.72") File projectDir @@ -303,9 +302,6 @@ class SimpleAndroidApp { Builder withAndroidVersion(VersionNumber androidVersion) { this.androidVersion = androidVersion - if (this.androidVersion < android("3.4.0")) { - this.ndkVersion = null - } return this } @@ -333,6 +329,9 @@ class SimpleAndroidApp { } SimpleAndroidApp build() { + if (ndkVersion == null && androidVersion >= android("3.4.0")) { + ndkVersion = VersionNumber.parse("21.4.7075529") + } return new SimpleAndroidApp(projectDir, cacheDir, androidVersion, ndkVersion, kotlinVersion, kotlinEnabled, kaptWorkersEnabled) } } From e3ac2e55889051782d70fc70183cfe57a5de0677 Mon Sep 17 00:00:00 2001 From: Mariusz Klochowicz Date: Tue, 27 Jan 2026 23:52:16 +1030 Subject: [PATCH 03/12] fix: add Gradle 9 compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gradle 9 removed several deprecated APIs that this plugin relied on. This commit updates the plugin to use the new APIs and modernizes the build configuration to support the changes. 1. Core API Changes (Gradle 9 removals) - Replace `fileMode = 493` with `filePermissions { unix("rwxr-xr-x") }` The CopySpec.fileMode property was deprecated in Gradle 8 and removed in Gradle 9. The new filePermissions API provides the same functionality. - Replace `project.exec {}` with injected `ExecOperations.exec {}` The Project.exec() method was deprecated in Gradle 7.5 and removed in Gradle 9. Tasks must now use service injection to obtain ExecOperations. This requires changing task classes from `open class` to `abstract class`. 2. Build Configuration Updates These changes are required because the new Gradle 9 APIs (filePermissions, ExecOperations) are only available in Gradle 8.3+, so we must compile against a newer Gradle version: - Gradle wrapper: 7.1.1 → 8.5 Required to compile against the new APIs (filePermissions added in 8.3) - Kotlin: 1.3.50 → 1.9.22 The old Kotlin Gradle plugin uses APIs removed in Gradle 8.x - AGP: 4.0.1 → 8.2.0 Required for Gradle 8.5+ compatibility - Java target: 1.8 → 17 Required by both Gradle 8.5+ and AGP 8.x - plugin-publish: 0.14.0 → 1.2.1 The old version uses the `pluginBundle {}` DSL which was removed. Metadata (website, vcsUrl, tags) now goes in `gradlePlugin {}` block. - Add Guava dependency Versions.groovy uses Guava collections which were previously provided transitively by AGP 4.x but are no longer included in AGP 8.x. - Add `null` branch to `when` expression in CargoBuildTask Kotlin 1.7+ requires exhaustive `when` for sealed classes with nullable types. 3. Test Matrix Updates - Updated supportedVersions to test AGP 8.x with Gradle 8.x - Updated jdkVersionFor() to use Java 17 for AGP 8.x Fixes #160 --- build.gradle | 6 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- plugin/build.gradle | 46 +++++++++++-------- .../kotlin/com/nishtahir/CargoBuildTask.kt | 18 +++++--- .../com/nishtahir/GenerateToolchainsTask.kt | 8 +++- .../kotlin/com/nishtahir/RustAndroidPlugin.kt | 4 +- 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/build.gradle b/build.gradle index 3882b4de..02d12e97 100644 --- a/build.gradle +++ b/build.gradle @@ -2,8 +2,10 @@ buildscript { Properties versionProperties = new Properties() versionProperties.load(new FileInputStream("$project.rootDir/version.properties")) - ext.kotlin_version = '1.3.50' - ext.agp_version = '4.0.1' + // Kotlin 1.9.22: Required for Gradle 8.5+ (old Kotlin Gradle plugin uses removed APIs) + ext.kotlin_version = '1.9.22' + // AGP 8.2.0: Required for Gradle 8.5+ compatibility + ext.agp_version = '8.2.0' ext.plugin_version = versionProperties.getProperty("version") repositories { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc3..a5952066 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/plugin/build.gradle b/plugin/build.gradle index 2251d37e..ee80bbb7 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -2,8 +2,9 @@ import groovy.json.JsonBuilder import org.gradle.util.VersionNumber plugins { - id 'com.gradle.plugin-publish' version '0.14.0' - id "org.gradle.test-retry" version "1.2.0" + // plugin-publish 1.2.1: Required for Gradle 8.5+ (old version uses removed pluginBundle API) + id 'com.gradle.plugin-publish' version '1.2.1' + id "org.gradle.test-retry" version "1.5.8" } apply plugin: "java-gradle-plugin" @@ -12,12 +13,16 @@ apply plugin: "groovy" apply plugin: "kotlin" gradlePlugin { + // website/vcsUrl/tags moved here from pluginBundle (removed in plugin-publish 1.0+) + website = 'https://github.com/mozilla/rust-android-gradle' + vcsUrl = 'https://github.com/mozilla/rust-android-gradle.git' plugins { rustAndroidGradlePlugin { id = 'org.mozilla.rust-android-gradle.rust-android' implementationClass = 'com.nishtahir.RustAndroidPlugin' displayName = 'Plugin for building Rust with Cargo in Android projects' description = 'A plugin that helps build Rust JNI libraries with Cargo for use in Android projects.' + tags.set(['rust', 'cargo', 'android']) } } } @@ -28,14 +33,13 @@ version "$plugin_version" def isCI = (System.getenv('CI') ?: 'false').toBoolean() // Maps supported Android plugin versions to the versions of Gradle that support it +// Updated for Gradle 8.5+ compatibility (required for Gradle 9 support) def supportedVersions = [ - "7.0.0": ["7.1.1"], - "4.2.2": ["6.8.3", "7.1.1"], - "4.1.3": ["6.5.1", "6.8.3"], - "4.0.2": ["6.1.1", "6.8.3"], - "3.6.4": ["5.6.4", "6.8.3"], - "3.5.4": ["5.4.1", "5.6.4", "6.8.3"], - "3.1.2": ["4.10.2"] + "8.2.0": ["8.5"], + "8.3.0": ["8.5"], + "8.4.0": ["8.6"], + "8.5.0": ["8.7"], + "8.6.0": ["8.7"], ] // A local repo we publish our library to for testing in order to workaround limitations @@ -51,6 +55,8 @@ publishing { dependencies { implementation gradleApi() + // Guava: Required for Versions.groovy (was transitively provided by old AGP) + implementation "com.google.guava:guava:32.1.3-jre" compileOnly "com.android.tools.build:gradle:${agp_version}" testImplementation gradleTestKit() @@ -61,17 +67,17 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api" } +// Java 17: Required for Gradle 8.5+ and AGP 8.x +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + compileKotlin { - kotlinOptions.jvmTarget = "1.8" + kotlinOptions.jvmTarget = "17" } compileTestKotlin { - kotlinOptions.jvmTarget = "1.8" -} - -pluginBundle { - website = 'https://github.com/mozilla/rust-android-gradle' - vcsUrl = 'https://github.com/mozilla/rust-android-gradle.git' - tags = ['rust', 'cargo', 'android'] + kotlinOptions.jvmTarget = "17" } @@ -152,7 +158,9 @@ static def normalizeVersion(String version) { } static def jdkVersionFor(String version) { - def jdkVersion = VersionNumber.parse(version) > VersionNumber.parse("7.0.0-alpha01") ? 11 : 8 - + // AGP 8.x requires Java 17 + def jdkVersion = VersionNumber.parse(version) >= VersionNumber.parse("8.0.0") ? 17 : + VersionNumber.parse(version) > VersionNumber.parse("7.0.0-alpha01") ? 11 : 8 + return JavaLanguageVersion.of(jdkVersion) } diff --git a/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt b/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt index 363f0ec4..4831a2e8 100644 --- a/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt +++ b/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt @@ -8,10 +8,14 @@ import org.gradle.api.Project import org.gradle.api.logging.LogLevel import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations import java.io.ByteArrayOutputStream import java.io.File +import javax.inject.Inject -open class CargoBuildTask : DefaultTask() { +abstract class CargoBuildTask : DefaultTask() { + @get:Inject + abstract val execOperations: ExecOperations @Input var toolchain: Toolchain? = null @@ -48,7 +52,7 @@ open class CargoBuildTask : DefaultTask() { ?: targetDirectory ?: "${module!!}/target" - val defaultTargetTriple = getDefaultTargetTriple(project, rustcCommand) + val defaultTargetTriple = getDefaultTargetTriple(execOperations, project, rustcCommand) var cargoOutputDir = File(if (toolchain.target == defaultTargetTriple) { "${target}/${profile}" @@ -84,9 +88,9 @@ open class CargoBuildTask : DefaultTask() { inline fun buildProjectForTarget(project: Project, toolchain: Toolchain, ndk: Ndk, cargoExtension: CargoExtension) { val apiLevel = cargoExtension.apiLevels[toolchain.platform]!! - val defaultTargetTriple = getDefaultTargetTriple(project, cargoExtension.rustcCommand) + val defaultTargetTriple = getDefaultTargetTriple(execOperations, project, cargoExtension.rustcCommand) - project.exec { spec -> + execOperations.exec { spec -> with(spec) { standardOutput = System.out val module = File(cargoExtension.module!!) @@ -120,6 +124,7 @@ open class CargoBuildTask : DefaultTask() { // there's a way to specify them in the cargo command line -- rustc accepts // them if passed in directly with `--cfg`, and cargo will pass them to rustc // if you use them as default featureSpec. + // Kotlin 1.9+ requires exhaustive when for sealed classes with nullable types when (features) { is Features.All -> { theCommandLine.add("--all-features") @@ -137,6 +142,7 @@ open class CargoBuildTask : DefaultTask() { theCommandLine.add(features.featureSet.joinToString(" ")) } } + null -> { /* Use default features */ } } if (cargoExtension.profile != "debug") { @@ -249,9 +255,9 @@ open class CargoBuildTask : DefaultTask() { } // This can't be private/internal as it's called from `buildProjectForTarget`. -fun getDefaultTargetTriple(project: Project, rustc: String): String? { +fun getDefaultTargetTriple(execOperations: ExecOperations, project: Project, rustc: String): String? { val stdout = ByteArrayOutputStream() - val result = project.exec { spec -> + val result = execOperations.exec { spec -> spec.standardOutput = stdout spec.commandLine = listOf(rustc, "--version", "--verbose") } diff --git a/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt b/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt index a8d3e7d5..0b088aa9 100644 --- a/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt +++ b/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt @@ -7,8 +7,12 @@ import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations +import javax.inject.Inject -open class GenerateToolchainsTask : DefaultTask() { +abstract class GenerateToolchainsTask : DefaultTask() { + @get:Inject + abstract val execOperations: ExecOperations @TaskAction @Suppress("unused") @@ -44,7 +48,7 @@ open class GenerateToolchainsTask : DefaultTask() { // already. It is fast to do so and fixes any issues // with partially reclaimed temporary files. val dir = File(cargoExtension.toolchainDirectory, arch + "-" + apiLevel) - project.exec { spec -> + execOperations.exec { spec -> spec.standardOutput = System.out spec.errorOutput = System.out spec.commandLine(cargoExtension.pythonCommand) diff --git a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt index 51662749..23273543 100644 --- a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt +++ b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt @@ -276,7 +276,9 @@ open class RustAndroidPlugin : Plugin { eachFile { it.path = it.path.replaceFirst("com/nishtahir", "") } - fileMode = 493 // 0755 in decimal; Kotlin doesn't have octal literals (!). + filePermissions { permissions -> + permissions.unix("rwxr-xr-x") // 0755 + } includeEmptyDirs = false duplicatesStrategy = DuplicatesStrategy.EXCLUDE } From f11499ca3822adc62088465748d5a77634cdd132 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Thu, 23 Jul 2026 13:28:19 -0700 Subject: [PATCH 04/12] Bump `substitute ... with project` to `substitute ... using project`. --- README.md | 2 +- samples/app/settings.gradle | 2 +- samples/library/settings.gradle | 2 +- samples/unittest/settings.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ec45ac99..43f7d662 100644 --- a/README.md +++ b/README.md @@ -529,7 +529,7 @@ An easy way to locally test changes made in this plugin is to simply add this to includeBuild('../rust-android-gradle') { dependencySubstitution { // As required. - substitute module('gradle.plugin.org.mozilla.rust-android-gradle:plugin') with project(':plugin') + substitute module('gradle.plugin.org.mozilla.rust-android-gradle:plugin') using project(':plugin') } } ``` diff --git a/samples/app/settings.gradle b/samples/app/settings.gradle index 70475595..a213a17a 100644 --- a/samples/app/settings.gradle +++ b/samples/app/settings.gradle @@ -1,6 +1,6 @@ includeBuild('../..') { dependencySubstitution { // As required. - substitute module('org.mozilla.rust-android-gradle:rust-android') with project(':plugin') + substitute module('org.mozilla.rust-android-gradle:rust-android') using project(':plugin') } } diff --git a/samples/library/settings.gradle b/samples/library/settings.gradle index 70475595..a213a17a 100644 --- a/samples/library/settings.gradle +++ b/samples/library/settings.gradle @@ -1,6 +1,6 @@ includeBuild('../..') { dependencySubstitution { // As required. - substitute module('org.mozilla.rust-android-gradle:rust-android') with project(':plugin') + substitute module('org.mozilla.rust-android-gradle:rust-android') using project(':plugin') } } diff --git a/samples/unittest/settings.gradle b/samples/unittest/settings.gradle index 70475595..a213a17a 100644 --- a/samples/unittest/settings.gradle +++ b/samples/unittest/settings.gradle @@ -1,6 +1,6 @@ includeBuild('../..') { dependencySubstitution { // As required. - substitute module('org.mozilla.rust-android-gradle:rust-android') with project(':plugin') + substitute module('org.mozilla.rust-android-gradle:rust-android') using project(':plugin') } } From 177a561e44bed940aa8647d45a87ffd2a16819e6 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Thu, 23 Jul 2026 14:08:49 -0700 Subject: [PATCH 05/12] Remove support for generating Android toolchains. --- README.md | 23 +------ .../kotlin/com/nishtahir/CargoExtension.kt | 1 - .../com/nishtahir/GenerateToolchainsTask.kt | 63 ------------------- .../kotlin/com/nishtahir/RustAndroidPlugin.kt | 55 ---------------- 4 files changed, 1 insertion(+), 141 deletions(-) delete mode 100644 plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt diff --git a/README.md b/README.md index 43f7d662..b6c393ed 100644 --- a/README.md +++ b/README.md @@ -188,21 +188,6 @@ cargo { } ``` -### prebuiltToolchains - -When set to `true` (which requires NDK version 19+), use the prebuilt toolchains bundled with the -NDK. When set to `false`, generate per-target architecture standalone NDK toolchains using -`make_standalone_toolchain.py`. When unset, use the prebuilt toolchains if the NDK version is 19+, -and fall back to generated toolchains for older NDK versions. - -Defaults to `null`. - -```groovy -cargo { - prebuiltToolchains = true -} -``` - ### verbose When set, execute `cargo build` with or without the `--verbose` flag. When unset, respect the @@ -370,13 +355,7 @@ cargo { ## Specifying NDK toolchains -The plugin can either use prebuilt NDK toolchain binaries, or search for (and if missing, build) -NDK toolchains as generated by `make_standalone_toolchain.py`. - -A prebuilt NDK toolchain will be used if: -1. `rust.prebuiltToolchain=true` in the per-(multi-)project `${rootDir}/local.properties` -1. `prebuiltToolchain=true` in the `cargo { ... }` block (if not overridden by `local.properties`) -1. The discovered NDK is version 19 or higher (if not overridden per above) +The plugin uses prebuilt NDK toolchain binaries. The toolchains are rooted in a single Android NDK toolchain directory. In order of preference, the toolchain root directory is determined by: diff --git a/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt b/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt index e0b0995b..3ed23d64 100644 --- a/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt +++ b/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt @@ -36,7 +36,6 @@ open class CargoExtension { var module: String? = null var libname: String? = null var targets: List? = null - var prebuiltToolchains: Boolean? = null var profile: String = "debug" var verbose: Boolean? = null var targetDirectory: String? = null diff --git a/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt b/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt deleted file mode 100644 index 0b088aa9..00000000 --- a/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.nishtahir - -import java.io.File - -import com.android.build.gradle.* -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.Project -import org.gradle.api.tasks.TaskAction -import org.gradle.process.ExecOperations -import javax.inject.Inject - -abstract class GenerateToolchainsTask : DefaultTask() { - @get:Inject - abstract val execOperations: ExecOperations - - @TaskAction - @Suppress("unused") - fun generateToolchainTask() { - project.plugins.all { - when (it) { - is AppPlugin -> configureTask(project) - is LibraryPlugin -> configureTask(project) - } - } - } - - inline fun configureTask(project: Project) { - val cargoExtension = project.extensions[CargoExtension::class] - val app = project.extensions[T::class] - val ndkPath = app.ndkDirectory - - // It's safe to unwrap, since we bailed at configuration time if this is unset. - val targets = cargoExtension.targets!! - - toolchains - .filter { it.type == ToolchainType.ANDROID_GENERATED } - .filter { (arch) -> targets.contains(arch) } - .forEach { (arch) -> - // We ensure all architectures have an API level at configuration time - val apiLevel = cargoExtension.apiLevels[arch]!! - - if (arch.endsWith("64") && apiLevel < 21) { - throw GradleException("Can't target 64-bit ${arch} with API level < 21 (${apiLevel})") - } - - // Always regenerate the toolchain, even if it exists - // already. It is fast to do so and fixes any issues - // with partially reclaimed temporary files. - val dir = File(cargoExtension.toolchainDirectory, arch + "-" + apiLevel) - execOperations.exec { spec -> - spec.standardOutput = System.out - spec.errorOutput = System.out - spec.commandLine(cargoExtension.pythonCommand) - spec.args("$ndkPath/build/tools/make_standalone_toolchain.py", - "--arch=$arch", - "--api=$apiLevel", - "--install-dir=${dir}", - "--force") - } - } - } -} diff --git a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt index 23273543..8bbd35f3 100644 --- a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt +++ b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt @@ -13,7 +13,6 @@ const val RUST_TASK_GROUP = "rust" enum class ToolchainType { ANDROID_PREBUILT, - ANDROID_GENERATED, DESKTOP, } @@ -57,30 +56,6 @@ val toolchains = listOf( "", "", "desktop/win32-x86-64"), - Toolchain("arm", - ToolchainType.ANDROID_GENERATED, - "armv7-linux-androideabi", - "arm-linux-androideabi", - "arm-linux-androideabi", - "android/armeabi-v7a"), - Toolchain("arm64", - ToolchainType.ANDROID_GENERATED, - "aarch64-linux-android", - "aarch64-linux-android", - "aarch64-linux-android", - "android/arm64-v8a"), - Toolchain("x86", - ToolchainType.ANDROID_GENERATED, - "i686-linux-android", - "i686-linux-android", - "i686-linux-android", - "android/x86"), - Toolchain("x86_64", - ToolchainType.ANDROID_GENERATED, - "x86_64-linux-android", - "x86_64-linux-android", - "x86_64-linux-android", - "android/x86_64"), Toolchain("arm", ToolchainType.ANDROID_PREBUILT, "armv7-linux-androideabi", // This is correct. "Note: For 32-bit ARM, the compiler is prefixed with @@ -242,26 +217,6 @@ open class RustAndroidPlugin : Plugin { Ndk(path = it, version = ndkVersion) } - // Determine whether to use prebuilt or generated toolchains - val usePrebuilt = - cargoExtension.localProperties.getProperty("rust.prebuiltToolchains")?.equals("true") ?: - cargoExtension.prebuiltToolchains ?: - (ndk.versionMajor >= 19); - - if (usePrebuilt && ndk.versionMajor < 19) { - throw GradleException("usePrebuilt = true requires NDK version 19+") - } - - val generateToolchain = if (!usePrebuilt) { - tasks.maybeCreate("generateToolchains", - GenerateToolchainsTask::class.java).apply { - group = RUST_TASK_GROUP - description = "Generate standard toolchain for given architectures" - } - } else { - null - } - // Fish linker wrapper scripts from our Java resources. val generateLinkerWrapper = rootProject.tasks.maybeCreate("generateLinkerWrapper", GenerateLinkerWrapperTask::class.java).apply { group = RUST_TASK_GROUP @@ -291,13 +246,6 @@ open class RustAndroidPlugin : Plugin { cargoExtension.targets!!.forEach { target -> val theToolchain = toolchains - .filter { - if (usePrebuilt) { - it.type != ToolchainType.ANDROID_GENERATED - } else { - it.type != ToolchainType.ANDROID_PREBUILT - } - } .find { it.platform == target } if (theToolchain == null) { throw GradleException("Target ${target} is not recognized (recognized targets: ${toolchains.map { it.platform }.sorted()}). Check `local.properties` and `build.gradle`.") @@ -311,9 +259,6 @@ open class RustAndroidPlugin : Plugin { this.ndk = ndk } - if (!usePrebuilt) { - targetBuildTask.dependsOn(generateToolchain!!) - } targetBuildTask.dependsOn(generateLinkerWrapper) buildTask.dependsOn(targetBuildTask) } From 1f5ef9298d63de818fa0286e55c9e169976aa12a Mon Sep 17 00:00:00 2001 From: Karn Saheb Date: Sat, 14 Feb 2026 10:50:44 -0800 Subject: [PATCH 06/12] Fix Gradle configuration cache support (#114) Move all Project access out of @TaskAction methods into task properties set at configuration time. This enables Gradle's configuration cache by ensuring tasks don't reference the Project graph during execution. - Make CargoBuildTask and GenerateToolchainsTask abstract with injected ExecOperations and FileSystemOperations services - Add @Input/@Internal properties for all values previously read from CargoExtension and Project at execution time - Make Ndk, Toolchain, Features, and FeatureSpec implement Serializable - Warn when cargo.exec closure is set (not compatible with config cache) --- .../kotlin/com/nishtahir/CargoBuildTask.kt | 262 ++++++++++-------- .../kotlin/com/nishtahir/CargoExtension.kt | 4 +- .../kotlin/com/nishtahir/RustAndroidPlugin.kt | 35 ++- 3 files changed, 187 insertions(+), 114 deletions(-) diff --git a/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt b/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt index 4831a2e8..fa83c923 100644 --- a/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt +++ b/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt @@ -1,14 +1,16 @@ package com.nishtahir; -import com.android.build.gradle.* import org.apache.tools.ant.taskdefs.condition.Os import org.gradle.api.DefaultTask import org.gradle.api.GradleException -import org.gradle.api.Project import org.gradle.api.logging.LogLevel import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.process.ExecOperations +import org.gradle.api.file.FileSystemOperations +import org.gradle.process.ExecSpec import java.io.ByteArrayOutputStream import java.io.File import javax.inject.Inject @@ -16,109 +18,122 @@ import javax.inject.Inject abstract class CargoBuildTask : DefaultTask() { @get:Inject abstract val execOperations: ExecOperations + + @get:Inject + abstract val fileSystemOperations: FileSystemOperations + @Input var toolchain: Toolchain? = null @Input var ndk: Ndk? = null - @Suppress("unused") - @TaskAction - fun build() = with(project) { - extensions[CargoExtension::class].apply { - // Need to capture the value to dereference smoothly. - val toolchain = toolchain - if (toolchain == null) { - throw GradleException("toolchain cannot be null") - } + @Internal + var projectDir: File = File("") - val ndk = ndk ?: throw GradleException("ndk cannot be null") + @Internal + var buildDir: File = File("") - project.plugins.all { - when (it) { - is AppPlugin -> buildProjectForTarget(project, toolchain, ndk, this) - is LibraryPlugin -> buildProjectForTarget(project, toolchain, ndk, this) - } - } - // CARGO_TARGET_DIR can be used to force the use of a global, shared target directory - // across all rust projects on a machine. Use it if it's set, otherwise use the - // configured `targetDirectory` value, and fall back to `${module}/target`. - // - // We also allow this to be specified in `local.properties`, not because this is - // something you should ever need to do currently, but we don't want it to ruin anyone's - // day if it turns out we're wrong about that. - val target = - getProperty("rust.cargoTargetDir", "CARGO_TARGET_DIR") - ?: targetDirectory - ?: "${module!!}/target" - - val defaultTargetTriple = getDefaultTargetTriple(execOperations, project, rustcCommand) - - var cargoOutputDir = File(if (toolchain.target == defaultTargetTriple) { - "${target}/${profile}" - } else { - "${target}/${toolchain.target}/${profile}" - }) - if (!cargoOutputDir.isAbsolute) { - cargoOutputDir = File(project.project.projectDir, cargoOutputDir.path) - } - cargoOutputDir = cargoOutputDir.canonicalFile + @Internal + var rootBuildDir: File = File("") - val intoDir = File(buildDir, "rustJniLibs/${toolchain.folder}") - intoDir.mkdirs() + @Input + var cargoCommand: String = "cargo" - copy { spec -> - spec.from(cargoOutputDir) - spec.into(intoDir) + @Input + var rustcCommand: String = "rustc" - // Need to capture the value to dereference smoothly. - val targetIncludes = targetIncludes - if (targetIncludes != null) { - spec.include(targetIncludes.asIterable()) - } else { - // It's safe to unwrap, since we bailed at configuration time if this is unset. - val libname = libname!! - spec.include("lib${libname}.so") - spec.include("lib${libname}.dylib") - spec.include("${libname}.dll") - } - } - } - } + @Input + var rustupChannel: String = "" + + @Input + var pythonCommand: String = "python" + + @Input + var module: String = "" - inline fun buildProjectForTarget(project: Project, toolchain: Toolchain, ndk: Ndk, cargoExtension: CargoExtension) { - val apiLevel = cargoExtension.apiLevels[toolchain.platform]!! - val defaultTargetTriple = getDefaultTargetTriple(execOperations, project, cargoExtension.rustcCommand) + @Input + @Optional + var libname: String? = null + + @Input + @Optional + var verbose: Boolean? = null + + @Input + var profile: String = "debug" + + @Input + @Optional + var cargoTargetDir: String? = null + + @Input + @Optional + var targetIncludes: Array? = null + + @Input + var featureSpec: FeatureSpec = FeatureSpec() + + @Input + @Optional + var extraCargoBuildArguments: List? = null + + @Input + var apiLevels: Map = mapOf() + + @Input + var generateBuildId: Boolean = false + + @Internal + var toolchainDirectory: File = File("") + + @Input + var autoConfigureClangSys: Boolean = false + + @Input + var targetProperties: Map = mapOf() + + @Internal + var execClosure: ((ExecSpec, Toolchain) -> Unit)? = null + + @Suppress("unused") + @TaskAction + fun build() { + val toolchain = toolchain ?: throw GradleException("toolchain cannot be null") + val ndk = ndk ?: throw GradleException("ndk cannot be null") + + val apiLevel = apiLevels[toolchain.platform]!! + val defaultTargetTriple = getDefaultTargetTriple(execOperations, logger, rustcCommand) execOperations.exec { spec -> with(spec) { standardOutput = System.out - val module = File(cargoExtension.module!!) - if (module.isAbsolute) { - workingDir = module + val moduleFile = File(module) + if (moduleFile.isAbsolute) { + workingDir = moduleFile } else { - workingDir = File(project.project.projectDir, module.path) + workingDir = File(projectDir, moduleFile.path) } workingDir = workingDir.canonicalFile - val theCommandLine = mutableListOf(cargoExtension.cargoCommand) + val theCommandLine = mutableListOf(cargoCommand) - if (!cargoExtension.rustupChannel.isEmpty()) { - val hasPlusSign = cargoExtension.rustupChannel.startsWith("+") + if (!rustupChannel.isEmpty()) { + val hasPlusSign = rustupChannel.startsWith("+") val maybePlusSign = if (!hasPlusSign) "+" else "" - theCommandLine.add(maybePlusSign + cargoExtension.rustupChannel) + theCommandLine.add(maybePlusSign + rustupChannel) } theCommandLine.add("build") // Respect `verbose` if it is set; otherwise, log if asked to // with `--info` or `--debug` from the command line. - if (cargoExtension.verbose ?: project.logger.isEnabled(LogLevel.INFO)) { + if (verbose ?: logger.isEnabled(LogLevel.INFO)) { theCommandLine.add("--verbose") } - val features = cargoExtension.featureSpec.features + val features = featureSpec.features // We just pass this along to cargo as something space separated... AFAICT // you're allowed to have featureSpec with spaces in them, but I don't think // there's a way to specify them in the cargo command line -- rustc accepts @@ -145,11 +160,11 @@ abstract class CargoBuildTask : DefaultTask() { null -> { /* Use default features */ } } - if (cargoExtension.profile != "debug") { + if (profile != "debug") { // Cargo is rigid: it accepts "--release" for release (and // nothing for dev). This is a cheap way of allowing only // two values. - theCommandLine.add("--${cargoExtension.profile}") + theCommandLine.add("--${profile}") } if (toolchain.target != defaultTargetTriple) { // Only providing --target for the non-default targets means desktop builds @@ -164,13 +179,13 @@ abstract class CargoBuildTask : DefaultTask() { val prefix = "RUST_ANDROID_GRADLE_TARGET_${toolchain_target}_" // For ORG_GRADLE_PROJECT_RUST_ANDROID_GRADLE_TARGET_x_KEY=VALUE, set KEY=VALUE. - project.logger.info("Passing through project properties with prefix '${prefix}' (environment variables with prefix 'ORG_GRADLE_PROJECT_${prefix}'") - project.properties.forEach { (key, value) -> - if (key.startsWith(prefix)) { - val realKey = key.substring(prefix.length) - project.logger.debug("Passing through environment variable '${key}' as '${realKey}=${value}'") - environment(realKey, value) - } + logger.info("Passing through project properties with prefix '${prefix}' (environment variables with prefix 'ORG_GRADLE_PROJECT_${prefix}'") + targetProperties.forEach { (key, value) -> + if (key.startsWith(prefix)) { + val realKey = key.substring(prefix.length) + logger.debug("Passing through environment variable '${key}' as '${realKey}=${value}'") + environment(realKey, value) + } } // Cross-compiling to Android requires toolchain massaging. @@ -178,7 +193,7 @@ abstract class CargoBuildTask : DefaultTask() { val ndkPath = ndk.path val ndkVersionMajor = ndk.versionMajor - val toolchainDirectory = if (toolchain.type == ToolchainType.ANDROID_PREBUILT) { + val toolchainDir = if (toolchain.type == ToolchainType.ANDROID_PREBUILT) { environment("CARGO_NDK_MAJOR_VERSION", ndkVersionMajor) val hostTag = if (Os.isFamily(Os.FAMILY_WINDOWS)) { @@ -194,20 +209,20 @@ abstract class CargoBuildTask : DefaultTask() { } File("$ndkPath/toolchains/llvm/prebuilt", hostTag) } else { - cargoExtension.toolchainDirectory + toolchainDirectory } val linker_wrapper = if (System.getProperty("os.name").startsWith("Windows")) { - File(project.rootProject.buildDir, "linker-wrapper/linker-wrapper.bat") + File(rootBuildDir, "linker-wrapper/linker-wrapper.bat") } else { - File(project.rootProject.buildDir, "linker-wrapper/linker-wrapper.sh") + File(rootBuildDir, "linker-wrapper/linker-wrapper.sh") } environment("CARGO_TARGET_${toolchain_target}_LINKER", linker_wrapper.path) - val cc = File(toolchainDirectory, "${toolchain.cc(apiLevel)}").path; - val cxx = File(toolchainDirectory, "${toolchain.cxx(apiLevel)}").path; - val ar = File(toolchainDirectory, "${toolchain.ar(apiLevel, ndkVersionMajor)}").path; + val cc = File(toolchainDir, "${toolchain.cc(apiLevel)}").path; + val cxx = File(toolchainDir, "${toolchain.cxx(apiLevel)}").path; + val ar = File(toolchainDir, "${toolchain.ar(apiLevel, ndkVersionMajor)}").path; // For build.rs in `cc` consumers: like "CC_i686-linux-android". See // https://github.com/alexcrichton/cc-rs#external-configuration-via-environment-variables. @@ -218,51 +233,78 @@ abstract class CargoBuildTask : DefaultTask() { // Set CLANG_PATH in the environment, so that bindgen (or anything // else using clang-sys in a build.rs) works properly, and doesn't // use host headers and such. - val shouldConfigure = cargoExtension.getFlagProperty( - "rust.autoConfigureClangSys", - "RUST_ANDROID_GRADLE_AUTO_CONFIGURE_CLANG_SYS", - // By default, only do this for non-desktop platforms. If we're - // building for desktop, things should work out of the box. - toolchain.type != ToolchainType.DESKTOP - ) - if (shouldConfigure) { + if (autoConfigureClangSys) { environment("CLANG_PATH", cc) } // Configure our linker wrapper. - environment("RUST_ANDROID_GRADLE_PYTHON_COMMAND", cargoExtension.pythonCommand) + environment("RUST_ANDROID_GRADLE_PYTHON_COMMAND", pythonCommand) environment("RUST_ANDROID_GRADLE_LINKER_WRAPPER_PY", - File(project.rootProject.buildDir, "linker-wrapper/linker-wrapper.py").path) + File(rootBuildDir, "linker-wrapper/linker-wrapper.py").path) environment("RUST_ANDROID_GRADLE_CC", cc) - if (cargoExtension.generateBuildId) { - environment("RUST_ANDROID_GRADLE_CC_LINK_ARG", "-Wl,--build-id,-soname,lib${cargoExtension.libname!!}.so") + if (generateBuildId) { + environment("RUST_ANDROID_GRADLE_CC_LINK_ARG", "-Wl,--build-id,-soname,lib${libname!!}.so") } else { - environment("RUST_ANDROID_GRADLE_CC_LINK_ARG", "-Wl,-soname,lib${cargoExtension.libname!!}.so") + environment("RUST_ANDROID_GRADLE_CC_LINK_ARG", "-Wl,-soname,lib${libname!!}.so") } } - cargoExtension.extraCargoBuildArguments?.let { + extraCargoBuildArguments?.let { theCommandLine.addAll(it) } commandLine = theCommandLine } - if (cargoExtension.exec != null) { - (cargoExtension.exec!!)(spec, toolchain) + if (execClosure != null) { + (execClosure!!)(spec, toolchain) } }.assertNormalExitValue() + + // CARGO_TARGET_DIR can be used to force the use of a global, shared target directory + // across all rust projects on a machine. Use it if it's set, otherwise use the + // configured `targetDirectory` value, and fall back to `${module}/target`. + val target = cargoTargetDir ?: "${module}/target" + + var cargoOutputDir = File(if (toolchain.target == defaultTargetTriple) { + "${target}/${profile}" + } else { + "${target}/${toolchain.target}/${profile}" + }) + if (!cargoOutputDir.isAbsolute) { + cargoOutputDir = File(projectDir, cargoOutputDir.path) + } + cargoOutputDir = cargoOutputDir.canonicalFile + + val intoDir = File(buildDir, "rustJniLibs/${toolchain.folder}") + intoDir.mkdirs() + + fileSystemOperations.copy { spec -> + spec.from(cargoOutputDir) + spec.into(intoDir) + + // Need to capture the value to dereference smoothly. + val targetIncludes = targetIncludes + if (targetIncludes != null) { + spec.include(targetIncludes.asIterable()) + } else { + // It's safe to unwrap, since we bailed at configuration time if this is unset. + val libname = libname!! + spec.include("lib${libname}.so") + spec.include("lib${libname}.dylib") + spec.include("${libname}.dll") + } + } } } -// This can't be private/internal as it's called from `buildProjectForTarget`. -fun getDefaultTargetTriple(execOperations: ExecOperations, project: Project, rustc: String): String? { +fun getDefaultTargetTriple(execOperations: ExecOperations, logger: org.gradle.api.logging.Logger, rustc: String): String? { val stdout = ByteArrayOutputStream() val result = execOperations.exec { spec -> spec.standardOutput = stdout spec.commandLine = listOf(rustc, "--version", "--verbose") } if (result.exitValue != 0) { - project.logger.warn( + logger.warn( "Failed to get default target triple from rustc (exit code: ${result.exitValue})") return null } @@ -277,9 +319,9 @@ fun getDefaultTargetTriple(execOperations: ExecOperations, project: Project, rus ?.let { it.substring(triplePrefix.length).trim() } if (triple == null) { - project.logger.warn("Failed to parse `rustc -Vv` output! (Please report a rust-android-gradle bug)") + logger.warn("Failed to parse `rustc -Vv` output! (Please report a rust-android-gradle bug)") } else { - project.logger.info("Default rust target triple: $triple") + logger.info("Default rust target triple: $triple") } return triple } diff --git a/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt b/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt index 3ed23d64..0d573902 100644 --- a/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt +++ b/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt @@ -7,7 +7,7 @@ import org.gradle.process.ExecSpec import java.io.File import java.util.* -sealed class Features { +sealed class Features : java.io.Serializable { class All() : Features() data class DefaultAnd(val featureSet: Set) : Features() @@ -15,7 +15,7 @@ sealed class Features { data class NoDefaultBut(val featureSet: Set) : Features() } -data class FeatureSpec(var features: Features? = null) { +data class FeatureSpec(var features: Features? = null) : java.io.Serializable { fun all() { this.features = Features.All() } diff --git a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt index 8bbd35f3..23732cc8 100644 --- a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt +++ b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt @@ -82,7 +82,7 @@ val toolchains = listOf( "android/x86_64") ) -data class Ndk(val path: File, val version: String) { +data class Ndk(val path: File, val version: String) : java.io.Serializable { val versionMajor: Int get() = version.split(".").first().toInt() } @@ -92,7 +92,7 @@ data class Toolchain(val platform: String, val target: String, val compilerTriple: String, val binutilsTriple: String, - val folder: String) { + val folder: String) : java.io.Serializable { fun cc(apiLevel: Int): File = if (System.getProperty("os.name").startsWith("Windows")) { if (type == ToolchainType.ANDROID_PREBUILT) { @@ -257,6 +257,37 @@ open class RustAndroidPlugin : Plugin { description = "Build library ($target)" toolchain = theToolchain this.ndk = ndk + projectDir = project.projectDir + this.buildDir = project.buildDir + rootBuildDir = project.rootProject.buildDir + cargoCommand = cargoExtension.cargoCommand + rustcCommand = cargoExtension.rustcCommand + rustupChannel = cargoExtension.rustupChannel + pythonCommand = cargoExtension.pythonCommand + module = cargoExtension.module!! + libname = cargoExtension.libname + verbose = cargoExtension.verbose + profile = cargoExtension.profile + cargoTargetDir = cargoExtension.getProperty("rust.cargoTargetDir", "CARGO_TARGET_DIR") + ?: cargoExtension.targetDirectory + targetIncludes = cargoExtension.targetIncludes + featureSpec = cargoExtension.featureSpec + extraCargoBuildArguments = cargoExtension.extraCargoBuildArguments + apiLevels = cargoExtension.apiLevels + generateBuildId = cargoExtension.generateBuildId + this.toolchainDirectory = cargoExtension.toolchainDirectory + autoConfigureClangSys = cargoExtension.getFlagProperty( + "rust.autoConfigureClangSys", + "RUST_ANDROID_GRADLE_AUTO_CONFIGURE_CLANG_SYS", + theToolchain.type != ToolchainType.DESKTOP + ) + targetProperties = project.properties + .filterKeys { it.startsWith("RUST_ANDROID_GRADLE_TARGET_") } + .mapValues { it.value?.toString() ?: "" } + cargoExtension.exec?.let { + logger.warn("rust-android-gradle: cargo.exec closure is not compatible with Gradle configuration cache") + execClosure = it + } } targetBuildTask.dependsOn(generateLinkerWrapper) From efd3c6a5027dad18a62af79e7c5b30850e654423 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 17 Aug 2026 14:44:11 -0700 Subject: [PATCH 07/12] Use `android.namespace` rather than `AndroidManifest.xml`. --- samples/app/build.gradle | 2 ++ samples/app/src/main/AndroidManifest.xml | 5 ++--- samples/library/build.gradle | 2 ++ samples/library/src/main/AndroidManifest.xml | 4 +--- samples/unittest/build.gradle | 3 +++ samples/unittest/src/main/AndroidManifest.xml | 3 +-- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/samples/app/build.gradle b/samples/app/build.gradle index 2c4bd139..d27015b2 100644 --- a/samples/app/build.gradle +++ b/samples/app/build.gradle @@ -19,6 +19,8 @@ android { compileSdkVersion 27 ndkVersion "26.3.11579264" + namespace = 'com.nishtahir.androidrust' + defaultConfig { applicationId "com.nishtahir.androidrust" minSdkVersion 21 diff --git a/samples/app/src/main/AndroidManifest.xml b/samples/app/src/main/AndroidManifest.xml index 472bdf4f..9a5a65a2 100644 --- a/samples/app/src/main/AndroidManifest.xml +++ b/samples/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + @@ -21,4 +20,4 @@ - \ No newline at end of file + diff --git a/samples/library/build.gradle b/samples/library/build.gradle index 9312d26c..a0666247 100644 --- a/samples/library/build.gradle +++ b/samples/library/build.gradle @@ -19,6 +19,8 @@ android { compileSdkVersion 27 ndkVersion "26.3.11579264" + namespace = 'com.nishtahir.library' + defaultConfig { minSdkVersion 21 targetSdkVersion 27 diff --git a/samples/library/src/main/AndroidManifest.xml b/samples/library/src/main/AndroidManifest.xml index 363ba392..9912c54c 100644 --- a/samples/library/src/main/AndroidManifest.xml +++ b/samples/library/src/main/AndroidManifest.xml @@ -1,6 +1,4 @@ - + diff --git a/samples/unittest/build.gradle b/samples/unittest/build.gradle index 55de3418..e8ebc36c 100644 --- a/samples/unittest/build.gradle +++ b/samples/unittest/build.gradle @@ -17,6 +17,9 @@ apply plugin: 'org.mozilla.rust-android-gradle.rust-android' android { compileSdkVersion 27 + + namespace = 'com.nishtahir.androidrust' + defaultConfig { applicationId "com.nishtahir.androidrust" minSdkVersion 21 diff --git a/samples/unittest/src/main/AndroidManifest.xml b/samples/unittest/src/main/AndroidManifest.xml index 1d7371cd..9c0748af 100644 --- a/samples/unittest/src/main/AndroidManifest.xml +++ b/samples/unittest/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + From d42e17af578e02248b2dc212a521e809d0449b0d Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 17 Aug 2026 14:57:16 -0700 Subject: [PATCH 08/12] Update samples: bump AGP version to 8.2.0; update NDK version. `samples/unittest` is still broken on macOS with architecture mismatches like: ``` com.nishtahir.androidrust.ExampleUnitTest > testViaJNI FAILED java.lang.NoClassDefFoundError: Could not initialize class com.nishtahir.androidrust.MainActivity at com.nishtahir.androidrust.ExampleUnitTest.testViaJNI(ExampleUnitTest.java:30) Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.UnsatisfiedLinkError: /Users/nalexander/Library/Caches/JNA/temp/jna17483397431938007410.tmp: dlopen(/Users/nalexander/Library/Caches/JNA/temp/jna17483397431938007410.tmp, 0x0001): tried: '/Users/nalexander/Library/Caches/JNA/temp/jna17483397431938007410.tmp' (fat file, but missing compatible architecture (have 'i386,x86_64', need 'arm64e' or 'arm64e.v1' or 'arm64' or 'arm64')), '/System/Volumes/Preboot/Cryptexes/OS/Users/nalexander/Library/Caches/JNA/temp/jna17483397431938007410.tmp' (no such file), '/Users/nalexander/Library/Caches/JNA/temp/jna17483397431938007410.tmp' (fat file, but missing compatible architecture (have 'i386,x86_64', need 'arm64e' or 'arm64e.v1' or 'arm64' or 'arm64')) [in thread "SDK 21 Main Thread"] at java.base/jdk.internal.loader.NativeLibraries.load(Native Method) at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:388) at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:232) at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:174) at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2394) at java.base/java.lang.Runtime.load0(Runtime.java:755) at java.base/java.lang.System.load(System.java:1970) at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:1018) at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:988) at com.sun.jna.Native.(Native.java:195) at com.sun.jna.NativeLibrary.(NativeLibrary.java:87) at com.nishtahir.androidrust.MainActivity.(MainActivity.java:28) at com.nishtahir.androidrust.ExampleUnitTest.testViaJNA(ExampleUnitTest.java:36) ``` --- samples/app/build.gradle | 8 ++++---- samples/library/build.gradle | 8 ++++---- samples/unittest/build.gradle | 14 ++++++++------ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/samples/app/build.gradle b/samples/app/build.gradle index d27015b2..80e984bb 100644 --- a/samples/app/build.gradle +++ b/samples/app/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.agp_version = '7.0.0' + ext.agp_version = '8.2.0' repositories { google() maven { @@ -16,15 +16,15 @@ apply plugin: 'com.android.application' apply plugin: 'org.mozilla.rust-android-gradle.rust-android' android { - compileSdkVersion 27 - ndkVersion "26.3.11579264" + compileSdkVersion 30 + ndkVersion "29.0.14206865" namespace = 'com.nishtahir.androidrust' defaultConfig { applicationId "com.nishtahir.androidrust" minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/samples/library/build.gradle b/samples/library/build.gradle index a0666247..b9b3a923 100644 --- a/samples/library/build.gradle +++ b/samples/library/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.agp_version = '7.0.0' + ext.agp_version = '8.2.0' repositories { google() maven { @@ -16,14 +16,14 @@ apply plugin: 'com.android.library' apply plugin: 'org.mozilla.rust-android-gradle.rust-android' android { - compileSdkVersion 27 - ndkVersion "26.3.11579264" + compileSdkVersion 30 + ndkVersion "29.0.14206865" namespace = 'com.nishtahir.library' defaultConfig { minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/samples/unittest/build.gradle b/samples/unittest/build.gradle index e8ebc36c..be9438a0 100644 --- a/samples/unittest/build.gradle +++ b/samples/unittest/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.agp_version = '4.0.1' + ext.agp_version = '8.2.0' repositories { google() maven { @@ -16,14 +16,14 @@ apply plugin: 'com.android.application' apply plugin: 'org.mozilla.rust-android-gradle.rust-android' android { - compileSdkVersion 27 + compileSdkVersion 30 namespace = 'com.nishtahir.androidrust' defaultConfig { applicationId "com.nishtahir.androidrust" minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -34,7 +34,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - ndkVersion "22.1.7171670" + ndkVersion "29.0.14206865" sourceSets { test.resources.srcDirs += "$buildDir/rustJniLibs/desktop" } @@ -43,14 +43,16 @@ android { cargo { module = "../rust" targets = ["x86_64", "linux-x86-64"] // "x86", "x86_64", "arm64"] + targets = ["arm64", "darwin-aarch64"] libname = "rust" } repositories { google() + // For JNA. + mavenCentral() } - configurations { // There's an interaction between Gradle's resolution of dependencies with different types // (@jar, @aar) for `implementation` and `testImplementation` and with Android Studio's built-in @@ -88,7 +90,7 @@ dependencies { // avoiding other configurations from being resolved. Tricky! testImplementation files(configurations.jnaForTest.copyRecursive().files) // testImplementation "androidx.test.ext:junit:$versions.androidx_junit" - testImplementation "org.robolectric:robolectric:4.2.1" + testImplementation "org.robolectric:robolectric:4.14.1" } afterEvaluate { From 692a0c652a40923b9e6f0f0946330d0ca8eae0ce Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 17 Aug 2026 15:17:23 -0700 Subject: [PATCH 09/12] Update samples: require Java 17. --- plugin/build.gradle | 1 - samples/app/build.gradle | 6 ++++++ samples/library/build.gradle | 6 ++++++ samples/unittest/build.gradle | 6 ++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/plugin/build.gradle b/plugin/build.gradle index ee80bbb7..be702b3e 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -80,7 +80,6 @@ compileTestKotlin { kotlinOptions.jvmTarget = "17" } - // Generate a json file that contains the matrix of Gradle and AGP versions to test against. def generatedResources = "$buildDir/generated-resources/main" tasks.register('generateVersions') { diff --git a/samples/app/build.gradle b/samples/app/build.gradle index 80e984bb..66351263 100644 --- a/samples/app/build.gradle +++ b/samples/app/build.gradle @@ -57,6 +57,12 @@ dependencies { testImplementation 'junit:junit:4.12' } +// Java 17: Required for Gradle 8.5+ and AGP 8.x +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + afterEvaluate { // The `cargoBuild` task isn't available until after evaluation. android.applicationVariants.all { variant -> diff --git a/samples/library/build.gradle b/samples/library/build.gradle index b9b3a923..672896ca 100644 --- a/samples/library/build.gradle +++ b/samples/library/build.gradle @@ -66,6 +66,12 @@ dependencies { testImplementation 'junit:junit:4.12' } +// Java 17: Required for Gradle 8.5+ and AGP 8.x +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + afterEvaluate { // The `cargoBuild` task isn't available until after evaluation. android.libraryVariants.all { variant -> diff --git a/samples/unittest/build.gradle b/samples/unittest/build.gradle index be9438a0..932af296 100644 --- a/samples/unittest/build.gradle +++ b/samples/unittest/build.gradle @@ -93,6 +93,12 @@ dependencies { testImplementation "org.robolectric:robolectric:4.14.1" } +// Java 17: Required for Gradle 8.5+ and AGP 8.x +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + afterEvaluate { // The `cargoBuild` task isn't available until after evaluation. android.applicationVariants.all { variant -> From 8f590d6f6cf6c024d677141a06c72d88461f72f1 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 17 Aug 2026 15:25:54 -0700 Subject: [PATCH 10/12] Bump Java version to 17 in GH Actions. --- .github/workflows/check.yml | 30 +++++++++++++++--------------- .github/workflows/release.yml | 8 ++++---- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f7a02c8c..a16f9b71 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,7 +20,7 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Gradle test run: | @@ -49,7 +49,7 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Rust stable uses: dtolnay/rust-toolchain@stable @@ -72,11 +72,11 @@ jobs: ${ANDROID_HOME}${SDKMANAGER} --install 'ndk;21.4.7075529' 'ndk;23.1.7779620' shell: bash - - name: Setup Java 11 - uses: actions/setup-java@v4 + - name: Setup Java 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 11 + java-version: 17 cache: 'gradle' - name: Assemble samples/app @@ -106,7 +106,7 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Rust stable uses: dtolnay/rust-toolchain@stable @@ -129,12 +129,12 @@ jobs: ${ANDROID_HOME}${SDKMANAGER} --install 'ndk;21.4.7075529' 'ndk;23.1.7779620' shell: bash - # Use Java 8 - - name: Setup Java 8 - uses: actions/setup-java@v4 + # Use Java 17 + - name: Setup Java 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 8 + java-version: 17 cache: 'gradle' - name: Gradle setup @@ -168,7 +168,7 @@ jobs: # 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@v4 + - uses: actions/checkout@v7 - name: Setup NDK env: @@ -191,12 +191,12 @@ jobs: toolchain: 1.67 targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - # Use Java 8 - - name: Setup Java 8 - uses: actions/setup-java@v4 + # Use Java 17 + - name: Setup Java 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 8 + java-version: 17 cache: 'gradle' - name: Gradle setup diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b8bef00..36b79843 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,12 +9,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - name: Set up JDK 8 - uses: actions/setup-java@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: - java-version: '8' + java-version: 17 distribution: 'temurin' - name: Write gradle.properties From b94994b0cabcb549e06a05aa75fb85e1bad24223 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 17 Aug 2026 16:44:51 -0700 Subject: [PATCH 11/12] Update tests to use newer NDKs. --- .../com/nishtahir/CargoTargetTest.groovy | 2 +- .../com/nishtahir/NdkVersionTest.groovy | 4 ---- .../com/nishtahir/SimpleAndroidApp.groovy | 24 +++++++++---------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy b/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy index cb0c5b38..d6d20827 100644 --- a/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy +++ b/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy @@ -11,7 +11,7 @@ class CargoTargetTest extends AbstractTest { def "cargoBuild produces #location for target #target"() { given: def androidVersion = TestVersions.latestAndroidVersionForCurrentJDK() - def ndkVersion = "21.4.7075529" + def ndkVersion = "29.0.14206865" def ndkVersionMajor = ndkVersion.split('\\.')[0] as int // Toolchain 1.68 or later versions are not compatible to old NDK prior to r23 // https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html diff --git a/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy b/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy index c1ca0f7c..a18c97e1 100644 --- a/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy +++ b/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy @@ -60,10 +60,6 @@ class NdkVersionTest extends AbstractTest { where: ndkVersion << [ - // Old LTS NDKs need to be installed manually - "21.4.7075529", - "23.1.7779620", - "26.3.11579264", // Partial list of NDK versions supported by Github Actions, per // https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2604-Readme.md#android "27.3.13750724", diff --git a/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy b/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy index b39c807c..0e01954f 100644 --- a/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy +++ b/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy @@ -51,7 +51,7 @@ class SimpleAndroidApp { } } dependencies { - classpath ('com.android.tools.build:gradle:$androidVersion') { force = true } + classpath ('com.android.tools.build:gradle') { version { strictly '$androidVersion' } } classpath "org.mozilla.rust-android-gradle:plugin:${Versions.PLUGIN_VERSION}" ${kotlinPluginDependencyIfEnabled} } @@ -60,15 +60,13 @@ class SimpleAndroidApp { writeActivity(library, libPackage, libraryActivity) file("${library}/src/main/AndroidManifest.xml") << """ - + """.stripIndent() writeActivity(app, appPackage, appActivity) file("${app}/src/main/AndroidManifest.xml") << """ - + = android("3.4.0")) { - ndkVersion = VersionNumber.parse("21.4.7075529") + ndkVersion = VersionNumber.parse("29.0.14206865") } return new SimpleAndroidApp(projectDir, cacheDir, androidVersion, ndkVersion, kotlinVersion, kotlinEnabled, kaptWorkersEnabled) } From 4cce14772681bf8d06e217b2e3098b8f1e6386ed Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 17 Aug 2026 17:02:57 -0700 Subject: [PATCH 12/12] Update tests to not exercise ancient Rust versions. --- .github/workflows/check.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a16f9b71..45742a35 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -56,12 +56,6 @@ jobs: with: targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - - name: Setup Rust 1.67 - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.67 - targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - - name: Setup NDK env: SDKMANAGER: /cmdline-tools/latest/bin/sdkmanager @@ -113,12 +107,6 @@ jobs: with: targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - - name: Setup Rust 1.67 - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.67 - targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - - name: Setup NDK env: SDKMANAGER: /cmdline-tools/latest/bin/sdkmanager @@ -185,12 +173,6 @@ jobs: with: targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - - name: Setup Rust 1.67 - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.67 - targets: x86_64-linux-android,x86_64-unknown-linux-gnu,aarch64-linux-android - # Use Java 17 - name: Setup Java 17 uses: actions/setup-java@v5