386 lines
14 KiB
Kotlin
Raw Normal View History

2023-03-02 21:16:25 +08:00
import com.android.build.api.artifact.ArtifactTransformationRequest
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.dsl.ApkSigningConfig
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
2021-09-08 00:45:15 +08:00
import com.android.build.gradle.BaseExtension
2021-09-09 20:19:49 -07:00
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
2022-06-18 02:08:44 +08:00
import com.android.builder.internal.packaging.IncrementalPackager
import com.android.tools.build.apkzlib.sign.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zfile.ZFiles
import com.android.tools.build.apkzlib.zip.ZFileOptions
2021-09-09 20:19:49 -07:00
import org.apache.tools.ant.filters.FixCrLfFilter
2021-09-08 00:45:15 +08:00
import org.gradle.api.Action
2023-03-02 21:16:25 +08:00
import org.gradle.api.DefaultTask
2021-09-08 00:45:15 +08:00
import org.gradle.api.JavaVersion
import org.gradle.api.Project
2023-03-02 21:16:25 +08:00
import org.gradle.api.file.DirectoryProperty
2022-06-03 01:13:29 -07:00
import org.gradle.api.plugins.ExtensionAware
2023-03-02 21:16:25 +08:00
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
2022-10-31 22:31:15 +08:00
import org.gradle.kotlin.dsl.*
2023-02-20 01:03:35 -08:00
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
2022-05-27 00:44:20 -07:00
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
2022-06-18 02:08:44 +08:00
import java.security.KeyStore
import java.security.cert.X509Certificate
2021-09-09 20:19:49 -07:00
import java.util.*
2022-06-20 02:51:58 +08:00
import java.util.jar.JarFile
import java.util.zip.*
2021-12-14 21:30:15 +08:00
private fun Project.androidBase(configure: Action<BaseExtension>) =
extensions.configure("android", configure)
2021-09-09 20:19:49 -07:00
2021-12-14 21:30:15 +08:00
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
2021-09-09 20:19:49 -07:00
extensions.configure("android", configure)
2021-09-08 00:45:15 +08:00
2022-06-03 01:13:29 -07:00
private fun BaseExtension.kotlinOptions(configure: Action<KotlinJvmOptions>) =
(this as ExtensionAware).extensions.findByName("kotlinOptions")?.let {
2022-05-27 00:44:20 -07:00
configure.execute(it as KotlinJvmOptions)
}
2023-02-20 01:03:35 -08:00
private fun BaseExtension.kotlin(configure: Action<KotlinAndroidProjectExtension>) =
(this as ExtensionAware).extensions.findByName("kotlin")?.let {
configure.execute(it as KotlinAndroidProjectExtension)
}
2021-12-14 21:30:15 +08:00
private val Project.android: BaseAppModuleExtension
2022-10-31 22:31:15 +08:00
get() = extensions["android"] as BaseAppModuleExtension
2021-09-08 00:45:15 +08:00
2023-03-02 21:16:25 +08:00
private val Project.androidComponents
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
2021-09-08 00:45:15 +08:00
fun Project.setupCommon() {
2021-12-14 21:30:15 +08:00
androidBase {
compileSdkVersion(33)
2022-12-26 02:02:21 -08:00
buildToolsVersion = "33.0.1"
2022-01-27 01:46:00 -08:00
ndkPath = "$sdkDirectory/ndk/magisk"
2021-09-08 00:45:15 +08:00
defaultConfig {
minSdk = 21
targetSdk = 33
2021-09-08 00:45:15 +08:00
}
compileOptions {
2023-02-20 14:55:27 +08:00
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
2021-09-08 00:45:15 +08:00
}
2022-06-03 01:13:29 -07:00
kotlinOptions {
2023-02-20 14:55:27 +08:00
jvmTarget = "17"
2022-06-03 01:13:29 -07:00
}
2023-02-20 01:03:35 -08:00
kotlin {
jvmToolchain(17)
}
2022-05-27 00:44:20 -07:00
}
2021-09-08 00:45:15 +08:00
}
2023-03-02 21:16:25 +08:00
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
2022-06-18 02:08:44 +08:00
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
storeFile!!.inputStream().use {
keyStore.load(it, storePassword!!.toCharArray())
}
val keyPwdArray = keyPassword!!.toCharArray()
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
return entry as KeyStore.PrivateKeyEntry
}
2023-03-02 21:16:25 +08:00
private fun addComment(inFile: File, outFile: File, signConfig: ApkSigningConfig, minSdk: Int, eocdComment: String) {
2022-06-18 02:08:44 +08:00
val privateKey = signConfig.getPrivateKey()
val signingOptions = SigningOptions.builder()
.setMinSdkVersion(minSdk)
.setV1SigningEnabled(true)
.setV2SigningEnabled(true)
.setKey(privateKey.privateKey)
.setCertificates(privateKey.certificate as X509Certificate)
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
.build()
val options = ZFileOptions().apply {
noTimestamps = true
autoSortFiles = true
}
2023-03-02 21:16:25 +08:00
outFile.parentFile.mkdirs()
inFile.copyTo(outFile, overwrite = true)
ZFiles.apk(outFile, options).use {
2022-06-18 02:08:44 +08:00
SigningExtension(signingOptions).register(it)
it.eocdComment = eocdComment.toByteArray()
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
2022-06-20 02:51:58 +08:00
it.get(JarFile.MANIFEST_NAME)?.delete()
2022-06-18 02:08:44 +08:00
}
}
2023-03-02 21:16:25 +08:00
abstract class AddCommentTask: DefaultTask() {
@get:Input
abstract val comment: Property<String>
@get:Input
abstract val signingConfig: Property<ApkSigningConfig>
@get:InputFiles
abstract val apkFolder: DirectoryProperty
@get:OutputDirectory
abstract val outFolder: DirectoryProperty
@get:Internal
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
@TaskAction
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
val inFile = File(artifact.outputFile)
val outFile = outFolder.file(inFile.name).get().asFile
addComment(inFile, outFile, signingConfig.get(), 0, comment.get())
outFile
}
}
2021-09-09 20:19:49 -07:00
private fun Project.setupAppCommon() {
2021-09-08 00:45:15 +08:00
setupCommon()
2021-09-09 20:19:49 -07:00
2021-09-08 00:45:15 +08:00
android {
signingConfigs {
create("config") {
Config["keyStore"]?.also {
storeFile = rootProject.file(it)
storePassword = Config["keyStorePass"]
keyAlias = Config["keyAlias"]
keyPassword = Config["keyPass"]
}
}
}
buildTypes {
2022-10-31 22:31:15 +08:00
signingConfigs["config"].also {
debug {
2021-09-08 00:45:15 +08:00
signingConfig = if (it.storeFile?.exists() == true) it
2022-10-31 22:31:15 +08:00
else signingConfigs["debug"]
2021-09-08 00:45:15 +08:00
}
2022-10-31 22:31:15 +08:00
release {
2021-09-08 00:45:15 +08:00
signingConfig = if (it.storeFile?.exists() == true) it
2022-10-31 22:31:15 +08:00
else signingConfigs["debug"]
2021-09-08 00:45:15 +08:00
}
}
}
2021-12-14 21:30:15 +08:00
lint {
2021-09-08 00:45:15 +08:00
disable += "MissingTranslation"
2023-02-20 01:23:56 -08:00
checkReleaseBuilds = false
2021-09-08 00:45:15 +08:00
}
2021-12-14 21:30:15 +08:00
dependenciesInfo {
includeInApk = false
}
2023-02-20 01:03:35 -08:00
buildFeatures {
buildConfig = true
}
2021-09-08 00:45:15 +08:00
}
2022-06-18 02:08:44 +08:00
2023-03-02 21:16:25 +08:00
androidComponents.onVariants { variant ->
val commentTask = tasks.register(
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
AddCommentTask::class.java
)
val transformationRequest = variant.artifacts.use(commentTask)
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
.toTransformMany(SingleArtifact.APK)
val signingConfig = android.buildTypes.getByName(variant.buildType!!).signingConfig
commentTask.configure {
this.transformationRequest.set(transformationRequest)
this.signingConfig.set(signingConfig)
this.comment.set("version=${Config.version}\n" +
2023-02-12 17:40:28 +08:00
"versionCode=${Config.versionCode}\n" +
2023-03-02 21:16:25 +08:00
"stubVersion=${Config.stubVersion}\n")
this.outFolder.set(File(buildDir, "outputs/apk/${variant.name}"))
2022-06-18 02:08:44 +08:00
}
}
2021-09-08 00:45:15 +08:00
}
2021-09-09 20:19:49 -07:00
fun Project.setupApp() {
setupAppCommon()
2022-10-31 22:31:15 +08:00
val syncLibs by tasks.registering(Sync::class) {
2021-09-09 20:19:49 -07:00
into("src/main/jniLibs")
into("armeabi-v7a") {
from(rootProject.file("native/out/armeabi-v7a")) {
2022-03-17 03:15:39 -07:00
include("busybox", "magiskboot", "magiskinit", "magiskpolicy", "magisk")
2021-09-09 20:19:49 -07:00
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
}
}
into("x86") {
from(rootProject.file("native/out/x86")) {
2022-03-17 03:15:39 -07:00
include("busybox", "magiskboot", "magiskinit", "magiskpolicy", "magisk")
2021-09-09 20:19:49 -07:00
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
}
}
into("arm64-v8a") {
from(rootProject.file("native/out/arm64-v8a")) {
2022-03-17 03:15:39 -07:00
include("busybox", "magiskboot", "magiskinit", "magiskpolicy", "magisk")
2021-09-09 20:19:49 -07:00
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
}
}
into("x86_64") {
from(rootProject.file("native/out/x86_64")) {
2022-03-17 03:15:39 -07:00
include("busybox", "magiskboot", "magiskinit", "magiskpolicy", "magisk")
2021-09-09 20:19:49 -07:00
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
}
}
onlyIf {
2022-03-17 03:15:39 -07:00
if (inputs.sourceFiles.files.size != 20)
2021-09-09 20:19:49 -07:00
throw StopExecutionException("Please build binaries first! (./build.py binary)")
true
}
}
2022-10-31 22:31:15 +08:00
val syncResources by tasks.registering(Sync::class) {
2021-09-09 20:19:49 -07:00
into("src/main/resources/META-INF/com/google/android")
from(rootProject.file("scripts/update_binary.sh")) {
rename { "update-binary" }
}
from(rootProject.file("scripts/flash_script.sh")) {
rename { "updater-script" }
}
}
android.applicationVariants.all {
2023-02-20 14:55:27 +08:00
val variantCapped = name.replaceFirstChar { it.uppercase() }
2022-12-26 15:23:06 -08:00
2023-02-20 01:23:56 -08:00
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(syncLibs)
processJavaResourcesProvider.configure { dependsOn(syncResources) }
2022-12-26 15:23:06 -08:00
val stubTask = tasks.getByPath(":stub:package$variantCapped")
val stubApk = stubTask.outputs.files.asFileTree.filter {
it.name.endsWith(".apk")
}
2022-08-26 03:43:31 -07:00
2022-10-31 22:31:15 +08:00
val syncAssets = tasks.register("sync${variantCapped}Assets", Sync::class) {
2023-02-20 01:23:56 -08:00
dependsOn(stubTask)
2022-10-31 22:31:15 +08:00
inputs.property("version", Config.version)
inputs.property("versionCode", Config.versionCode)
2023-02-27 11:57:22 +08:00
into("src/${this@all.name}/assets")
2022-10-31 22:31:15 +08:00
from(rootProject.file("scripts")) {
include("util_functions.sh", "boot_patch.sh", "addon.d.sh")
include("uninstaller.sh", "module_installer.sh")
}
from(rootProject.file("tools/bootctl"))
into("chromeos") {
from(rootProject.file("tools/futility"))
from(rootProject.file("tools/keys")) {
include("kernel_data_key.vbprivk", "kernel.keyblock")
}
}
2022-12-26 15:23:06 -08:00
from(stubApk) {
2022-08-26 03:43:31 -07:00
rename { "stub.apk" }
}
2022-10-31 22:31:15 +08:00
filesMatching("**/util_functions.sh") {
filter {
it.replace(
"#MAGISK_VERSION_STUB",
"MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}"
)
}
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
2022-08-26 03:43:31 -07:00
}
}
2023-02-20 01:23:56 -08:00
mergeAssetsProvider.configure { dependsOn(syncAssets) }
2021-12-14 21:30:15 +08:00
2021-09-09 20:19:49 -07:00
val keysDir = rootProject.file("tools/keys")
val outSrcDir = File(buildDir, "generated/source/keydata/$name")
val outSrc = File(outSrcDir, "com/topjohnwu/magisk/signing/KeyData.java")
2022-08-26 03:43:31 -07:00
val genSrcTask = tasks.register("generate${variantCapped}KeyData") {
2021-09-09 20:19:49 -07:00
inputs.dir(keysDir)
outputs.file(outSrc)
doLast {
genKeyData(keysDir, outSrc)
}
}
registerJavaGeneratingTask(genSrcTask, outSrcDir)
}
}
fun Project.setupStub() {
setupAppCommon()
2023-03-02 21:16:25 +08:00
androidComponents.onVariants { variant ->
val manifestUpdater =
project.tasks.register(
"${variant.name}ManifestProducer",
ManifestUpdater::class.java
) {
dependsOn("generate${variant.name.replaceFirstChar { it.uppercase() }}ObfuscatedClass")
appClassDir.set(File(buildDir, "generated/source/app/${variant.name}"))
factoryClassDir.set(File(buildDir, "generated/source/factory/${variant.name}"))
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
ManifestUpdater::mergedManifest,
ManifestUpdater::outputManifest)
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
2021-09-09 20:19:49 -07:00
android.applicationVariants.all {
2023-02-20 14:55:27 +08:00
val variantCapped = name.replaceFirstChar { it.uppercase() }
val variantLowered = name.lowercase()
2023-03-02 21:16:25 +08:00
val outFactoryClassDir = File(buildDir, "generated/source/factory/${variantLowered}")
val outAppClassDir = File(buildDir, "generated/source/app/${variantLowered}")
2023-02-20 01:23:56 -08:00
val outResDir = File(buildDir, "generated/source/res/${variantLowered}")
2021-12-14 21:30:15 +08:00
val aapt = File(android.sdkDirectory, "build-tools/${android.buildToolsVersion}/aapt2")
val apk = File(buildDir, "intermediates/processed_res/" +
"${variantLowered}/out/resources-${variantLowered}.ap_")
val apkTmp = File("${apk}.tmp")
2021-09-09 20:19:49 -07:00
2023-03-02 21:16:25 +08:00
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
2023-02-20 01:23:56 -08:00
inputs.property("seed", RAND_SEED)
2023-03-02 21:16:25 +08:00
outputs.dirs(outFactoryClassDir, outAppClassDir)
2021-09-09 20:19:49 -07:00
doLast {
2023-03-02 21:16:25 +08:00
outFactoryClassDir.mkdirs()
outAppClassDir.mkdirs()
genStubClass(outFactoryClassDir, outAppClassDir)
2021-12-14 21:30:15 +08:00
}
}
2023-03-02 21:16:25 +08:00
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
2021-09-09 20:19:49 -07:00
2023-02-20 01:23:56 -08:00
val processResourcesTask = tasks.getByPath(":stub:process${variantCapped}Resources")
processResourcesTask.doLast {
exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}
2021-09-09 20:19:49 -07:00
2023-02-20 01:23:56 -08:00
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->
ZipOutputStream(apk.outputStream()).use {
it.setLevel(Deflater.BEST_COMPRESSION)
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
it.closeEntry()
}
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
2021-09-09 20:19:49 -07:00
}
}
2023-02-20 01:23:56 -08:00
apkTmp.delete()
genEncryptedResources(ByteArrayInputStream(bos.toByteArray()), outResDir)
2021-09-09 20:19:49 -07:00
}
2023-02-20 01:23:56 -08:00
@Suppress("DEPRECATION")
registerJavaGeneratingTask(processResourcesTask, outResDir)
2021-09-09 20:19:49 -07:00
}
2021-12-14 21:30:15 +08:00
// Override optimizeReleaseResources task
2023-02-20 01:23:56 -08:00
val apk = File(buildDir, "intermediates/processed_res/" +
"release/out/resources-release.ap_")
val optRes = File(buildDir, "intermediates/optimized_processed_res/" +
"release/resources-release-optimize.ap_")
2021-12-14 21:30:15 +08:00
tasks.whenTaskAdded {
if (name == "optimizeReleaseResources") {
doLast { apk.copyTo(optRes, true) }
}
}
2022-01-09 02:22:34 +08:00
tasks.named<Delete>("clean") {
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
}
2021-09-09 20:19:49 -07:00
}