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..45742a35 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@v7 - 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,20 +49,28 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 - - name: Setup Rust - 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 + - 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 Java 11 - uses: actions/setup-java@v2 + - name: Setup NDK + env: + SDKMANAGER: /cmdline-tools/latest/bin/sdkmanager + run: | + 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 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 11 + java-version: 17 cache: 'gradle' - name: Assemble samples/app @@ -92,21 +100,29 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE for the job. - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 - - 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 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 - - # Use Java 8 - - name: Setup Java 8 - uses: actions/setup-java@v2 + 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 17 + - name: Setup Java 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 8 + java-version: 17 cache: 'gradle' - name: Gradle setup @@ -140,21 +156,29 @@ 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@v7 - - 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 - - # Use Java 8 - - name: Setup Java 8 - uses: actions/setup-java@v2 + 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 + + # 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 0db5c6fb..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@v2 + - uses: actions/checkout@v7 - - name: Set up JDK 8 - uses: actions/setup-java@v2 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: - java-version: '8' + java-version: 17 distribution: 'temurin' - name: Write gradle.properties diff --git a/README.md b/README.md index ec45ac99..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: @@ -529,7 +508,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/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..be702b3e 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,20 +67,19 @@ 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" } - // 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') { @@ -152,7 +157,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..fa83c923 100644 --- a/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt +++ b/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt @@ -1,125 +1,145 @@ 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 + +abstract class CargoBuildTask : DefaultTask() { + @get:Inject + abstract val execOperations: ExecOperations + + @get:Inject + abstract val fileSystemOperations: FileSystemOperations -open class CargoBuildTask : DefaultTask() { @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(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 = "" + + @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 - inline fun buildProjectForTarget(project: Project, toolchain: Toolchain, ndk: Ndk, cargoExtension: CargoExtension) { - val apiLevel = cargoExtension.apiLevels[toolchain.platform]!! - val defaultTargetTriple = getDefaultTargetTriple(project, cargoExtension.rustcCommand) + @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() - project.exec { spec -> + @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 // 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,13 +157,14 @@ open class CargoBuildTask : DefaultTask() { theCommandLine.add(features.featureSet.joinToString(" ")) } } + 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 @@ -158,13 +179,13 @@ open 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. @@ -172,7 +193,7 @@ open 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)) { @@ -188,20 +209,20 @@ open 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. @@ -212,51 +233,78 @@ open 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(project: Project, rustc: String): String? { +fun getDefaultTargetTriple(execOperations: ExecOperations, logger: org.gradle.api.logging.Logger, 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") } if (result.exitValue != 0) { - project.logger.warn( + logger.warn( "Failed to get default target triple from rustc (exit code: ${result.exitValue})") return null } @@ -271,9 +319,9 @@ fun getDefaultTargetTriple(project: Project, rustc: String): String? { ?.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 e0b0995b..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() } @@ -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 a8d3e7d5..00000000 --- a/plugin/src/main/kotlin/com/nishtahir/GenerateToolchainsTask.kt +++ /dev/null @@ -1,59 +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 - -open class GenerateToolchainsTask : DefaultTask() { - - @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) - project.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 51662749..23732cc8 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 @@ -107,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() } @@ -117,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) { @@ -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 @@ -276,7 +231,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 } @@ -289,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`.") @@ -307,11 +257,39 @@ 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 + } } - if (!usePrebuilt) { - targetBuildTask.dependsOn(generateToolchain!!) - } targetBuildTask.dependsOn(generateLinkerWrapper) buildTask.dependsOn(targetBuildTask) } diff --git a/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy b/plugin/src/test/groovy/com/nishtahir/CargoTargetTest.groovy index b44f749f..d6d20827 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 = "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 + 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/NdkVersionTest.groovy b/plugin/src/test/groovy/com/nishtahir/NdkVersionTest.groovy index b409b408..a18c97e1 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() @@ -56,9 +61,10 @@ 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 - "26.3.11579264", - "27.2.12479018", + // 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/SimpleAndroidApp.groovy b/plugin/src/test/groovy/com/nishtahir/SimpleAndroidApp.groovy index c539083e..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") - if (isAndroid34x) { + if (ndkVersion != null) { return """ndkVersion '${ndkVersion}'""" } else { return "" @@ -275,7 +272,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 +300,6 @@ class SimpleAndroidApp { Builder withAndroidVersion(VersionNumber androidVersion) { this.androidVersion = androidVersion - if (this.androidVersion < android("3.4.0")) { - this.ndkVersion = null - } return this } @@ -333,6 +327,9 @@ class SimpleAndroidApp { } SimpleAndroidApp build() { + if (ndkVersion == null && androidVersion >= android("3.4.0")) { + ndkVersion = VersionNumber.parse("29.0.14206865") + } return new SimpleAndroidApp(projectDir, cacheDir, androidVersion, ndkVersion, kotlinVersion, kotlinEnabled, kaptWorkersEnabled) } } 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() + } } } diff --git a/samples/app/build.gradle b/samples/app/build.gradle index 2c4bd139..66351263 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,13 +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" @@ -55,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/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/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..672896ca 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,12 +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" @@ -64,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/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/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..932af296 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,11 +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" @@ -31,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" } @@ -40,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 @@ -85,7 +90,13 @@ 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" +} + +// Java 17: Required for Gradle 8.5+ and AGP 8.x +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } afterEvaluate { 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') } } 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 @@ - +