Skip to content

Commit

Permalink
feat(core): 支持AGP 8所需的新版Transform API
Browse files Browse the repository at this point in the history
test: 测试AGP 8.0到及更新版本
  • Loading branch information
shifujun committed Dec 4, 2023
1 parent 422035f commit 4ad00b9
Show file tree
Hide file tree
Showing 7 changed files with 161 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
Expand Up @@ -123,6 +123,8 @@ typealias ClassName_EntryName = Pair<String, String>

abstract class InputClass() {
abstract fun renameOutput(oldName: String, newName: String)

abstract fun getOutputClassNames(): Set<String>
}

class DirInputClass() : InputClass() {
Expand All @@ -144,6 +146,10 @@ class DirInputClass() : InputClass() {
outputs[newName] = File(file.parent, newFileName)
}

override fun getOutputClassNames(): Set<String> {
return outputs.keys
}

private fun getSimpleName(name: String): String {
val i = name.lastIndexOf('.')
if (i == -1) {
Expand Down Expand Up @@ -171,6 +177,9 @@ class JarInputClass() : InputClass() {
outputs[newName] = newName.replace('.', '/') + ".class"
}

override fun getOutputClassNames(): Set<String> {
return outputs.keys
}
}

abstract class TransformInput {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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.getInputClass()
}.flatMap {
it.getOutputClassNames()
}

outputClassNames.forEach { className ->
val entryName = className.replace('.', '/') + ".class"
jarOutput.putNextEntry(ZipEntry(entryName))
classTransform.onOutputClass(entryName, className, jarOutput)
}
}
}

private class TransformInputImpl(
val file: File,
override val kind: Kind
) : TransformInput() {

override fun asFile(): File = file

override fun toOutput(): File {
return file//fixme 不应该要求TransformInput实现toOutput方法
}

}
}

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 4ad00b9

Please sign in to comment.