Skip to content

Commit

Permalink
build: implement native convention plugins
Browse files Browse the repository at this point in the history
- Allow to use environment variables / Gradle properties to determine CMake and NDK version and build ABI
- Use legacy packing for JNI libs
  • Loading branch information
WhiredPlanck committed Jan 16, 2024
1 parent 50e3f2b commit 47b6910
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 61 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/commit-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ jobs:
- ubuntu-22.04
- macos-13
- windows-2022
abi:
- armeabi-v7a
- arm64-v8a
- x86
- x86_64
env:
BUILD_ABI: ${{ matrix.abi }}
steps:
- name: Fetch source code
uses: actions/checkout@v4
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/pull-request-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ jobs:
- ubuntu-22.04
- macos-13
- windows-2022
abi:
- armeabi-v7a
- arm64-v8a
- x86
- x86_64
env:
BUILD_ABI: ${{ matrix.abi }}
steps:
- name: Fetch source code
uses: actions/checkout@v4
Expand Down
72 changes: 11 additions & 61 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
@file:Suppress("UnstableApiUsage")

import com.android.build.gradle.internal.tasks.factory.dependsOn
import java.util.Properties
import org.gradle.configurationcache.extensions.capitalized

plugins {
id("com.osfans.trime.native-app-convention")
id("com.osfans.trime.data-checksums")
alias(libs.plugins.android.application)
alias(libs.plugins.aboutlibraries)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
Expand All @@ -17,7 +15,6 @@ android {
namespace = "com.osfans.trime"
compileSdk = 34
buildToolsVersion = "34.0.0"
ndkVersion = "25.2.9519653"

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

signingConfigs {
create("release") {
val keyPropFile = rootProject.file("keystore.properties")
if (keyPropFile.exists()) {
val props = Properties()
props.load(keyPropFile.inputStream())

storeFile = rootProject.file(props["storeFile"]!!)
storePassword = props["storePassword"] as? String
keyAlias = props["keyAlias"] as? String
keyPassword = props["keyPassword"] as? String
}
}
}

buildTypes {
release {
isMinifyEnabled = false
//proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-android.txt"
signingConfig = signingConfigs.getByName("release")
signingConfig = with(ApkRelease) {
if (project.buildApkRelease) {
signingConfigs.create("release") {
storeFile = file(project.storeFile!!)
storePassword = project.storePassword
keyAlias = project.keyAlias
keyPassword = project.keyPassword
}
} else null
}

resValue("string", "trime_app_name", "@string/app_name_release")
}
Expand All @@ -65,17 +56,6 @@ android {
}
}

// Use prebuilt JNI library if the "app/prebuilt" exists
//
// Steps to generate the prebuilt directory:
// $ ./gradlew app:assembleRelease
// $ cp --recursive app/build/intermediates/stripped_native_libs/universalRelease/out/lib app/prebuilt
if (file("prebuilt").exists()) {
sourceSets.getByName("main").jniLibs.srcDirs(setOf("prebuilt"))
} else {
externalNativeBuild.cmake.path("src/main/jni/CMakeLists.txt")
}

buildFeatures {
buildConfig = true
viewBinding = true
Expand All @@ -95,32 +75,6 @@ android {
checkReleaseBuilds = false
}

externalNativeBuild {
cmake {
version = "3.22.1"
}
}

splits {
// Configures multiple APKs based on ABI.
abi {
// Enables building multiple APKs per ABI.
isEnable = true

// By default all ABIs are included, so use reset() and include to specify that we only
// want APKs for x86 and x86_64.

// Resets the list of ABIs that Gradle should create APKs for to none.
reset()

// Specifies a list of ABIs that Gradle should create APKs for.
include("x86", "x86_64", "armeabi-v7a", "arm64-v8a")

// Specifies that we do not want to also generate a universal APK that includes all ABIs.
isUniversalApk = false
}
}

testOptions {
unitTests.all {
it.useJUnitPlatform()
Expand Down Expand Up @@ -153,10 +107,6 @@ android.applicationVariants.all {
}
}

tasks.register<Delete>("cleanCxxIntermediates") {
delete(file(".cxx"))
}.also { tasks.clean.dependsOn(it) }

dependencies {
ksp(project(":codegen"))
implementation(libs.kotlinx.coroutines)
Expand Down
4 changes: 4 additions & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@ gradlePlugin {
id = "com.osfans.trime.data-checksums"
implementationClass = "DataChecksumsPlugin"
}
register("nativeAppConvention") {
id = "com.osfans.trime.native-app-convention"
implementationClass = "NativeAppConventionPlugin"
}
}
}
28 changes: 28 additions & 0 deletions build-logic/convention/src/main/kotlin/ApkRelease.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import org.gradle.api.Project
import java.io.File
import java.util.Properties

object ApkRelease {
private const val KEYSTORE_PROPERTIES = "keystore.properties"

private val props by lazy {
File(KEYSTORE_PROPERTIES).takeIf { it.exists() }
?.let { Properties().apply { load(it.inputStream()) } }
?: Properties()
}

val Project.storeFile
get() = props["storeFile"] as? String

val Project.storePassword
get() = props["storePassword"] as? String

val Project.keyAlias
get() = props["keyAlias"] as? String

val Project.keyPassword
get() = props["keyPassword"] as? String

val Project.buildApkRelease
get() = storeFile?.let { File(it).exists() } ?: false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure

class NativeAppConventionPlugin : NativeBaseConventionPlugin() {
override fun apply(target: Project) {
super.apply(target)

target.extensions.configure<BaseAppModuleExtension> {
packaging {
jniLibs {
useLegacyPackaging = true
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import Versions.cmakeVersion
import Versions.ndkVersion
import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Delete
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.task

open class NativeBaseConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.pluginManager.apply("com.android.application")
target.extensions.configure<CommonExtension<*, *, *, *, *>>("android") {
ndkVersion = target.ndkVersion
// Use prebuilt JNI library if the "app/prebuilt" exists
//
// Steps to generate the prebuilt directory:
// $ ./gradlew app:assembleRelease
// $ cp --recursive app/build/intermediates/stripped_native_libs/universalRelease/out/lib app/prebuilt
if (target.file("prebuilt").exists()) {
sourceSets.getByName("main").jniLibs.srcDirs(setOf("prebuilt"))
} else {
externalNativeBuild {
cmake {
version = target.cmakeVersion
path("src/main/jni/CMakeLists.txt")
}
}
}

if (ApkRelease.run { target.buildApkRelease }) {
// in this case, the version code of arm64-v8a will be used for the single production,
// unless `buildABI` is specified
defaultConfig {
ndk {
abiFilters.add("armeabi-v7a")
abiFilters.add("arm64-v8a")
abiFilters.add("x86")
abiFilters.add("x86_64")
}
}
} else {
splits {
abi {
isEnable = true
reset()
include(target.buildABI)
isUniversalApk = false
}
}
}
}
registerCleanCxxTask(target)
}

private fun registerCleanCxxTask(project: Project) {
project.task<Delete>("cleanCxxIntermediates") {
delete(project.file(".cxx"))
}.also {
project.cleanTask.dependsOn(it)
}
}
}
10 changes: 10 additions & 0 deletions build-logic/convention/src/main/kotlin/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ val Project.assetsDir: File
val Project.cleanTask: Task
get() = tasks.getByName("clean")

// Change default ABI here
val Project.buildABI
get() =
envOrProp("BUILD_ABI", "buildABI") {
// "armeabi-v7a"
"arm64-v8a"
// "x86"
// "x86_64"
}

val Project.builder
get() =
envOrProp("CI_NAME", "ciName") {
Expand Down
12 changes: 12 additions & 0 deletions build-logic/convention/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import org.gradle.api.Project

object Versions {
private const val DEFAULT_CMAKE = "3.22.1"
private const val DEFAULT_NDK = "25.2.9519653"

val Project.cmakeVersion
get() = envOrProp("CMAKE_VERSION", "cmakeVersion") { DEFAULT_CMAKE }

val Project.ndkVersion
get() = envOrProp("NDK_VERSION", "ndkVersion") { DEFAULT_NDK }
}

0 comments on commit 47b6910

Please sign in to comment.