Skip to content

Commit c33f1ee

Browse files
author
Thom Chiovoloni
authored
Merge pull request #17 from MaulingMonkey/ndk-r19-toolchains
Add support for NDK r19 prebuilt toolchains
2 parents c5e93af + 50a8cca commit c33f1ee

File tree

5 files changed

+149
-38
lines changed

5 files changed

+149
-38
lines changed

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,21 @@ cargo {
156156
}
157157
```
158158

159+
### prebuiltToolchains
160+
161+
When set to `true` (which requires NDK version 19+), use the prebuilt toolchains bundled with the
162+
NDK. When set to `false`, generate per-target architecture standalone NDK toolchains using
163+
`make_standalone_toolchain.py`. When unset, use the prebuilt toolchains if the NDK version is 19+,
164+
and fall back to generated toolchains for older NDK versions.
165+
166+
Defaults to `null`.
167+
168+
```groovy
169+
cargo {
170+
prebuiltToolchains = true
171+
}
172+
```
173+
159174
### verbose
160175

161176
When set, execute `cargo build` with or without the `--verbose` flag. When unset, respect the
@@ -302,8 +317,13 @@ cargo {
302317

303318
## Specifying NDK toolchains
304319

305-
The plugin looks for (and will generate) per-target architecture standalone NDK toolchains as
306-
generated by `make_standalone_toolchain.py`.
320+
The plugin can either use prebuilt NDK toolchain binaries, or search for (and if missing, build)
321+
NDK toolchains as generated by `make_standalone_toolchain.py`.
322+
323+
A prebuilt NDK toolchain will be used if:
324+
1. `rust.prebuiltToolchain=true` in the per-(multi-)project `${rootDir}/local.properties`
325+
1. `prebuiltToolchain=true` in the `cargo { ... }` block (if not overridden by `local.properties`)
326+
1. The discovered NDK is version 19 or higher (if not overridden per above)
307327

308328
The toolchains are rooted in a single Android NDK toolchain directory. In order of preference, the
309329
toolchain root directory is determined by:

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.nishtahir;
22

33
import com.android.build.gradle.*
4+
import org.apache.tools.ant.taskdefs.condition.Os
45
import org.gradle.api.DefaultTask
56
import org.gradle.api.GradleException
67
import org.gradle.api.Project
@@ -139,11 +140,29 @@ open class CargoBuildTask : DefaultTask() {
139140
}
140141

141142
// Cross-compiling to Android requires toolchain massaging.
142-
if (toolchain.type == ToolchainType.ANDROID) {
143+
if (toolchain.type != ToolchainType.DESKTOP) {
144+
val toolchainDirectory = if (toolchain.type == ToolchainType.ANDROID_PREBUILT) {
145+
val ndkPath = app.ndkDirectory
146+
val hostTag = if (Os.isFamily(Os.FAMILY_WINDOWS)) {
147+
if (Os.isArch("x86_64") || Os.isArch("amd64")) {
148+
"windows-x86_64"
149+
} else {
150+
"windows"
151+
}
152+
} else if (Os.isFamily(Os.FAMILY_MAC)) {
153+
"darwin-x86_64"
154+
} else {
155+
"linux-x86_64"
156+
}
157+
File("$ndkPath/toolchains/llvm/prebuilt", hostTag)
158+
} else {
159+
cargoExtension.toolchainDirectory
160+
}
161+
143162
// Be aware that RUSTFLAGS can have problems with embedded
144163
// spaces, but that shouldn't be a problem here.
145-
val cc = File(cargoExtension.toolchainDirectory, "${toolchain.cc(apiLevel)}").path;
146-
val ar = File(cargoExtension.toolchainDirectory, "${toolchain.ar(apiLevel)}").path;
164+
val cc = File(toolchainDirectory, "${toolchain.cc(apiLevel)}").path;
165+
val ar = File(toolchainDirectory, "${toolchain.ar(apiLevel)}").path;
147166

148167
// For cargo: like "CARGO_TARGET_I686_LINUX_ANDROID_CC". This is really weakly
149168
// documented; see https://github.com/rust-lang/cargo/issues/5690 and follow

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ open class CargoExtension {
3535
var module: String? = null
3636
var libname: String? = null
3737
var targets: List<String>? = null
38+
var prebuiltToolchains: Boolean? = null
3839
var profile: String = "debug"
3940
var verbose: Boolean? = null
4041
var targetDirectory: String? = null

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ open class GenerateToolchainsTask : DefaultTask() {
3131
val targets = cargoExtension.targets!!
3232

3333
toolchains
34-
.filter { it.type == ToolchainType.ANDROID }
34+
.filter { it.type == ToolchainType.ANDROID_GENERATED }
3535
.filter { (arch) -> targets.contains(arch) }
3636
.forEach { (arch) ->
3737
if (arch.endsWith("64") && apiLevel < 21) {

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

Lines changed: 103 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import java.util.Properties
1111
const val RUST_TASK_GROUP = "rust"
1212

1313
enum class ToolchainType {
14-
ANDROID,
14+
ANDROID_PREBUILT,
15+
ANDROID_GENERATED,
1516
DESKTOP,
1617
}
1718

@@ -20,67 +21,104 @@ val toolchains = listOf(
2021
Toolchain("linux-x86-64",
2122
ToolchainType.DESKTOP,
2223
"x86_64-unknown-linux-gnu",
23-
"<cc>",
24-
"<ar>",
24+
"<compilerTriple>",
25+
"<binutilsTriple>",
2526
"desktop/linux-x86-64"),
2627
Toolchain("darwin",
2728
ToolchainType.DESKTOP,
2829
"x86_64-apple-darwin",
29-
"<cc>",
30-
"<ar>",
30+
"<compilerTriple>",
31+
"<binutilsTriple>",
3132
"desktop/darwin"),
3233
Toolchain("win32-x86-64-msvc",
3334
ToolchainType.DESKTOP,
3435
"x86_64-pc-windows-msvc",
35-
"<cc>",
36-
"<ar>",
36+
"<compilerTriple>",
37+
"<binutilsTriple>",
3738
"desktop/win32-x86-64"),
3839
Toolchain("win32-x86-64-gnu",
3940
ToolchainType.DESKTOP,
4041
"x86_64-pc-windows-gnu",
41-
"<cc>",
42-
"<ar>",
42+
"<compilerTriple>",
43+
"<binutilsTriple>",
4344
"desktop/win32-x86-64"),
4445
Toolchain("arm",
45-
ToolchainType.ANDROID,
46+
ToolchainType.ANDROID_GENERATED,
4647
"armv7-linux-androideabi",
47-
"bin/arm-linux-androideabi-clang",
48-
"bin/arm-linux-androideabi-ar",
48+
"arm-linux-androideabi",
49+
"arm-linux-androideabi",
4950
"android/armeabi-v7a"),
5051
Toolchain("arm64",
51-
ToolchainType.ANDROID,
52+
ToolchainType.ANDROID_GENERATED,
53+
"aarch64-linux-android",
54+
"aarch64-linux-android",
5255
"aarch64-linux-android",
53-
"bin/aarch64-linux-android-clang",
54-
"bin/aarch64-linux-android-ar",
5556
"android/arm64-v8a"),
5657
Toolchain("x86",
57-
ToolchainType.ANDROID,
58+
ToolchainType.ANDROID_GENERATED,
59+
"i686-linux-android",
60+
"i686-linux-android",
5861
"i686-linux-android",
59-
"bin/i686-linux-android-clang",
60-
"bin/i686-linux-android-ar",
6162
"android/x86"),
6263
Toolchain("x86_64",
63-
ToolchainType.ANDROID,
64+
ToolchainType.ANDROID_GENERATED,
65+
"x86_64-linux-android",
66+
"x86_64-linux-android",
67+
"x86_64-linux-android",
68+
"android/x86_64"),
69+
Toolchain("arm",
70+
ToolchainType.ANDROID_PREBUILT,
71+
"armv7-linux-androideabi", // This is correct. "Note: For 32-bit ARM, the compiler is prefixed with
72+
"armv7a-linux-androideabi", // armv7a-linux-androideabi, but the binutils tools are prefixed with
73+
"arm-linux-androideabi", // arm-linux-androideabi. For other architectures, the prefixes are the same
74+
"android/armeabi-v7a"), // for all tools." (Ref: https://developer.android.com/ndk/guides/other_build_systems#overview )
75+
Toolchain("arm64",
76+
ToolchainType.ANDROID_PREBUILT,
77+
"aarch64-linux-android",
78+
"aarch64-linux-android",
79+
"aarch64-linux-android",
80+
"android/arm64-v8a"),
81+
Toolchain("x86",
82+
ToolchainType.ANDROID_PREBUILT,
83+
"i686-linux-android",
84+
"i686-linux-android",
85+
"i686-linux-android",
86+
"android/x86"),
87+
Toolchain("x86_64",
88+
ToolchainType.ANDROID_PREBUILT,
89+
"x86_64-linux-android",
90+
"x86_64-linux-android",
6491
"x86_64-linux-android",
65-
"bin/x86_64-linux-android-clang",
66-
"bin/x86_64-linux-android-ar",
6792
"android/x86_64")
6893
)
6994

7095
data class Toolchain(val platform: String,
7196
val type: ToolchainType,
7297
val target: String,
73-
val cc: String,
74-
val ar: String,
98+
val compilerTriple: String,
99+
val binutilsTriple: String,
75100
val folder: String) {
76101
fun cc(apiLevel: Int): File =
77102
if (System.getProperty("os.name").startsWith("Windows")) {
78-
File("$platform-$apiLevel", "$cc.cmd")
103+
if (type == ToolchainType.ANDROID_PREBUILT) {
104+
File("bin", "$compilerTriple$apiLevel-clang.cmd")
105+
} else {
106+
File("$platform-$apiLevel/bin", "$compilerTriple-clang.cmd")
107+
}
79108
} else {
80-
File("$platform-$apiLevel", "$cc")
109+
if (type == ToolchainType.ANDROID_PREBUILT) {
110+
File("bin", "$compilerTriple$apiLevel-clang")
111+
} else {
112+
File("$platform-$apiLevel/bin", "$compilerTriple-clang")
113+
}
81114
}
82115

83-
fun ar(apiLevel: Int): File = File("$platform-$apiLevel", "$ar")
116+
fun ar(apiLevel: Int): File =
117+
if (type == ToolchainType.ANDROID_PREBUILT) {
118+
File("bin", "$binutilsTriple-ar")
119+
} else {
120+
File("$platform-$apiLevel/bin", "$binutilsTriple-ar")
121+
}
84122
}
85123

86124
@Suppress("unused")
@@ -136,10 +174,33 @@ open class RustAndroidPlugin : Plugin<Project> {
136174
sourceSets.getByName("test").resources.srcDir(File("$buildDir/rustJniLibs/desktop"))
137175
}
138176

139-
val generateToolchain = tasks.maybeCreate("generateToolchains",
140-
GenerateToolchainsTask::class.java).apply {
141-
group = RUST_TASK_GROUP
142-
description = "Generate standard toolchain for given architectures"
177+
// Determine the NDK version, if present
178+
val ndkSourceProperties = Properties()
179+
val ndkSourcePropertiesFile = File(extensions[T::class].ndkDirectory, "source.properties")
180+
if (ndkSourcePropertiesFile.exists()) {
181+
ndkSourceProperties.load(ndkSourcePropertiesFile.inputStream())
182+
}
183+
val ndkVersion = ndkSourceProperties.getProperty("Pkg.Revision", "0.0")
184+
val ndkVersionMajor = ndkVersion.split(".").first().toInt()
185+
186+
// Determine whether to use prebuilt or generated toolchains
187+
val usePrebuilt =
188+
cargoExtension.localProperties.getProperty("rust.prebuiltToolchains")?.equals("true") ?:
189+
cargoExtension.prebuiltToolchains ?:
190+
(ndkVersionMajor >= 19);
191+
192+
if (usePrebuilt && ndkVersionMajor < 19) {
193+
throw GradleException("usePrebuilt = true requires NDK version 19+")
194+
}
195+
196+
val generateToolchain = if (!usePrebuilt) {
197+
tasks.maybeCreate("generateToolchains",
198+
GenerateToolchainsTask::class.java).apply {
199+
group = RUST_TASK_GROUP
200+
description = "Generate standard toolchain for given architectures"
201+
}
202+
} else {
203+
null
143204
}
144205

145206
// Fish linker wrapper scripts from our Java resources.
@@ -167,7 +228,15 @@ open class RustAndroidPlugin : Plugin<Project> {
167228
}
168229

169230
cargoExtension.targets!!.forEach { target ->
170-
val theToolchain = toolchains.find { it.platform == target }
231+
val theToolchain = toolchains
232+
.filter {
233+
if (usePrebuilt) {
234+
it.type != ToolchainType.ANDROID_GENERATED
235+
} else {
236+
it.type != ToolchainType.ANDROID_PREBUILT
237+
}
238+
}
239+
.find { it.platform == target }
171240
if (theToolchain == null) {
172241
throw GradleException("Target ${target} is not recognized (recognized targets: ${toolchains.map { it.platform }.sorted()}). Check `local.properties` and `build.gradle`.")
173242
}
@@ -179,7 +248,9 @@ open class RustAndroidPlugin : Plugin<Project> {
179248
toolchain = theToolchain
180249
}
181250

182-
targetBuildTask.dependsOn(generateToolchain)
251+
if (!usePrebuilt) {
252+
targetBuildTask.dependsOn(generateToolchain!!)
253+
}
183254
targetBuildTask.dependsOn(generateLinkerWrapper)
184255
buildTask.dependsOn(targetBuildTask)
185256
}

0 commit comments

Comments
 (0)