Skip to content

Commit 1de70a7

Browse files
committed
Add per build type cargoBuild${buildType} tasks
The goal of this change is to support explicitly building particular Cargo profiles per Android build type. This change makes it possible to build both release and debug tasks in a single gradle invocation without editing the use of the `cargo` extension. There are some backwards incompatible changes present: `profile` is deprecated and is replaced with the *required* map `buildTypeToProfile`. This map controls which `cargoBuild${buildType}` tasks are created and what Cargo profile is used for each. Once rust-lang/cargo#6988 is resolved and stabilized, we should switch the implementation to use `cargo build --profile=$x` explicitly rather than `--release`.
1 parent aff8cdb commit 1de70a7

File tree

6 files changed

+83
-54
lines changed

6 files changed

+83
-54
lines changed

README.md

+17-14
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,17 @@ rustup target add x86_64-pc-windows-msvc # for win32-x86-64-msvc
5353
...
5454
```
5555

56-
Finally, run the `cargoBuild` task to cross compile:
56+
Finally, run a `cargoBuild${buildType}` task (see `buildTypeToProfile`) to cross compile:
5757
```sh
58-
./gradlew cargoBuild
58+
./gradlew cargoBuildDebug
5959
```
6060
Or add it as a dependency to one of your other build tasks, to build your rust code when you normally build your project:
6161
```gradle
6262
tasks.whenTaskAdded { task ->
63-
if ((task.name == 'javaPreCompileDebug' || task.name == 'javaPreCompileRelease')) {
64-
task.dependsOn 'cargoBuild'
63+
if (task.name == 'javaPreCompileDebug') {
64+
task.dependsOn 'cargoBuildDebug'
65+
} else if (task.name == 'javaPreCompileRelease') {
66+
task.dependsOn 'cargoBuildRelease'
6567
}
6668
}
6769
```
@@ -99,11 +101,6 @@ which can be specified using the `apiLevel` option. This option defaults to the
99101
level. As of API level 21, 64-bit builds are possible; and conversely, the `arm64` and `x86_64`
100102
targets require `apiLevel >= 21`.
101103

102-
### Cargo release profile
103-
104-
The `profile` option selects between the `--debug` and `--release` profiles in `cargo`. *Defaults
105-
to `debug`!*
106-
107104
### Extension reference
108105

109106
### module
@@ -190,18 +187,24 @@ cargo {
190187
}
191188
```
192189

193-
### profile
194-
195-
The Cargo [release profile](https://doc.rust-lang.org/book/second-edition/ch14-01-release-profiles.html#customizing-builds-with-release-profiles) to build.
190+
### buildTypeToProfile
196191

197-
Defaults to `"debug"`.
192+
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).
193+
Each entry in the map causes a new `cargoBuild${buildType}` task to be created.
198194

199195
```groovy
200196
cargo {
201-
profile = 'release'
197+
buildTypeToProfile = [
198+
"debug": "debug",
199+
"alpha": "debug",
200+
"beta": "release",
201+
"release": "release",
202+
]
202203
}
203204
```
204205

206+
The above example creates these targets: `cargoBuildDebug`, `cargoBuildAlpha`, `cargoBuildBeta`, `cargoBuildRelease`.
207+
205208
### features
206209

207210
Set the Cargo [features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section).

plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt

+19-8
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import org.gradle.api.DefaultTask
66
import org.gradle.api.GradleException
77
import org.gradle.api.Project
88
import org.gradle.api.logging.LogLevel
9+
import org.gradle.api.tasks.Input;
910
import org.gradle.api.tasks.TaskAction
1011
import java.io.ByteArrayOutputStream
1112
import java.io.File
1213

1314
open class CargoBuildTask : DefaultTask() {
15+
@Input
1416
var toolchain: Toolchain? = null
17+
@Input
18+
var profile: String? = null
1519

1620

1721
@Suppress("unused")
@@ -24,10 +28,15 @@ open class CargoBuildTask : DefaultTask() {
2428
throw GradleException("toolchain cannot be null")
2529
}
2630

31+
var profile = profile
32+
if (profile == null) {
33+
throw GradleException("profile cannot be null")
34+
}
35+
2736
project.plugins.all {
2837
when (it) {
29-
is AppPlugin -> buildProjectForTarget<AppExtension>(project, toolchain, this)
30-
is LibraryPlugin -> buildProjectForTarget<LibraryExtension>(project, toolchain, this)
38+
is AppPlugin -> buildProjectForTarget<AppExtension>(project, toolchain, profile, this)
39+
is LibraryPlugin -> buildProjectForTarget<LibraryExtension>(project, toolchain, profile, this)
3140
}
3241
}
3342
// CARGO_TARGET_DIR can be used to force the use of a global, shared target directory
@@ -68,7 +77,7 @@ open class CargoBuildTask : DefaultTask() {
6877
}
6978
}
7079

71-
inline fun <reified T : BaseExtension> buildProjectForTarget(project: Project, toolchain: Toolchain, cargoExtension: CargoExtension) {
80+
inline fun <reified T : BaseExtension> buildProjectForTarget(project: Project, toolchain: Toolchain, profile: String, cargoExtension: CargoExtension) {
7281
val app = project.extensions[T::class]
7382
val apiLevel = cargoExtension.apiLevels[toolchain.platform]!!
7483
val defaultTargetTriple = getDefaultTargetTriple(project, cargoExtension.rustcCommand)
@@ -120,12 +129,14 @@ open class CargoBuildTask : DefaultTask() {
120129
}
121130
}
122131

123-
if (cargoExtension.profile != "debug") {
124-
// Cargo is rigid: it accepts "--release" for release (and
125-
// nothing for dev). This is a cheap way of allowing only
126-
// two values.
127-
theCommandLine.add("--${cargoExtension.profile}")
132+
// TODO: When --profile is stabilized use it instead of --release
133+
// https://github.com/rust-lang/cargo/issues/6988
134+
if (profile == "release") {
135+
theCommandLine.add("--release")
136+
} else if (profile != "dev") {
137+
throw GradleException("Profile may only be 'dev' or 'release', got '${profile}'")
128138
}
139+
129140
if (toolchain.target != defaultTargetTriple) {
130141
// Only providing --target for the non-default targets means desktop builds
131142
// can share the build cache with `cargo build`/`cargo test`/etc invocations,

plugin/src/main/kotlin/com/nishtahir/CargoExtension.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ open class CargoExtension {
3737
var libname: String? = null
3838
var targets: List<String>? = null
3939
var prebuiltToolchains: Boolean? = null
40-
var profile: String = "debug"
40+
var buildTypeToProfile: Map<String, String> = mapOf()
4141
var verbose: Boolean? = null
4242
var targetDirectory: String? = null
4343
var targetIncludes: Array<String>? = null

plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt

+34-27
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ open class RustAndroidPlugin : Plugin<Project> {
172172
throw GradleException("libname cannot be null")
173173
}
174174

175+
if (cargoExtension.buildTypeToProfile.isEmpty()) {
176+
throw GradleException("buildTypeToProfile must have entries")
177+
}
178+
175179
// Allow to set targets, including per-project, in local.properties.
176180
val localTargets: String? =
177181
cargoExtension.localProperties.getProperty("rust.targets.${project.name}") ?:
@@ -256,38 +260,41 @@ open class RustAndroidPlugin : Plugin<Project> {
256260
includeEmptyDirs = false
257261
}
258262

259-
val buildTask = tasks.maybeCreate("cargoBuild",
260-
DefaultTask::class.java).apply {
261-
group = RUST_TASK_GROUP
262-
description = "Build library (all targets)"
263-
}
263+
cargoExtension.buildTypeToProfile.forEach { buildType, theProfile ->
264+
val buildTask = tasks.maybeCreate("cargoBuild${buildType.capitalize()}",
265+
DefaultTask::class.java).apply {
266+
group = RUST_TASK_GROUP
267+
description = "Build library (all targets) for android build type ${buildType}"
268+
}
264269

265-
cargoExtension.targets!!.forEach { target ->
266-
val theToolchain = toolchains
267-
.filter {
268-
if (usePrebuilt) {
269-
it.type != ToolchainType.ANDROID_GENERATED
270-
} else {
271-
it.type != ToolchainType.ANDROID_PREBUILT
270+
cargoExtension.targets!!.forEach { target ->
271+
val theToolchain = toolchains
272+
.filter {
273+
if (usePrebuilt) {
274+
it.type != ToolchainType.ANDROID_GENERATED
275+
} else {
276+
it.type != ToolchainType.ANDROID_PREBUILT
277+
}
272278
}
273-
}
274-
.find { it.platform == target }
275-
if (theToolchain == null) {
276-
throw GradleException("Target ${target} is not recognized (recognized targets: ${toolchains.map { it.platform }.sorted()}). Check `local.properties` and `build.gradle`.")
277-
}
279+
.find { it.platform == target }
280+
if (theToolchain == null) {
281+
throw GradleException("Target ${target} is not recognized (recognized targets: ${toolchains.map { it.platform }.sorted()}). Check `local.properties` and `build.gradle`.")
282+
}
278283

279-
val targetBuildTask = tasks.maybeCreate("cargoBuild${target.capitalize()}",
280-
CargoBuildTask::class.java).apply {
281-
group = RUST_TASK_GROUP
282-
description = "Build library ($target)"
283-
toolchain = theToolchain
284-
}
284+
val targetBuildTask = tasks.maybeCreate("cargoBuild${target.capitalize()}${buildType.capitalize()}",
285+
CargoBuildTask::class.java).apply {
286+
group = RUST_TASK_GROUP
287+
description = "Build library ($target) for android build type ${buildType}"
288+
toolchain = theToolchain
289+
profile = theProfile
290+
}
285291

286-
if (!usePrebuilt) {
287-
targetBuildTask.dependsOn(generateToolchain!!)
292+
if (!usePrebuilt) {
293+
targetBuildTask.dependsOn(generateToolchain!!)
294+
}
295+
targetBuildTask.dependsOn(generateLinkerWrapper)
296+
buildTask.dependsOn(targetBuildTask)
288297
}
289-
targetBuildTask.dependsOn(generateLinkerWrapper)
290-
buildTask.dependsOn(targetBuildTask)
291298
}
292299
}
293300
}

samples/app/build.gradle

+6-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ cargo {
3737
module = "../rust"
3838
targets = ["arm", "x86", "x86_64", "arm64"]
3939
libname = "rust"
40+
buildTypeToProfile = [
41+
"debug": "dev",
42+
"release": "dev",
43+
]
4044
}
4145

4246
repositories {
@@ -54,13 +58,13 @@ dependencies {
5458
}
5559

5660
afterEvaluate {
57-
// The `cargoBuild` task isn't available until after evaluation.
61+
// The `cargoBuild...` tasks aren't available until after evaluation.
5862
android.applicationVariants.all { variant ->
5963
def productFlavor = ""
6064
variant.productFlavors.each {
6165
productFlavor += "${it.name.capitalize()}"
6266
}
6367
def buildType = "${variant.buildType.name.capitalize()}"
64-
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"])
68+
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild${buildType}"])
6569
}
6670
}

samples/library/build.gradle

+6-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ cargo {
4242
"darwin",
4343
]
4444
libname = "rust"
45+
buildTypeToProfile = [
46+
"debug": "dev",
47+
"release": "dev",
48+
]
4549

4650
features {
4751
defaultAnd "foo", "bar"
@@ -68,13 +72,13 @@ dependencies {
6872
}
6973

7074
afterEvaluate {
71-
// The `cargoBuild` task isn't available until after evaluation.
75+
// The `cargoBuild...` tasks aren't available until after evaluation.
7276
android.libraryVariants.all { variant ->
7377
def productFlavor = ""
7478
variant.productFlavors.each {
7579
productFlavor += "${it.name.capitalize()}"
7680
}
7781
def buildType = "${variant.buildType.name.capitalize()}"
78-
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"])
82+
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild${buildType}"])
7983
}
8084
}

0 commit comments

Comments
 (0)