Skip to content

Commit c0d3ab0

Browse files
author
matrixdev
committed
added multi-library support
1 parent 1192d56 commit c0d3ab0

File tree

5 files changed

+90
-61
lines changed

5 files changed

+90
-61
lines changed

example/app/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ android {
3434
}
3535

3636
androidRust {
37-
path = file("src/rust_library")
37+
module("library") {
38+
path = file("src/rust_library")
39+
}
3840
minimumSupportedRustVersion = "1.62.1"
3941
}
4042

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
package dev.matrix.agp.rust
22

3-
import dev.matrix.agp.rust.utils.Abi
43
import java.io.File
54

65
@DslMarker
76
annotation class AndroidRustDslMarker
87

98
@AndroidRustDslMarker
109
@Suppress("unused")
11-
open class AndroidRustExtension {
10+
open class AndroidRustExtension : AndroidRustConfiguration() {
11+
var minimumSupportedRustVersion = ""
12+
var modules = mutableMapOf<String, AndroidRustModule>()
13+
14+
fun module(name: String, configure: AndroidRustModule.() -> Unit) {
15+
modules.getOrPut(name, ::AndroidRustModule).configure()
16+
}
17+
}
18+
19+
@AndroidRustDslMarker
20+
@Suppress("unused")
21+
class AndroidRustModule : AndroidRustConfiguration() {
1222
lateinit var path: File
1323

14-
var profile = ""
15-
var targets = Abi.values().asSequence().map { it.rustName }.toSet()
16-
var minimumSupportedRustVersion = ""
1724
var buildTypes = hashMapOf(
1825
"debug" to AndroidRustBuildType().also {
1926
it.profile = "dev"
@@ -24,13 +31,17 @@ open class AndroidRustExtension {
2431
)
2532

2633
fun buildType(name: String, configure: AndroidRustBuildType.() -> Unit) {
27-
configure(buildTypes.getOrPut(name, ::AndroidRustBuildType))
34+
buildTypes.getOrPut(name, ::AndroidRustBuildType).configure()
2835
}
2936
}
3037

3138
@AndroidRustDslMarker
3239
@Suppress("unused")
33-
open class AndroidRustBuildType {
40+
class AndroidRustBuildType : AndroidRustConfiguration()
41+
42+
@AndroidRustDslMarker
43+
@Suppress("unused")
44+
open class AndroidRustConfiguration {
3445
var profile = ""
35-
var targets = emptySet<String>()
46+
var targets = ArrayList<String>()
3647
}

plugin/src/main/java/dev/matrix/agp/rust/AndroidRustPlugin.kt

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,47 @@ class AndroidRustPlugin : Plugin<Project> {
2525
val minimumSupportedRustVersion = SemanticVersion(extension.minimumSupportedRustVersion)
2626

2727
androidComponents.finalizeDsl { dsl ->
28-
val allRustAbiSet = HashSet<Abi>()
28+
val allRustAbiSet = mutableSetOf<Abi>()
2929
val ndkDirectory = androidExtension.ndkDirectory
3030
val ndkVersion = SemanticVersion(androidExtension.ndkVersion)
3131
val extensionBuildDirectory = File(project.buildDir, "intermediates/rust")
3232

3333
for (buildType in dsl.buildTypes) {
34-
val buildTypeName = buildType.name.capitalize(Locale.getDefault())
34+
val buildTypeNameCap = buildType.name.capitalize(Locale.getDefault())
3535

3636
val variantBuildDirectory = File(extensionBuildDirectory, buildType.name)
3737
val variantJniLibsDirectory = File(variantBuildDirectory, "jniLibs")
3838

39-
val rustBuildType = extension.buildTypes[buildType.name]
40-
val rustAbiSet = resolveAbiList(project, extension.targets, rustBuildType?.targets)
41-
allRustAbiSet.addAll(rustAbiSet)
42-
43-
val cleanTaskName = "cleanRust${buildTypeName}"
39+
val cleanTaskName = "cleanRust${buildTypeNameCap}"
4440
val cleanTask = project.tasks.register(cleanTaskName, RustCleanTask::class.java) {
4541
this.variantJniLibsDirectory.set(variantJniLibsDirectory)
4642
}
4743

48-
for (rustAbi in rustAbiSet) {
49-
val buildTaskName = "buildRust${buildTypeName}[${rustAbi.androidName}]"
50-
val buildTask = project.tasks.register(buildTaskName, RustBuildTask::class.java) {
51-
this.abi.set(rustAbi)
52-
this.apiLevel.set(dsl.defaultConfig.minSdk ?: 21)
53-
this.ndkVersion.set(ndkVersion)
54-
this.ndkDirectory.set(ndkDirectory)
55-
this.rustProfile.set(rustBuildType?.profile.orEmpty().ifEmpty { extension.profile })
56-
this.rustProjectDirectory.set(extension.path)
57-
this.variantBuildDirectory.set(variantBuildDirectory)
58-
this.variantJniLibsDirectory.set(variantJniLibsDirectory)
44+
for ((moduleName, module) in extension.modules) {
45+
val moduleNameCap = moduleName.capitalize(Locale.getDefault())
46+
val moduleBuildDirectory = File(variantBuildDirectory, moduleName)
47+
48+
val rustBuildType = module.buildTypes[buildType.name]
49+
val rustConfiguration = mergeRustConfigurations(rustBuildType, module, extension)
50+
51+
val rustAbiSet = resolveAbiList(project, rustConfiguration.targets)
52+
allRustAbiSet.addAll(rustAbiSet)
53+
54+
for (rustAbi in rustAbiSet) {
55+
val buildTaskName = "buildRust${buildTypeNameCap}${moduleNameCap}[${rustAbi.androidName}]"
56+
val buildTask = project.tasks.register(buildTaskName, RustBuildTask::class.java) {
57+
this.abi.set(rustAbi)
58+
this.apiLevel.set(dsl.defaultConfig.minSdk ?: 21)
59+
this.ndkVersion.set(ndkVersion)
60+
this.ndkDirectory.set(ndkDirectory)
61+
this.rustProfile.set(rustConfiguration.profile)
62+
this.rustProjectDirectory.set(module.path)
63+
this.cargoTargetDirectory.set(moduleBuildDirectory)
64+
this.variantJniLibsDirectory.set(variantJniLibsDirectory)
65+
}
66+
buildTask.dependsOn(cleanTask)
67+
tasksByBuildType.getOrPut(buildType.name, ::ArrayList).add(buildTask)
5968
}
60-
buildTask.dependsOn(cleanTask)
61-
tasksByBuildType.getOrPut(buildType.name, ::ArrayList).add(buildTask)
6269
}
6370

6471
dsl.sourceSets.findByName(buildType.name)?.jniLibs?.srcDir(variantJniLibsDirectory)
@@ -80,30 +87,43 @@ class AndroidRustPlugin : Plugin<Project> {
8087
}
8188
}
8289

83-
private fun resolveAbiList(
84-
project: Project,
85-
extension: Collection<String>,
86-
buildType: Collection<String>?,
87-
): Collection<Abi> {
88-
val requested = when (buildType != null && buildType.isNotEmpty()) {
89-
true -> Abi.fromRustNames(buildType)
90-
else -> Abi.fromRustNames(extension)
91-
}
90+
private fun resolveAbiList(project: Project, requested: Collection<String>): Collection<Abi> {
91+
val requestedAbi = Abi.fromRustNames(requested)
9292

93-
val injected = Abi.fromInjectedBuildAbi(project)
94-
if (injected.isEmpty()) {
95-
return requested
93+
val injectedAbi = Abi.fromInjectedBuildAbi(project)
94+
if (injectedAbi.isEmpty()) {
95+
return requestedAbi
9696
}
9797

98-
val intersection = requested.intersect(injected)
99-
check(intersection.isNotEmpty()) {
100-
"ABIs requested by IDE ($injected) are not supported by the build config ($requested)"
98+
val intersectionAbi = requestedAbi.intersect(injectedAbi)
99+
check(intersectionAbi.isNotEmpty()) {
100+
"ABIs requested by IDE ($injectedAbi) are not supported by the build config ($requested)"
101101
}
102102

103103
return when {
104-
intersection.contains(Abi.Arm64) -> listOf(Abi.Arm64)
105-
intersection.contains(Abi.X86_64) -> listOf(Abi.X86_64)
106-
else -> listOf(intersection.first())
104+
intersectionAbi.contains(Abi.Arm64) -> listOf(Abi.Arm64)
105+
intersectionAbi.contains(Abi.X86_64) -> listOf(Abi.X86_64)
106+
else -> listOf(intersectionAbi.first())
107+
}
108+
}
109+
110+
private fun mergeRustConfigurations(vararg configurations: AndroidRustConfiguration?): AndroidRustConfiguration {
111+
val defaultConfiguration = AndroidRustConfiguration().also {
112+
it.profile = "release"
113+
it.targets = Abi.values().mapTo(ArrayList(), Abi::rustName)
107114
}
115+
116+
return configurations.asSequence()
117+
.filterNotNull()
118+
.plus(defaultConfiguration)
119+
.reduce { result, base ->
120+
if (result.profile.isEmpty()) {
121+
result.profile = base.profile
122+
}
123+
if (result.targets.isEmpty()) {
124+
result.targets = base.targets
125+
}
126+
result
127+
}
108128
}
109129
}

plugin/src/main/java/dev/matrix/agp/rust/RustBuildTask.kt

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal abstract class RustBuildTask : DefaultTask() {
3030
abstract val rustProjectDirectory: Property<File>
3131

3232
@get:Input
33-
abstract val variantBuildDirectory: Property<File>
33+
abstract val cargoTargetDirectory: Property<File>
3434

3535
@get:Input
3636
abstract val variantJniLibsDirectory: Property<File>
@@ -43,7 +43,7 @@ internal abstract class RustBuildTask : DefaultTask() {
4343
val ndkDirectory = ndkDirectory.get()
4444
val rustProfile = rustProfile.get()
4545
val rustProjectDirectory = rustProjectDirectory.get()
46-
val variantBuildDirectory = variantBuildDirectory.get()
46+
val cargoTargetDirectory = cargoTargetDirectory.get()
4747
val variantJniLibsDirectory = variantJniLibsDirectory.get()
4848

4949
val platform = when (Os.current) {
@@ -53,18 +53,11 @@ internal abstract class RustBuildTask : DefaultTask() {
5353
Os.Unknown -> throw Exception("OS is not supported")
5454
}
5555

56-
var toolchainFolder = ndkDirectory
57-
toolchainFolder = File(toolchainFolder, "toolchains")
58-
toolchainFolder = File(toolchainFolder, "llvm")
59-
toolchainFolder = File(toolchainFolder, "prebuilt")
60-
toolchainFolder = File(toolchainFolder, platform)
61-
toolchainFolder = File(toolchainFolder, "bin")
62-
56+
val toolchainFolder = File(ndkDirectory, "toolchains/llvm/prebuilt/$platform/bin")
6357
val cc = File(toolchainFolder, abi.cc(apiLevel))
6458
val cxx = File(toolchainFolder, abi.ccx(apiLevel))
6559
val ar = File(toolchainFolder, abi.ar(ndkVersion.major))
6660

67-
val cargoTargetDirectory = File(variantBuildDirectory, "build")
6861
val cargoTargetTriplet = abi.rustTargetTriple
6962
.replace('-', '_')
7063
.toUpperCase(Locale.getDefault())

plugin/src/main/java/dev/matrix/agp/rust/utils/Abi.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,16 @@ internal enum class Abi(
4545

4646
fun fromInjectedBuildAbi(project: Project): Set<Abi> {
4747
val values = project.properties["android.injected.build.abi"] ?: return emptySet()
48-
return values.toString().split(",").mapNotNullTo(HashSet()) { fromAndroidName(it.trim()) }
48+
return values.toString().split(",")
49+
.asSequence()
50+
.mapNotNull { fromAndroidName(it.trim()) }
51+
.toSet()
4952
}
5053

5154
fun fromRustNames(names: Collection<String>): Set<Abi> {
52-
return names.asSequence().map {
53-
requireNotNull(fromRustName(it)) { "unsupported abi version string: ${it}" }
54-
}.toSet()
55+
return names.asSequence()
56+
.map { requireNotNull(fromRustName(it)) { "unsupported abi version string: $it" } }
57+
.toSet()
5558
}
5659
}
5760

0 commit comments

Comments
 (0)