提交 63c09f5a 编写于 作者: J johnsonlee 提交者: zhiguoWang

Supports property `booster.transform.verify` for transform verifying

上级 73033b74
package com.didiglobal.booster.gradle
import com.android.build.api.transform.Context
import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.Format
import com.android.build.api.transform.JarInput
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.SecondaryInput
import com.android.build.api.transform.Status.ADDED
import com.android.build.api.transform.Status.CHANGED
import com.android.build.api.transform.Status.NOTCHANGED
import com.android.build.api.transform.Status.REMOVED
import com.android.build.api.transform.TransformInput
import com.android.build.api.transform.TransformInvocation
import com.android.build.api.transform.TransformOutputProvider
import com.android.dx.command.dexer.Main
import com.didiglobal.booster.kotlinx.NCPU
import com.didiglobal.booster.kotlinx.file
import com.didiglobal.booster.kotlinx.green
import com.didiglobal.booster.transform.AbstractKlassPool
import com.didiglobal.booster.transform.ArtifactManager
import com.didiglobal.booster.transform.TransformContext
......@@ -21,23 +20,31 @@ import com.didiglobal.booster.transform.artifacts
import com.didiglobal.booster.transform.util.transform
import java.io.File
import java.net.URI
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
/**
* Represents a delegate of TransformInvocation
*
* @author johnsonlee
*/
internal class BoosterTransformInvocation(private val delegate: TransformInvocation, internal val transform: BoosterTransform) : TransformInvocation, TransformContext, ArtifactManager {
internal class BoosterTransformInvocation(
private val delegate: TransformInvocation,
internal val transform: BoosterTransform
) : TransformInvocation by delegate, TransformContext, ArtifactManager {
private val executor = Executors.newWorkStealingPool(NCPU)
private val project = transform.project
private val outputs = CopyOnWriteArrayList<File>()
override val name: String = delegate.context.variantName
override val projectDir: File = delegate.project.projectDir
override val projectDir: File = project.projectDir
override val buildDir: File = delegate.project.buildDir
override val buildDir: File = project.buildDir
override val temporaryDir: File = delegate.context.temporaryDir
......@@ -66,26 +73,12 @@ internal class BoosterTransformInvocation(private val delegate: TransformInvocat
@Suppress("UNCHECKED_CAST")
override fun <T> getProperty(name: String, default: T): T = project.properties[name] as? T ?: default
override fun getInputs(): MutableCollection<TransformInput> = delegate.inputs
override fun getSecondaryInputs(): MutableCollection<SecondaryInput> = delegate.secondaryInputs
override fun getReferencedInputs(): MutableCollection<TransformInput> = delegate.referencedInputs
override fun isIncremental() = delegate.isIncremental
override fun getOutputProvider(): TransformOutputProvider? = delegate.outputProvider
override fun getContext(): Context = delegate.context
override fun get(type: String) = variant.artifacts.get(type)
internal fun doFullTransform() = doTransform(this::transformFully)
internal fun doIncrementalTransform() = doTransform(this::transformIncrementally)
private val tasks = mutableListOf<Future<*>>()
private fun onPreTransform() {
transform.transformers.forEach {
it.onPreTransform(this)
......@@ -93,49 +86,56 @@ internal class BoosterTransformInvocation(private val delegate: TransformInvocat
}
private fun onPostTransform() {
tasks.forEach {
it.get()
}
transform.transformers.forEach {
it.onPostTransform(this)
}
}
private fun doTransform(block: () -> Unit) {
private fun doTransform(block: (ExecutorService) -> Iterable<Future<*>>) {
this.outputs.clear()
this.onPreTransform()
block()
val executor = Executors.newFixedThreadPool(NCPU)
try {
block(executor).forEach {
it.get()
}
} finally {
executor.shutdown()
executor.awaitTermination(1, TimeUnit.HOURS)
}
this.onPostTransform()
if (project.getProperty(OPT_TRANSFORM_VERIFY, false)) {
this.doVerify()
}
}
private fun transformFully() {
this.inputs.map {
it.jarInputs + it.directoryInputs
}.flatten().forEach { input ->
tasks += executor.submit {
val format = if (input is DirectoryInput) Format.DIRECTORY else Format.JAR
outputProvider?.let { provider ->
project.logger.info("Transforming ${input.file}")
input.transform(provider.getContentLocation(input.name, input.contentTypes, input.scopes, format), this)
}
private fun transformFully(executor: ExecutorService) = this.inputs.map {
it.jarInputs + it.directoryInputs
}.flatten().map { input ->
executor.submit {
val format = if (input is DirectoryInput) Format.DIRECTORY else Format.JAR
outputProvider?.let { provider ->
project.logger.info("Transforming ${input.file}")
input.transform(provider.getContentLocation(input.name, input.contentTypes, input.scopes, format))
}
}
}
private fun transformIncrementally() {
this.inputs.parallelStream().forEach { input ->
input.jarInputs.parallelStream().filter { it.status != NOTCHANGED }.forEach { jarInput ->
tasks += executor.submit {
doIncrementalTransform(jarInput)
}
private fun transformIncrementally(executor: ExecutorService) = this.inputs.map { input ->
input.jarInputs.filter { it.status != NOTCHANGED }.map { jarInput ->
executor.submit {
doIncrementalTransform(jarInput)
}
input.directoryInputs.parallelStream().filter { it.changedFiles.isNotEmpty() }.forEach { dirInput ->
val base = dirInput.file.toURI()
tasks += executor.submit {
doIncrementalTransform(dirInput, base)
}
} + input.directoryInputs.filter { it.changedFiles.isNotEmpty() }.map { dirInput ->
val base = dirInput.file.toURI()
executor.submit {
doIncrementalTransform(dirInput, base)
}
}
}
}.flatten()
@Suppress("NON_EXHAUSTIVE_WHEN")
private fun doIncrementalTransform(jarInput: JarInput) {
......@@ -144,7 +144,7 @@ internal class BoosterTransformInvocation(private val delegate: TransformInvocat
CHANGED, ADDED -> {
project.logger.info("Transforming ${jarInput.file}")
outputProvider?.let { provider ->
jarInput.transform(provider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR), this)
jarInput.transform(provider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR))
}
}
}
......@@ -158,10 +158,10 @@ internal class BoosterTransformInvocation(private val delegate: TransformInvocat
project.logger.info("Deleting $file")
outputProvider?.let { provider ->
provider.getContentLocation(dirInput.name, dirInput.contentTypes, dirInput.scopes, Format.DIRECTORY).parentFile.listFiles()?.asSequence()
?.filter { it.isDirectory }
?.map { File(it, dirInput.file.toURI().relativize(file.toURI()).path) }
?.filter { it.exists() }
?.forEach { it.delete() }
?.filter { it.isDirectory }
?.map { File(it, dirInput.file.toURI().relativize(file.toURI()).path) }
?.filter { it.exists() }
?.forEach { it.delete() }
}
file.delete()
}
......@@ -170,24 +170,56 @@ internal class BoosterTransformInvocation(private val delegate: TransformInvocat
outputProvider?.let { provider ->
val root = provider.getContentLocation(dirInput.name, dirInput.contentTypes, dirInput.scopes, Format.DIRECTORY)
val output = File(root, base.relativize(file.toURI()).path)
outputs += output
file.transform(output) { bytecode ->
bytecode.transform(this)
bytecode.transform()
}
}
}
}
}
}
}
private fun ByteArray.transform(invocation: BoosterTransformInvocation): ByteArray {
return invocation.transform.transformers.fold(this) { bytes, transformer ->
transformer.transform(invocation, bytes)
private fun doVerify() {
outputs.forEach { output ->
val dex = temporaryDir.file("${output.nameWithoutExtension}.dex")
val args = Main.Arguments().apply {
numThreads = NCPU
debug = true
warnings = true
emptyOk = true
multiDex = false
jarOutput = false
optimize = false
minSdkVersion = variant.extension.defaultConfig.targetSdkVersion.apiLevel
fileNames = arrayOf(output.absolutePath)
outName = dex.absolutePath
}
if (0 != Main.run(args)) {
throw VerifyError(output.absolutePath)
}
println("${green("")} $output")
dex.deleteRecursively()
}
}
}
private fun QualifiedContent.transform(output: File, invocation: BoosterTransformInvocation) {
this.file.transform(output) { bytecode ->
bytecode.transform(invocation)
private fun QualifiedContent.transform(output: File) {
outputs += output
this.file.transform(output) { bytecode ->
bytecode.transform()
}
}
private fun ByteArray.transform(): ByteArray {
return transform.transformers.fold(this) { bytes, transformer ->
transformer.transform(this@BoosterTransformInvocation, bytes)
}
}
}
\ No newline at end of file
}
/**
* The option for transform outputs verifying, default is false
*/
private const val OPT_TRANSFORM_VERIFY = "booster.transform.verify"
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册