Skip to content

Commit

Permalink
feat(core): 支持AGP 8.0+所需的新版Transform API
Browse files Browse the repository at this point in the history
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
  • Loading branch information
shifujun committed Dec 5, 2023
1 parent 671592d commit 8ac6b1f
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ internal interface AGPCompat {
fun getProcessResourcesTask(output: BaseVariantOutput): Task
fun getAaptAdditionalParameters(processResourcesTask: Task): List<String>
fun getMinSdkVersion(pluginVariant: ApplicationVariant): Int
fun hasDeprecatedTransformApi(): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@

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
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
Expand Down Expand Up @@ -52,15 +56,43 @@ class ShadowPlugin : Plugin<Project> {

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<GradleTransformWrapper>(taskProvider)
.toTransform(
ScopedArtifact.CLASSES,
GradleTransformWrapper::allJars,
GradleTransformWrapper::allDirectories,
GradleTransformWrapper::output
)
}
}
}

addFlavorForTransform(baseExtension)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<RegularFile>

// Gradle will set this property with all class directories that available in scope
@get:InputFiles
abstract val allDirectories: ListProperty<Directory>

// 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<TransformInput> = 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<TransformInput>) {
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

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 8ac6b1f

Please sign in to comment.