-
Notifications
You must be signed in to change notification settings - Fork 1.3k
进一步兼容 AGP 8.9.0 #1417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
进一步兼容 AGP 8.9.0 #1417
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,13 +26,15 @@ import com.android.build.gradle.BaseExtension | |
| import com.android.build.gradle.api.ApplicationVariant | ||
| import com.android.sdklib.AndroidVersion.VersionCodes | ||
| import com.tencent.shadow.core.gradle.extensions.PackagePluginExtension | ||
| import com.tencent.shadow.core.manifest_parser.createResourceMapper | ||
| import com.tencent.shadow.core.manifest_parser.generatePluginManifest | ||
| import com.tencent.shadow.core.transform.DeprecatedTransformWrapper | ||
| import com.tencent.shadow.core.transform.GradleTransformWrapper | ||
| import com.tencent.shadow.core.transform.ShadowTransform | ||
| import com.tencent.shadow.core.transform_kit.AndroidClassPoolBuilder | ||
| import com.tencent.shadow.core.transform_kit.ClassPoolBuilder | ||
| import org.gradle.api.* | ||
| import org.gradle.api.tasks.TaskProvider | ||
| import org.gradle.api.tasks.compile.JavaCompile | ||
| import java.io.File | ||
| import java.net.URLClassLoader | ||
|
|
@@ -103,8 +105,9 @@ class ShadowPlugin : Plugin<Project> { | |
| initAndroidClassPoolBuilder(baseExtension, project) | ||
|
|
||
| createPackagePluginTasks(project) | ||
|
|
||
| addLocateApkanalyzerTask(project) | ||
| if (agpCompat.hasDeprecatedTransformApi()) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
你这里复用的判断条件似乎没有对shrinkResources是否开启作判断。从你的描述来看,我们应该只对同时符合AGP版本和shrinkResources开启的场景使用这个新的功能。 |
||
| addLocateApkanalyzerTask(project) | ||
| } | ||
|
|
||
| onEachPluginVariant(project) { pluginVariant -> | ||
| checkAaptPackageIdConfig(pluginVariant) | ||
|
|
@@ -214,6 +217,77 @@ class ShadowPlugin : Plugin<Project> { | |
| appExtension: AppExtension, | ||
| pluginVariant: ApplicationVariant | ||
| ) { | ||
| val variantName = pluginVariant.name | ||
| val capitalizeVariantName = variantName.capitalize() | ||
|
|
||
| // 添加生成PluginManifest.java任务 | ||
| val pluginManifestSourceDir = | ||
| File(project.buildDir, "generated/source/pluginManifest/$variantName") | ||
|
|
||
| val javacTask = project.tasks.getByName("compile${capitalizeVariantName}JavaWithJavac") | ||
|
|
||
| // AGP 4.0.0 不支持下面的写法,转成 ()-> TaskProvider<Task?>? 再包一层的实现。 | ||
| // val func = if (agpCompat.hasDeprecatedTransformApi()) this::createGeneratePluginManifestTaskOld else this::createGeneratePluginManifestTaskNew | ||
| // val generatePluginManifestTask = func.invoke(project, pluginVariant, pluginManifestSourceDir) | ||
| val func: () -> TaskProvider<Task?>? = if (agpCompat.hasDeprecatedTransformApi()) { | ||
| { | ||
| createGeneratePluginManifestTaskOld(project, pluginVariant, pluginManifestSourceDir) | ||
| } | ||
| } else { | ||
| { | ||
| createGeneratePluginManifestTaskNew(project, pluginVariant, pluginManifestSourceDir) | ||
| } | ||
| } | ||
| val generatePluginManifestTask = func.invoke() | ||
| javacTask.dependsOn(generatePluginManifestTask) | ||
|
|
||
| // 把PluginManifest.java添加为源码 | ||
| val relativePath = | ||
| project.projectDir.toPath().relativize(pluginManifestSourceDir.toPath()).toString() | ||
| (javacTask as JavaCompile).source(project.fileTree(relativePath)) | ||
| } | ||
|
|
||
| private fun createGeneratePluginManifestTaskNew( | ||
| project: Project, | ||
| pluginVariant: ApplicationVariant, | ||
| pluginManifestSourceDir: File, | ||
| ): TaskProvider<Task?>? { | ||
| val output = pluginVariant.outputs.first() | ||
|
|
||
| val variantName = pluginVariant.name | ||
| val capitalizeVariantName = variantName.capitalize() | ||
|
|
||
| return project.tasks.register("generate${capitalizeVariantName}PluginManifest") { | ||
| // 依赖 processManifest 任务以获取最终的 AndroidManifest.xml | ||
| val processManifestTask = agpCompat.getProcessManifestTask(output) | ||
| // 依赖 processResources 任务以获取 R.txt | ||
| val processResourcesTask = agpCompat.getProcessResourcesTask(output) | ||
| it.dependsOn(processManifestTask) | ||
| it.dependsOn(processResourcesTask) | ||
|
|
||
| it.outputs.dir(pluginManifestSourceDir).withPropertyName("pluginManifestSourceDir") | ||
|
|
||
| it.doLast { | ||
| // 解析合并后的 AndroidManifest.xml + R.txt | ||
| // 这种方案直接解析 XML 格式的 AndroidManifest.xml ,不再依赖 aapt2 产生的二进制产物 | ||
| val mergedManifest = agpCompat.getProcessManifestFile(project, pluginVariant, output) | ||
| val rTxt = agpCompat.getRTxtFile(project, processResourcesTask, variantName) | ||
| val resourceMapper = createResourceMapper(rTxt) | ||
| generatePluginManifest( | ||
| mergedManifest, | ||
| pluginManifestSourceDir, | ||
| "com.tencent.shadow.core.manifest_parser", | ||
| resourceMapper | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private fun createGeneratePluginManifestTaskOld( | ||
| project: Project, | ||
| pluginVariant: ApplicationVariant, | ||
| pluginManifestSourceDir: File, | ||
| ): TaskProvider<Task?>? { | ||
| val output = pluginVariant.outputs.first() | ||
|
|
||
| val variantName = pluginVariant.name | ||
|
|
@@ -249,12 +323,7 @@ class ShadowPlugin : Plugin<Project> { | |
| } | ||
| } | ||
|
|
||
|
|
||
| // 添加生成PluginManifest.java任务 | ||
| val pluginManifestSourceDir = | ||
| File(project.buildDir, "generated/source/pluginManifest/$variantName") | ||
| val generatePluginManifestTask = | ||
| project.tasks.register("generate${capitalizeVariantName}PluginManifest") { | ||
| return project.tasks.register("generate${capitalizeVariantName}PluginManifest") { | ||
| it.dependsOn(decodeBinaryManifestTask) | ||
| it.inputs.file(decodeXml) | ||
| it.outputs.dir(pluginManifestSourceDir).withPropertyName("pluginManifestSourceDir") | ||
|
|
@@ -267,13 +336,6 @@ class ShadowPlugin : Plugin<Project> { | |
| ) | ||
| } | ||
| } | ||
| val javacTask = project.tasks.getByName("compile${capitalizeVariantName}JavaWithJavac") | ||
| javacTask.dependsOn(generatePluginManifestTask) | ||
|
|
||
| // 把PluginManifest.java添加为源码 | ||
| val relativePath = | ||
| project.projectDir.toPath().relativize(pluginManifestSourceDir.toPath()).toString() | ||
| (javacTask as JavaCompile).source(project.fileTree(relativePath)) | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package com.tencent.shadow.core.manifest_parser | ||
|
|
||
| import java.io.File | ||
| import java.util.Collections | ||
|
|
||
| /** | ||
| * manifest-parser的入口方法 | ||
|
|
@@ -9,13 +10,73 @@ import java.io.File | |
| * 一般位于apk工程的build/intermediates/merged_manifest目录中。 | ||
| * @param outputDir 生成文件的输出目录 | ||
| * @param packageName 生成类的包名 | ||
| * @param resourceMapper 资源映射器 | ||
| */ | ||
| fun generatePluginManifest( | ||
| xmlFile: File, | ||
| outputDir: File, | ||
| packageName: String | ||
| packageName: String, | ||
| resourceMapper: ((String) -> String)? = null | ||
| ) { | ||
| val androidManifest = AndroidManifestReader().read(xmlFile) | ||
| val generator = PluginManifestGenerator() | ||
| generator.generate(androidManifest, outputDir, packageName) | ||
| } | ||
| generator.generate(androidManifest, outputDir, packageName, resourceMapper) | ||
| } | ||
|
|
||
| /** | ||
| * 创建资源映射器。 | ||
| * | ||
| * @param rTxt R.txt文件 | ||
| * @return 资源映射器 | ||
| */ | ||
| fun createResourceMapper(rTxt: File): (String) -> String { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个mapper后面用了多处,应该定义一个typealias |
||
| val rTxtMap = parseRTxt(rTxt) | ||
|
|
||
| return { resName -> | ||
| if (resName.startsWith("@android:")) { | ||
| // @android:style/Theme.NoTitleBar -> android.R.style.Theme_NoTitleBar | ||
| val parts = resName.substringAfter("@android:").split("/") | ||
| val type = parts[0] | ||
| val name = parts[1].replace(".", "_") | ||
| "android.R.$type.$name" | ||
| } else { | ||
| // @[package:]type/name -> id 值 | ||
| var raw = resName.substringAfter("@") | ||
| if (raw.contains(":")) { | ||
| raw = raw.substringAfter(":") | ||
| } | ||
| val parts = raw.split("/") | ||
| val type = parts[0] | ||
| val name = parts[1].replace('.', '_') | ||
| val key = "@$type/$name" | ||
| rTxtMap[key] ?: throw IllegalArgumentException("Resource not found in R.txt: $resName (normalized: $key)") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 解析 R.txt 文件并生成资源 ID 映射表。 R.txt 包含项目引用的所有资源 ID。 | ||
| * | ||
| * @param rTxtFile R.txt 文件对象 | ||
| * @return 资源全称(如 @string/app_name)到 ID 的映射 | ||
| */ | ||
| fun parseRTxt(rTxtFile: File): Map<String, String> { | ||
| if (!rTxtFile.exists()) return Collections.emptyMap() | ||
|
|
||
| val map = mutableMapOf<String, String>() | ||
| rTxtFile.useLines { | ||
| it.forEach { line -> | ||
| if (!(line.startsWith("int "))) { | ||
| return@forEach | ||
| } | ||
| val parts = line.split(Regex("\\s+")).filter { it.isNotBlank() } | ||
| if (parts.size == 4 && parts[0] == "int") { | ||
| val type = parts[1] | ||
| val name = parts[2] | ||
| val idStr = parts[3] | ||
| map["@$type/$name"] = idStr | ||
| } | ||
| } | ||
| } | ||
| return map | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个分支条件直接复用了transformAPI版本的判断,应该是巧合吧?这样以后可能就看不懂了。还是专门定一个判断接口吧,即便实现是一样的也没关系。