/******************************************************************************
* *
* Copyright (C) 2021 by nekohasekai
*
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
******************************************************************************/
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.FilterConfiguration
import com.android.build.api.variant.impl.VariantOutputImpl
import org.apache.tools.ant.filters.StringInputStream
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
import java.util.Properties
import kotlin.io.encoding.Base64
private val Project.android: CommonExtension
get() = extensions.getByName("android") as CommonExtension
private val Project.androidApp: ApplicationExtension
get() = extensions.getByType()
private val Project.androidComponents: ApplicationAndroidComponentsExtension
get() = extensions.getByType()
private lateinit var metadata: Properties
private lateinit var localProperties: Properties
fun Project.requireMetadata(): Properties {
if (!::metadata.isInitialized) {
metadata = Properties().apply {
load(rootProject.file("version.properties").inputStream())
}
}
return metadata
}
fun Project.requireLocalProperties(): Properties {
if (!::localProperties.isInitialized) {
localProperties = Properties()
val base64 = System.getenv("LOCAL_PROPERTIES")
if (!base64.isNullOrBlank()) {
localProperties.load(StringInputStream(String(Base64.withPadding(Base64.PaddingOption.PRESENT_OPTIONAL).decode(base64))))
} else if (project.rootProject.file("local.properties").exists()) {
localProperties.load(rootProject.file("local.properties").inputStream())
}
}
return localProperties
}
fun Project.setupCommon(projectName: String = "") {
android.apply {
buildToolsVersion = "37.0.0"
compileSdk = 37
defaultConfig.minSdk = if (projectName.lowercase() == "naive") 24 else 23
compileOptions.sourceCompatibility = JavaVersion.VERSION_21
compileOptions.targetCompatibility = JavaVersion.VERSION_21
lint.showAll = true
lint.checkAllWarnings = true
lint.checkReleaseBuilds = false
lint.warningsAsErrors = true
packaging.jniLibs.useLegacyPackaging = true
// Do not strip symbols by AGP to improve reproducibility. Symbols are manually stripped in advanced.
packaging.jniLibs.keepDebugSymbols.add("**/*.so")
packaging.resources.excludes.addAll(
listOf(
"**/*.kotlin_*",
"/META-INF/*.version",
"/META-INF/androidx/**",
"/META-INF/native/**",
"/META-INF/native-image/**",
"/META-INF/INDEX.LIST",
"DebugProbesKt.bin",
"com/**",
"org/**",
"**/*.java",
"**/*.proto",
)
)
}
}
fun Project.setupAppCommon(projectName: String = "") {
setupCommon(projectName)
val lp = requireLocalProperties()
val keystorePwd = lp.getProperty("KEYSTORE_PASS") ?: System.getenv("KEYSTORE_PASS")
val alias = lp.getProperty("ALIAS_NAME") ?: System.getenv("ALIAS_NAME")
val pwd = lp.getProperty("ALIAS_PASS") ?: System.getenv("ALIAS_PASS")
androidApp.apply {
if (keystorePwd != null) {
signingConfigs.create("release") {
storeFile = rootProject.file("release.keystore")
storePassword = keystorePwd
keyAlias = alias
keyPassword = pwd
enableV3Signing = true
}
}
defaultConfig.targetSdk = 37
buildTypes.getByName("release") {
@Suppress("UnstableApiUsage")
vcsInfo.include = false
signingConfigs.findByName("release")?.let {
signingConfig = it
}
ndk.debugSymbolLevel = "NONE"
}
buildTypes.getByName("debug") {
applicationIdSuffix = "debug"
isDebuggable = true
isJniDebuggable = true
}
dependenciesInfo.includeInApk = false
dependenciesInfo.includeInBundle = false
@Suppress("UnstableApiUsage")
bundle.language.enableSplit = false
@Suppress("UnstableApiUsage")
bundle.abi.enableSplit = false
if (gradle.startParameter.taskNames.isNotEmpty() && gradle.startParameter.taskNames.any { it.lowercase().contains("assemble") }) {
splits.abi.apply {
isEnable = true
isUniversalApk = false
reset()
include("x86", "x86_64", "armeabi-v7a", "arm64-v8a")
}
}
}
val cleanTask = tasks.register("cleanAboutLibrariesGenerated") {
delete(layout.buildDirectory.dir("generated/aboutLibraries"))
}
tasks.configureEach {
if (name.contains("preBuild")) {
dependsOn(cleanTask)
}
}
dependencies.add("implementation", project(":plugin:api"))
}
fun Project.setupPlugin(projectName: String) {
setupAppCommon(projectName)
val propPrefix = projectName.uppercase()
val verName = requireMetadata().getProperty("${propPrefix}_VERSION_NAME").trim()
val verCode = requireMetadata().getProperty("${propPrefix}_VERSION").trim().toInt()
androidApp.apply {
defaultConfig.versionName = verName
defaultConfig.versionCode = verCode
flavorDimensions.add("vendor")
productFlavors.create("oss")
}
androidComponents.apply {
onVariants { variant ->
variant.outputs.forEach { output ->
(output as? VariantOutputImpl)?.let { variantOutputImpl ->
val versionName = variantOutputImpl.versionName.orNull.orEmpty()
variantOutputImpl.outputFileName.set(variantOutputImpl.outputFileName.get()
.replace(project.name, "${projectName}-plugin-$versionName")
.replace("-release", "")
.replace("-oss", "")
)
}
}
}
}
}
fun Project.setupApp() {
val pkgName = requireMetadata().getProperty("PACKAGE_NAME").trim()
val verName = requireMetadata().getProperty("VERSION_NAME").trim()
val verCode = requireMetadata().getProperty("VERSION_CODE").trim().toInt() * 5
setupAppCommon()
androidApp.apply {
defaultConfig.applicationId = pkgName
defaultConfig.versionCode = verCode
defaultConfig.versionName = verName
buildTypes.getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
file("proguard-rules.pro")
)
}
buildFeatures.aidl = true
buildFeatures.buildConfig = true
buildFeatures.viewBinding = true
buildFeatures.compose = true
compileOptions.isCoreLibraryDesugaringEnabled = true
flavorDimensions.add("vendor")
productFlavors.create("oss")
tasks.register("downloadAssets") {
downloadAssets(update = false)
}
tasks.register("updateAssets") {
downloadRootCAList()
downloadAssets(update = true)
}
}
androidComponents.apply {
onVariants { variant ->
variant.outputs.forEach { output ->
when (output.filters.find { it.filterType == FilterConfiguration.FilterType.ABI }?.identifier) {
"arm64-v8a" -> output.versionCode.set(verCode + 4)
"x86_64" -> output.versionCode.set(verCode + 3)
"armeabi-v7a" -> output.versionCode.set(verCode + 2)
"x86" -> output.versionCode.set(verCode + 1)
}
(output as? VariantOutputImpl)?.let { variantOutputImpl ->
val versionName = variantOutputImpl.versionName.orNull.orEmpty()
variantOutputImpl.outputFileName.set(variantOutputImpl.outputFileName.get()
.replace(project.name, "Owenclave-$versionName")
.replace("-release", "")
.replace("-oss", "")
)
}
}
}
}
}