Skip to content

Commit 47b6910

Browse files
committed
build: implement native convention plugins
- Allow to use environment variables / Gradle properties to determine CMake and NDK version and build ABI - Use legacy packing for JNI libs
1 parent 50e3f2b commit 47b6910

File tree

9 files changed

+159
-61
lines changed

9 files changed

+159
-61
lines changed

.github/workflows/commit-ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ jobs:
1313
- ubuntu-22.04
1414
- macos-13
1515
- windows-2022
16+
abi:
17+
- armeabi-v7a
18+
- arm64-v8a
19+
- x86
20+
- x86_64
21+
env:
22+
BUILD_ABI: ${{ matrix.abi }}
1623
steps:
1724
- name: Fetch source code
1825
uses: actions/checkout@v4

.github/workflows/pull-request-ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ jobs:
2020
- ubuntu-22.04
2121
- macos-13
2222
- windows-2022
23+
abi:
24+
- armeabi-v7a
25+
- arm64-v8a
26+
- x86
27+
- x86_64
28+
env:
29+
BUILD_ABI: ${{ matrix.abi }}
2330
steps:
2431
- name: Fetch source code
2532
uses: actions/checkout@v4

app/build.gradle.kts

Lines changed: 11 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
@file:Suppress("UnstableApiUsage")
22

3-
import com.android.build.gradle.internal.tasks.factory.dependsOn
4-
import java.util.Properties
53
import org.gradle.configurationcache.extensions.capitalized
64

75
plugins {
6+
id("com.osfans.trime.native-app-convention")
87
id("com.osfans.trime.data-checksums")
9-
alias(libs.plugins.android.application)
108
alias(libs.plugins.aboutlibraries)
119
alias(libs.plugins.kotlin.android)
1210
alias(libs.plugins.kotlin.serialization)
@@ -17,7 +15,6 @@ android {
1715
namespace = "com.osfans.trime"
1816
compileSdk = 34
1917
buildToolsVersion = "34.0.0"
20-
ndkVersion = "25.2.9519653"
2118

2219
defaultConfig {
2320
applicationId = "com.osfans.trime"
@@ -35,26 +32,20 @@ android {
3532
buildConfigField("String", "BUILD_VERSION_NAME", "\"${project.buildVersionName}\"")
3633
}
3734

38-
signingConfigs {
39-
create("release") {
40-
val keyPropFile = rootProject.file("keystore.properties")
41-
if (keyPropFile.exists()) {
42-
val props = Properties()
43-
props.load(keyPropFile.inputStream())
44-
45-
storeFile = rootProject.file(props["storeFile"]!!)
46-
storePassword = props["storePassword"] as? String
47-
keyAlias = props["keyAlias"] as? String
48-
keyPassword = props["keyPassword"] as? String
49-
}
50-
}
51-
}
52-
5335
buildTypes {
5436
release {
5537
isMinifyEnabled = false
5638
//proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-android.txt"
57-
signingConfig = signingConfigs.getByName("release")
39+
signingConfig = with(ApkRelease) {
40+
if (project.buildApkRelease) {
41+
signingConfigs.create("release") {
42+
storeFile = file(project.storeFile!!)
43+
storePassword = project.storePassword
44+
keyAlias = project.keyAlias
45+
keyPassword = project.keyPassword
46+
}
47+
} else null
48+
}
5849

5950
resValue("string", "trime_app_name", "@string/app_name_release")
6051
}
@@ -65,17 +56,6 @@ android {
6556
}
6657
}
6758

68-
// Use prebuilt JNI library if the "app/prebuilt" exists
69-
//
70-
// Steps to generate the prebuilt directory:
71-
// $ ./gradlew app:assembleRelease
72-
// $ cp --recursive app/build/intermediates/stripped_native_libs/universalRelease/out/lib app/prebuilt
73-
if (file("prebuilt").exists()) {
74-
sourceSets.getByName("main").jniLibs.srcDirs(setOf("prebuilt"))
75-
} else {
76-
externalNativeBuild.cmake.path("src/main/jni/CMakeLists.txt")
77-
}
78-
7959
buildFeatures {
8060
buildConfig = true
8161
viewBinding = true
@@ -95,32 +75,6 @@ android {
9575
checkReleaseBuilds = false
9676
}
9777

98-
externalNativeBuild {
99-
cmake {
100-
version = "3.22.1"
101-
}
102-
}
103-
104-
splits {
105-
// Configures multiple APKs based on ABI.
106-
abi {
107-
// Enables building multiple APKs per ABI.
108-
isEnable = true
109-
110-
// By default all ABIs are included, so use reset() and include to specify that we only
111-
// want APKs for x86 and x86_64.
112-
113-
// Resets the list of ABIs that Gradle should create APKs for to none.
114-
reset()
115-
116-
// Specifies a list of ABIs that Gradle should create APKs for.
117-
include("x86", "x86_64", "armeabi-v7a", "arm64-v8a")
118-
119-
// Specifies that we do not want to also generate a universal APK that includes all ABIs.
120-
isUniversalApk = false
121-
}
122-
}
123-
12478
testOptions {
12579
unitTests.all {
12680
it.useJUnitPlatform()
@@ -153,10 +107,6 @@ android.applicationVariants.all {
153107
}
154108
}
155109

156-
tasks.register<Delete>("cleanCxxIntermediates") {
157-
delete(file(".cxx"))
158-
}.also { tasks.clean.dependsOn(it) }
159-
160110
dependencies {
161111
ksp(project(":codegen"))
162112
implementation(libs.kotlinx.coroutines)

build-logic/convention/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@ gradlePlugin {
1717
id = "com.osfans.trime.data-checksums"
1818
implementationClass = "DataChecksumsPlugin"
1919
}
20+
register("nativeAppConvention") {
21+
id = "com.osfans.trime.native-app-convention"
22+
implementationClass = "NativeAppConventionPlugin"
23+
}
2024
}
2125
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import org.gradle.api.Project
2+
import java.io.File
3+
import java.util.Properties
4+
5+
object ApkRelease {
6+
private const val KEYSTORE_PROPERTIES = "keystore.properties"
7+
8+
private val props by lazy {
9+
File(KEYSTORE_PROPERTIES).takeIf { it.exists() }
10+
?.let { Properties().apply { load(it.inputStream()) } }
11+
?: Properties()
12+
}
13+
14+
val Project.storeFile
15+
get() = props["storeFile"] as? String
16+
17+
val Project.storePassword
18+
get() = props["storePassword"] as? String
19+
20+
val Project.keyAlias
21+
get() = props["keyAlias"] as? String
22+
23+
val Project.keyPassword
24+
get() = props["keyPassword"] as? String
25+
26+
val Project.buildApkRelease
27+
get() = storeFile?.let { File(it).exists() } ?: false
28+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
2+
import org.gradle.api.Project
3+
import org.gradle.kotlin.dsl.configure
4+
5+
class NativeAppConventionPlugin : NativeBaseConventionPlugin() {
6+
override fun apply(target: Project) {
7+
super.apply(target)
8+
9+
target.extensions.configure<BaseAppModuleExtension> {
10+
packaging {
11+
jniLibs {
12+
useLegacyPackaging = true
13+
}
14+
}
15+
}
16+
}
17+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Versions.cmakeVersion
2+
import Versions.ndkVersion
3+
import com.android.build.api.dsl.CommonExtension
4+
import org.gradle.api.Plugin
5+
import org.gradle.api.Project
6+
import org.gradle.api.tasks.Delete
7+
import org.gradle.kotlin.dsl.configure
8+
import org.gradle.kotlin.dsl.task
9+
10+
open class NativeBaseConventionPlugin : Plugin<Project> {
11+
override fun apply(target: Project) {
12+
target.pluginManager.apply("com.android.application")
13+
target.extensions.configure<CommonExtension<*, *, *, *, *>>("android") {
14+
ndkVersion = target.ndkVersion
15+
// Use prebuilt JNI library if the "app/prebuilt" exists
16+
//
17+
// Steps to generate the prebuilt directory:
18+
// $ ./gradlew app:assembleRelease
19+
// $ cp --recursive app/build/intermediates/stripped_native_libs/universalRelease/out/lib app/prebuilt
20+
if (target.file("prebuilt").exists()) {
21+
sourceSets.getByName("main").jniLibs.srcDirs(setOf("prebuilt"))
22+
} else {
23+
externalNativeBuild {
24+
cmake {
25+
version = target.cmakeVersion
26+
path("src/main/jni/CMakeLists.txt")
27+
}
28+
}
29+
}
30+
31+
if (ApkRelease.run { target.buildApkRelease }) {
32+
// in this case, the version code of arm64-v8a will be used for the single production,
33+
// unless `buildABI` is specified
34+
defaultConfig {
35+
ndk {
36+
abiFilters.add("armeabi-v7a")
37+
abiFilters.add("arm64-v8a")
38+
abiFilters.add("x86")
39+
abiFilters.add("x86_64")
40+
}
41+
}
42+
} else {
43+
splits {
44+
abi {
45+
isEnable = true
46+
reset()
47+
include(target.buildABI)
48+
isUniversalApk = false
49+
}
50+
}
51+
}
52+
}
53+
registerCleanCxxTask(target)
54+
}
55+
56+
private fun registerCleanCxxTask(project: Project) {
57+
project.task<Delete>("cleanCxxIntermediates") {
58+
delete(project.file(".cxx"))
59+
}.also {
60+
project.cleanTask.dependsOn(it)
61+
}
62+
}
63+
}

build-logic/convention/src/main/kotlin/Utils.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ val Project.assetsDir: File
4343
val Project.cleanTask: Task
4444
get() = tasks.getByName("clean")
4545

46+
// Change default ABI here
47+
val Project.buildABI
48+
get() =
49+
envOrProp("BUILD_ABI", "buildABI") {
50+
// "armeabi-v7a"
51+
"arm64-v8a"
52+
// "x86"
53+
// "x86_64"
54+
}
55+
4656
val Project.builder
4757
get() =
4858
envOrProp("CI_NAME", "ciName") {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import org.gradle.api.Project
2+
3+
object Versions {
4+
private const val DEFAULT_CMAKE = "3.22.1"
5+
private const val DEFAULT_NDK = "25.2.9519653"
6+
7+
val Project.cmakeVersion
8+
get() = envOrProp("CMAKE_VERSION", "cmakeVersion") { DEFAULT_CMAKE }
9+
10+
val Project.ndkVersion
11+
get() = envOrProp("NDK_VERSION", "ndkVersion") { DEFAULT_NDK }
12+
}

0 commit comments

Comments
 (0)