Skip to content

Commit

Permalink
refactor(core): 剥离对旧Transform API的依赖到Wrapper类
Browse files Browse the repository at this point in the history
以便再开发一个Wrapper对接新的Transform API。最大化复用已有代码。
  • Loading branch information
shifujun committed Dec 4, 2023
1 parent a66223b commit bbe68fe
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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.ShadowTransform
import com.tencent.shadow.core.transform_kit.AndroidClassPoolBuilder
import com.tencent.shadow.core.transform_kit.ClassPoolBuilder
Expand Down Expand Up @@ -51,11 +52,15 @@ class ShadowPlugin : Plugin<Project> {

val shadowExtension = project.extensions.create("shadow", ShadowExtension::class.java)
if (!project.hasProperty("disable_shadow_transform")) {
baseExtension.registerTransform(ShadowTransform(
project,
lateInitBuilder,
{ shadowExtension.transformConfig.useHostContext }
))
baseExtension.registerTransform(
DeprecatedTransformWrapper(project,
ShadowTransform(
project,
lateInitBuilder,
{ shadowExtension.transformConfig.useHostContext }
)
)
)
}

addFlavorForTransform(baseExtension)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@

package com.tencent.shadow.core.transform_kit

import com.android.build.api.transform.TransformInvocation
import javassist.ClassPool
import javassist.CtClass
import org.gradle.api.Project
import java.io.*
import java.io.BufferedWriter
import java.io.DataOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.FileWriter
import java.io.OutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.system.measureTimeMillis
Expand All @@ -46,8 +50,8 @@ abstract class AbstractTransform(
mDebugClassJarZOS = ZipOutputStream(FileOutputStream(mDebugClassJar))
}

override fun beforeTransform(invocation: TransformInvocation) {
super.beforeTransform(invocation)
override fun beforeTransform() {
super.beforeTransform()
ReplaceClassName.resetErrorCount()
cleanDebugClassFileDir()
}
Expand All @@ -61,8 +65,8 @@ abstract class AbstractTransform(
mTransformManager.fireAll()
}

override fun afterTransform(invocation: TransformInvocation) {
super.afterTransform(invocation)
override fun afterTransform() {
super.afterTransform()

mDebugClassJarZOS.flush()
mDebugClassJarZOS.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,20 @@
package com.tencent.shadow.core.transform_kit

import com.android.SdkConstants
import com.android.build.api.transform.*
import com.android.build.api.transform.QualifiedContent.ContentType
import com.android.build.api.transform.QualifiedContent.Scope
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.utils.FileUtils
import com.google.common.collect.ImmutableList
import com.google.common.io.Files
import org.gradle.api.Project
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.OutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream

/**
* 类转换基类
*
* @author cubershi
*/
abstract class ClassTransform(val project: Project) : Transform() {
val inputSet: MutableSet<TransformInput> = mutableSetOf()
abstract class ClassTransform(val project: Project) {

/**
* 获取输入文件对应的输出文件路径.即将文件this路径中的inputDir部分替换为outputDir.
Expand All @@ -50,104 +41,61 @@ abstract class ClassTransform(val project: Project) : Transform() {
return File(outputDir, this.toRelativeString(inputDir))
}

fun input(
inputs: Collection<com.android.build.api.transform.TransformInput>,
outputProvider: TransformOutputProvider
) {
fun input(inputs: Iterable<TransformInput>) {
val logger = project.logger
if (logger.isInfoEnabled) {
val sb = StringBuilder()
sb.appendln()
inputs.forEach {
it.directoryInputs.forEach {
sb.appendln(it.file.absolutePath)
}
it.jarInputs.forEach {
sb.appendln(it.file.absolutePath)
}
sb.appendln(it.asFile().absolutePath)
}
logger.info("ClassTransform input paths:$sb")
}

inputs.forEach {
it.directoryInputs.forEach {
val inputDir = it.file
val transformInput = TransformInput(it)
inputSet.add(transformInput)
val allFiles = FileUtils.getAllFiles(it.file)
allFiles.filter {
it?.name?.endsWith(SdkConstants.DOT_CLASS) ?: false
}.forEach {
val inputClass = DirInputClass()
inputClass.onInputClass(
it,
it.toOutputFile(inputDir, transformInput.toOutput(outputProvider))
)
transformInput.addInputClass(inputClass)
}
}

it.jarInputs.forEach {
val transformInput = TransformInput(it)
inputSet.add(transformInput)
ZipInputStream(FileInputStream(it.file)).use { zis ->
var entry: ZipEntry?
while (true) {
entry = zis.nextEntry
if (entry == null) break

val name = entry.name

// 忽略一些实际上不会进入编译classpath的文件
if (entry.isDirectory) continue
if (!name.endsWith(SdkConstants.DOT_CLASS)) continue
if (name.startsWith("META-INF/", true)) continue
if (name.endsWith("module-info.class", true)) continue
if (name.endsWith("package-info.class", true)) continue

// 记录好entry和name的关系,添加再添加成transform的输入
val inputClass = JarInputClass()
inputClass.onInputClass(zis, name)
inputs.forEach { transformInput ->
when (transformInput.kind) {
TransformInput.Kind.DIRECTORY -> {
val inputDir = transformInput.asFile()
val allFiles = FileUtils.getAllFiles(inputDir)
allFiles.filter {
it?.name?.endsWith(SdkConstants.DOT_CLASS) ?: false
}.forEach {
val inputClass = DirInputClass()
inputClass.onInputClass(
it,
it.toOutputFile(inputDir, transformInput.toOutput())
)
transformInput.addInputClass(inputClass)
}
}
}
}
}

fun output(outputProvider: TransformOutputProvider) {
inputSet.forEach { input ->
when (input.format) {
Format.DIRECTORY -> {
input.getInputClass().forEach {
val dirInputClass = it as DirInputClass
dirInputClass.getOutput().forEach {
val className = it.first
val file = it.second
Files.createParentDirs(file)
FileOutputStream(file).use {
onOutputClass(null, className, it)
}
}
}
}
Format.JAR -> {
val outputJar = input.toOutput(outputProvider)
Files.createParentDirs(outputJar)
ZipOutputStream(FileOutputStream(outputJar)).use { zos ->
input.getInputClass().forEach {
val jarInputClass = it as JarInputClass
jarInputClass.getOutput().forEach {
val className = it.first
val entryName = it.second
zos.putNextEntry(ZipEntry(entryName))
onOutputClass(entryName, className, zos)
}
TransformInput.Kind.JAR -> {
ZipInputStream(FileInputStream(transformInput.asFile())).use { zis ->
var entry: ZipEntry?
while (true) {
entry = zis.nextEntry
if (entry == null) break

val name = entry.name

// 忽略一些实际上不会进入编译classpath的文件
if (entry.isDirectory) continue
if (!name.endsWith(SdkConstants.DOT_CLASS)) continue
if (name.startsWith("META-INF/", true)) continue
if (name.endsWith("module-info.class", true)) continue
if (name.endsWith("package-info.class", true)) continue

// 记录好entry和name的关系,添加再添加成transform的输入
val inputClass = JarInputClass()
inputClass.onInputClass(zis, name)
transformInput.addInputClass(inputClass)
}
}
}
}
}


}

abstract fun onOutputClass(entryName: String?, className: String, outputStream: OutputStream)
Expand All @@ -158,48 +106,17 @@ abstract class ClassTransform(val project: Project) : Transform() {

abstract fun onTransform()

override fun getName(): String = this::class.simpleName!!

override fun getInputTypes(): MutableSet<ContentType> = TransformManager.CONTENT_CLASS

override fun isIncremental(): Boolean = false

override fun getScopes(): MutableSet<in Scope> = TransformManager.SCOPE_FULL_PROJECT

/**
* 每一次执行transform前调用。在一次构建中可能有多个Variant,多个Variant会共用同一个
* Transform对象(就是这个类的对象)。在这里提供一个时机清理transform过程中产生的缓存,
* 避免对下一次transform产生影响。
*/
open fun beforeTransform(invocation: TransformInvocation) {
invocation.outputProvider.deleteAll()
inputSet.clear()
open fun beforeTransform() {
}

open fun afterTransform(invocation: TransformInvocation) {
open fun afterTransform() {
}

final override fun transform(invocation: TransformInvocation) {
beforeTransform(invocation)
input(invocation.inputs, invocation.outputProvider)
onTransform()
output(invocation.outputProvider)
afterTransform(invocation)
}

override fun getSecondaryFiles(): ImmutableList<SecondaryFile>? {
val transformJar = File(this::class.java.protectionDomain.codeSource.location.toURI())
val transformKitJar =
File(ClassTransform::class.java.protectionDomain.codeSource.location.toURI())

return ImmutableList.of(
//将当前类运行所在的jar本身作为转换输入的SecondaryFiles,也就作为了这个transform task的inputs的
//一部分,这使得当这个Transform程序变化时,构建能检测到这个Transform需要重新执行。这是直接编辑这个
//Transform源码后,应用了这个Plugin的debug工程能直接生效的关键。
SecondaryFile.nonIncremental(project.files(transformJar)),
SecondaryFile.nonIncremental(project.files(transformKitJar))
)
}
}
typealias ClassName_OutputFile = Pair<String, File>
typealias ClassName_EntryName = Pair<String, String>
Expand All @@ -221,10 +138,6 @@ class DirInputClass() : InputClass() {
outputs[className] = file
}

fun getOutput(className: String): File {
return outputs[className]!!
}

override fun renameOutput(oldName: String, newName: String) {
val file = outputs.remove(oldName)!!
val newFileName = file.name.replace(getSimpleName(oldName), getSimpleName(newName))
Expand Down Expand Up @@ -254,44 +167,24 @@ class JarInputClass() : InputClass() {
outputs[className] = entryName
}

fun getOutput(className: String): String {
return outputs[className]!!
}

override fun renameOutput(oldName: String, newName: String) {
outputs[newName] = newName.replace('.', '/') + ".class"
}

}


class TransformInput(
val name: String,
val contentTypes: Set<ContentType>,
val scopes: MutableSet<in Scope>,
val format: Format
) {
constructor(di: DirectoryInput) : this(
di.name, di.contentTypes, di.scopes, Format.DIRECTORY
)

constructor(ji: JarInput) : this(
ji.name, ji.contentTypes, ji.scopes, Format.JAR
)

abstract class TransformInput {
private val inputClassSet = mutableSetOf<InputClass>()

enum class Kind { DIRECTORY, JAR }

fun addInputClass(inputClass: InputClass) {
inputClassSet.add(inputClass)
}

fun getInputClass() = inputClassSet.toSet()

fun toOutput(outputProvider: TransformOutputProvider) =
outputProvider.getContentLocation(
name,
contentTypes,
scopes,
format
)
abstract val kind: Kind
abstract fun asFile(): File
abstract fun toOutput(): File
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

package com.tencent.shadow.core.transform_kit

import com.android.build.api.transform.TransformInvocation
import javassist.ClassPool
import javassist.CtClass
import org.gradle.api.Project
Expand Down Expand Up @@ -49,8 +48,8 @@ open class JavassistTransform(project: Project, val classPoolBuilder: ClassPoolB
mCtClassInputMap[ctClass] = this
}

override fun beforeTransform(invocation: TransformInvocation) {
super.beforeTransform(invocation)
override fun beforeTransform() {
super.beforeTransform()
mCtClassInputMap.clear()
classPool = classPoolBuilder.build()
}
Expand Down
Loading

0 comments on commit bbe68fe

Please sign in to comment.