From 8ac6b1f3dbde92ea04cc73a9742b16a2221d07db Mon Sep 17 00:00:00 2001 From: shifujun Date: Fri, 1 Dec 2023 16:38:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=94=AF=E6=8C=81AGP=208.0+?= =?UTF-8?q?=E6=89=80=E9=9C=80=E7=9A=84=E6=96=B0=E7=89=88Transform=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GradleTransformWrapper对接新版API实现的是最基本的全量输入和输出功能。 没有实现增量编辑的能力,也没有对齐旧版API中在getSecondaryFiles中将自身代码加入, 以便开发中更新transform代码可触发重新执行transform。 因此使用GradleTransformWrapper开发transform时可能需要手动clean。 ShadowPlugin加入了hasDeprecatedTransformApi检测, 只在判断出AGP主版本号大于等于8时才会应用GradleTransformWrapper。 增加了projects/test/gradle-plugin-agp-compat-test中对AGP 8.0+已知版本的自动化测试。 resolve #1212 --- .../tencent/shadow/core/gradle/AGPCompat.kt | 1 + .../shadow/core/gradle/AGPCompatImpl.kt | 14 ++++ .../shadow/core/gradle/ShadowPlugin.kt | 44 ++++++++-- .../core/transform/GradleTransformWrapper.kt | 80 +++++++++++++++++++ .../stub-project/build.gradle | 6 ++ .../test_JDK17.sh | 7 ++ 6 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/GradleTransformWrapper.kt diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt index 1e822dbc6..0088123a0 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt @@ -33,4 +33,5 @@ internal interface AGPCompat { fun getProcessResourcesTask(output: BaseVariantOutput): Task fun getAaptAdditionalParameters(processResourcesTask: Task): List fun getMinSdkVersion(pluginVariant: ApplicationVariant): Int + fun hasDeprecatedTransformApi(): Boolean } \ No newline at end of file diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt index 39fe43d4f..e9c6f0621 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt @@ -71,6 +71,20 @@ internal class AGPCompatImpl : AGPCompat { return mergedFlavor.minSdkVersion?.apiLevel ?: VersionCodes.BASE } + override fun hasDeprecatedTransformApi(): Boolean { + try { + val version = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION + val majorVersion = version.substringBefore('.', "0").toInt() + if (majorVersion >= 8) { + return false//能parse出来主版本号大于等于8,我们就认为旧版Transform API不可用了。 + } + } catch (ignored: Error) { + } + + //读取版本号失败,就推测是旧版本的AGP,就应该有旧版本的Transform API + return true + } + companion object { fun getStringFromProperty(x: Any?): String { return when (x) { diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt index 98c1e04cc..93df1a3b6 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt @@ -18,6 +18,9 @@ package com.tencent.shadow.core.gradle +import com.android.build.api.artifact.ScopedArtifact +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.ScopedArtifacts import com.android.build.gradle.AppExtension import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.ApplicationVariant @@ -25,6 +28,7 @@ import com.android.sdklib.AndroidVersion.VersionCodes import com.tencent.shadow.core.gradle.extensions.PackagePluginExtension 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 @@ -52,15 +56,43 @@ class ShadowPlugin : Plugin { val shadowExtension = project.extensions.create("shadow", ShadowExtension::class.java) if (!project.hasProperty("disable_shadow_transform")) { - baseExtension.registerTransform( - DeprecatedTransformWrapper(project, - ShadowTransform( + val shadowTransform = ShadowTransform( + project, + lateInitBuilder, + { shadowExtension.transformConfig.useHostContext } + ) + if (agpCompat.hasDeprecatedTransformApi()) { + baseExtension.registerTransform( + DeprecatedTransformWrapper( project, - lateInitBuilder, - { shadowExtension.transformConfig.useHostContext } + shadowTransform ) ) - ) + } else { + val androidComponentsExtension = + project.extensions.getByName("androidComponents") as ApplicationAndroidComponentsExtension + androidComponentsExtension.onVariants( + selector = androidComponentsExtension.selector() + .withFlavor( + ShadowTransform.DimensionName + to ShadowTransform.ApplyShadowTransformFlavorName + ) + ) { variant -> + val taskProvider = project.tasks.register( + "${variant.name}ShadowTransform", + GradleTransformWrapper::class.java, + shadowTransform + ) + variant.artifacts.forScope(ScopedArtifacts.Scope.ALL) + .use(taskProvider) + .toTransform( + ScopedArtifact.CLASSES, + GradleTransformWrapper::allJars, + GradleTransformWrapper::allDirectories, + GradleTransformWrapper::output + ) + } + } } addFlavorForTransform(baseExtension) diff --git a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/GradleTransformWrapper.kt b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/GradleTransformWrapper.kt new file mode 100644 index 000000000..5f7e9163c --- /dev/null +++ b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/GradleTransformWrapper.kt @@ -0,0 +1,80 @@ +package com.tencent.shadow.core.transform + +import com.tencent.shadow.core.transform_kit.ClassTransform +import com.tencent.shadow.core.transform_kit.TransformInput +import org.gradle.api.DefaultTask +import org.gradle.api.file.Directory +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileOutputStream +import java.util.jar.JarOutputStream +import java.util.zip.ZipEntry +import javax.inject.Inject + +/** + * 适配AGP 7.2引入的新的Transform API,AGP没给这个API特别命名,但我们知道它是Gradle直接提供的Transform + * 接口。与之对应的是DeprecatedTransformWrapper适配旧的Transform API接口。 + */ +abstract class GradleTransformWrapper @Inject constructor(@Internal val classTransform: ClassTransform) : + DefaultTask() { + // This property will be set to all Jar files available in scope + @get:InputFiles + abstract val allJars: ListProperty + + // Gradle will set this property with all class directories that available in scope + @get:InputFiles + abstract val allDirectories: ListProperty + + // Task will put all classes from directories and jars after optional modification into single jar + @get:OutputFile + abstract val output: RegularFileProperty + + @TaskAction + fun taskAction() { + val inputs: List = allDirectories.get().map { + TransformInputImpl(it.asFile, TransformInput.Kind.DIRECTORY) + } + allJars.get().map { + TransformInputImpl(it.asFile, TransformInput.Kind.JAR) + } + + classTransform.beforeTransform() + classTransform.input(inputs) + classTransform.onTransform() + output(inputs) + classTransform.afterTransform() + } + + private fun output(inputs: Iterable) { + val jarOutput = JarOutputStream( + BufferedOutputStream(FileOutputStream(output.get().asFile)) + ) + jarOutput.use { + val outputClassNames = inputs.flatMap { + it.inputClassNames + } + + outputClassNames.forEach { className -> + val entryName = className.replace('.', '/') + ".class" + jarOutput.putNextEntry(ZipEntry(entryName)) + classTransform.onOutputClass(className, jarOutput) + } + } + } + + private class TransformInputImpl( + val file: File, + override val kind: Kind + ) : TransformInput() { + + override fun asFile(): File = file + + } +} + diff --git a/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle b/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle index 2d7fec529..ff6c437b4 100644 --- a/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle +++ b/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle @@ -41,6 +41,12 @@ allprojects { ext.disable_shadow_transform = true android { + try { + namespace 'app' + } catch (Exception ignored) { + // AGP 8之前找不到namespace这个方法的,不用设置。 + } + compileSdkVersion COMPILE_SDK_VERSION defaultConfig { diff --git a/projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh b/projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh index 7ba7ecd4b..e59a95d99 100755 --- a/projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh +++ b/projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh @@ -14,5 +14,12 @@ source ./test_prepare.sh # AGP release页面:https://developer.android.com/studio/releases/gradle-plugin # AGP Maven仓库:https://mvnrepository.com/artifact/com.android.tools.build/gradle # Gradle下载:https://services.gradle.org/distributions/ +setGradleVersion 8.4 +testUnderAGPVersion 8.3.0-alpha16 +setGradleVersion 8.2.1 +testUnderAGPVersion 8.2.0 +setGradleVersion 8.0.2 +testUnderAGPVersion 8.1.4 +testUnderAGPVersion 8.0.2 setGradleVersion 7.5.1 testUnderAGPVersion 7.4.1