diff --git a/README.md b/README.md index 9eacbcd..b030d9b 100644 --- a/README.md +++ b/README.md @@ -65,15 +65,17 @@ rustup target add x86_64-pc-windows-msvc # for win32-x86-64-msvc ... ``` -Finally, run the `cargoBuild` task to cross compile: +Finally, run a `cargoBuild${buildType}` task (see `buildTypeToProfile`) to cross compile: ```sh -./gradlew cargoBuild +./gradlew cargoBuildDebug ``` Or add it as a dependency to one of your other build tasks, to build your rust code when you normally build your project: ```gradle tasks.whenTaskAdded { task -> - if ((task.name == 'javaPreCompileDebug' || task.name == 'javaPreCompileRelease')) { - task.dependsOn 'cargoBuild' + if (task.name == 'javaPreCompileDebug') { + task.dependsOn 'cargoBuildDebug' + } else if (task.name == 'javaPreCompileRelease') { + task.dependsOn 'cargoBuildRelease' } } ``` @@ -111,11 +113,6 @@ which can be specified using the `apiLevel` option. This option defaults to the level. As of API level 21, 64-bit builds are possible; and conversely, the `arm64` and `x86_64` targets require `apiLevel >= 21`. -### Cargo release profile - -The `profile` option selects between the `--debug` and `--release` profiles in `cargo`. *Defaults -to `debug`!* - ### Extension reference ### module @@ -202,18 +199,24 @@ cargo { } ``` -### profile - -The Cargo [release profile](https://doc.rust-lang.org/book/second-edition/ch14-01-release-profiles.html#customizing-builds-with-release-profiles) to build. +### buildTypeToProfile -Defaults to `"debug"`. +This mandatory option specifies the Cargo [release profile](https://doc.rust-lang.org/book/second-edition/ch14-01-release-profiles.html#customizing-builds-with-release-profiles) to build per [Android build type](https://developer.android.com/studio/build/build-variants#build-types). +Each entry in the map causes a new `cargoBuild${buildType}` task to be created. ```groovy cargo { - profile = 'release' + buildTypeToProfile = [ + "debug": "debug", + "alpha": "debug", + "beta": "release", + "release": "release", + ] } ``` +The above example creates these targets: `cargoBuildDebug`, `cargoBuildAlpha`, `cargoBuildBeta`, `cargoBuildRelease`. + ### features Set the Cargo [features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). diff --git a/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt b/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt index 78e7cf4..89e069e 100644 --- a/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt +++ b/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt @@ -6,12 +6,16 @@ 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.TaskAction import java.io.ByteArrayOutputStream import java.io.File open class CargoBuildTask : DefaultTask() { + @Input var toolchain: Toolchain? = null + @Input + var profile: String? = null @Suppress("unused") @@ -24,10 +28,15 @@ open class CargoBuildTask : DefaultTask() { throw GradleException("toolchain cannot be null") } + var profile = profile + if (profile == null) { + throw GradleException("profile cannot be null") + } + project.plugins.all { when (it) { - is AppPlugin -> buildProjectForTarget(project, toolchain, this) - is LibraryPlugin -> buildProjectForTarget(project, toolchain, this) + is AppPlugin -> buildProjectForTarget(project, toolchain, profile, this) + is LibraryPlugin -> buildProjectForTarget(project, toolchain, profile, this) } } // CARGO_TARGET_DIR can be used to force the use of a global, shared target directory @@ -44,10 +53,18 @@ open class CargoBuildTask : DefaultTask() { val defaultTargetTriple = getDefaultTargetTriple(project, rustcCommand) + // See https://rust-lang.github.io/rfcs/2678-named-custom-cargo-profiles.html#new-dir-name-attribute + // Right now "dev" maps to "debug", but depending on how RFC 2678 gets merged in the end, we may need to read a property out of `cargo metadata` + var profileDirName = if (profile == "dev") { + "debug" + } else { + profile + } + val cargoOutputDir = if (toolchain.target == defaultTargetTriple) { - "${targetDirectory}/${profile}" + "${targetDirectory}/${profileDirName}" } else { - "${targetDirectory}/${toolchain.target}/${profile}" + "${targetDirectory}/${toolchain.target}/${profileDirName}" } copy { spec -> spec.from(File(project.projectDir, cargoOutputDir)) @@ -68,7 +85,7 @@ open class CargoBuildTask : DefaultTask() { } } - inline fun buildProjectForTarget(project: Project, toolchain: Toolchain, cargoExtension: CargoExtension) { + inline fun buildProjectForTarget(project: Project, toolchain: Toolchain, profile: String, cargoExtension: CargoExtension) { val app = project.extensions[T::class] val apiLevel = cargoExtension.apiLevels[toolchain.platform]!! val defaultTargetTriple = getDefaultTargetTriple(project, cargoExtension.rustcCommand) @@ -120,12 +137,14 @@ open class CargoBuildTask : DefaultTask() { } } - if (cargoExtension.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}") + // TODO: When --profile is stabilized use it instead of --release + // https://github.com/rust-lang/cargo/issues/6988 + if (profile == "release") { + theCommandLine.add("--release") + } else if (profile != "dev") { + throw GradleException("Profile may only be 'dev' or 'release', got '${profile}'") } + if (toolchain.target != defaultTargetTriple) { // Only providing --target for the non-default targets means desktop builds // can share the build cache with `cargo build`/`cargo test`/etc invocations, diff --git a/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt b/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt index 791d314..e741e41 100644 --- a/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt +++ b/plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt @@ -37,7 +37,7 @@ open class CargoExtension { var libname: String? = null var targets: List? = null var prebuiltToolchains: Boolean? = null - var profile: String = "debug" + var buildTypeToProfile: Map = mapOf() var verbose: Boolean? = null var targetDirectory: String? = null var targetIncludes: Array? = null diff --git a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt index 461fcb8..2dd2192 100644 --- a/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt +++ b/plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt @@ -172,6 +172,10 @@ open class RustAndroidPlugin : Plugin { throw GradleException("libname cannot be null") } + if (cargoExtension.buildTypeToProfile.isEmpty()) { + throw GradleException("buildTypeToProfile must have entries") + } + // Allow to set targets, including per-project, in local.properties. val localTargets: String? = cargoExtension.localProperties.getProperty("rust.targets.${project.name}") ?: @@ -256,38 +260,41 @@ open class RustAndroidPlugin : Plugin { includeEmptyDirs = false } - val buildTask = tasks.maybeCreate("cargoBuild", - DefaultTask::class.java).apply { - group = RUST_TASK_GROUP - description = "Build library (all targets)" - } + cargoExtension.buildTypeToProfile.forEach { buildType, theProfile -> + val buildTask = tasks.maybeCreate("cargoBuild${buildType.capitalize()}", + DefaultTask::class.java).apply { + group = RUST_TASK_GROUP + description = "Build library (all targets) for android build type ${buildType}" + } - cargoExtension.targets!!.forEach { target -> - val theToolchain = toolchains - .filter { - if (usePrebuilt) { - it.type != ToolchainType.ANDROID_GENERATED - } else { - it.type != ToolchainType.ANDROID_PREBUILT + 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`.") - } + .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`.") + } - val targetBuildTask = tasks.maybeCreate("cargoBuild${target.capitalize()}", - CargoBuildTask::class.java).apply { - group = RUST_TASK_GROUP - description = "Build library ($target)" - toolchain = theToolchain - } + val targetBuildTask = tasks.maybeCreate("cargoBuild${target.capitalize()}${buildType.capitalize()}", + CargoBuildTask::class.java).apply { + group = RUST_TASK_GROUP + description = "Build library ($target) for android build type ${buildType}" + toolchain = theToolchain + profile = theProfile + } - if (!usePrebuilt) { - targetBuildTask.dependsOn(generateToolchain!!) + if (!usePrebuilt) { + targetBuildTask.dependsOn(generateToolchain!!) + } + targetBuildTask.dependsOn(generateLinkerWrapper) + buildTask.dependsOn(targetBuildTask) } - targetBuildTask.dependsOn(generateLinkerWrapper) - buildTask.dependsOn(targetBuildTask) } } } diff --git a/samples/app/build.gradle b/samples/app/build.gradle index 6317214..5dd76b6 100644 --- a/samples/app/build.gradle +++ b/samples/app/build.gradle @@ -37,6 +37,10 @@ cargo { module = "../rust" targets = ["arm", "x86", "x86_64", "arm64"] libname = "rust" + buildTypeToProfile = [ + "debug": "dev", + "release": "dev", + ] } repositories { @@ -54,13 +58,13 @@ dependencies { } afterEvaluate { - // The `cargoBuild` task isn't available until after evaluation. + // The `cargoBuild...` tasks aren't available until after evaluation. android.applicationVariants.all { variant -> def productFlavor = "" variant.productFlavors.each { productFlavor += "${it.name.capitalize()}" } def buildType = "${variant.buildType.name.capitalize()}" - tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"]) + tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild${buildType}"]) } } diff --git a/samples/library/build.gradle b/samples/library/build.gradle index 97357ff..3a6e71f 100644 --- a/samples/library/build.gradle +++ b/samples/library/build.gradle @@ -42,6 +42,10 @@ cargo { "darwin", ] libname = "rust" + buildTypeToProfile = [ + "debug": "dev", + "release": "dev", + ] features { defaultAnd "foo", "bar" @@ -68,13 +72,13 @@ dependencies { } afterEvaluate { - // The `cargoBuild` task isn't available until after evaluation. + // The `cargoBuild...` tasks aren't available until after evaluation. android.libraryVariants.all { variant -> def productFlavor = "" variant.productFlavors.each { productFlavor += "${it.name.capitalize()}" } def buildType = "${variant.buildType.name.capitalize()}" - tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"]) + tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild${buildType}"]) } }