diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterAppTransform.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterAppTransform.kt deleted file mode 100644 index 215aa3dc8..000000000 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterAppTransform.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.didiglobal.booster.gradle - -import org.gradle.api.Project - -/** - * Represents android transform for application project - * - * @author johnsonlee - */ -@Deprecated( - message = "Use class BoosterTransform instead", - replaceWith = ReplaceWith(expression = "BoosterTransform(project)") -) -class BoosterAppTransform(project: Project) : BoosterTransform(project) diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterLibTransform.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterLibTransform.kt deleted file mode 100644 index da0f6a8db..000000000 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterLibTransform.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.didiglobal.booster.gradle - -import org.gradle.api.Project - -/** - * Represents android transform for library project - * - * @author johnsonlee - */ -@Deprecated( - message = "Use class BoosterTransform instead", - replaceWith = ReplaceWith(expression = "BoosterTransform(project)") -) -class BoosterLibTransform(project: Project) : BoosterTransform(project) diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterPlugin.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterPlugin.kt index ecf330eb2..fa26c6b27 100644 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterPlugin.kt +++ b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterPlugin.kt @@ -1,7 +1,10 @@ package com.didiglobal.booster.gradle import com.android.build.gradle.AppExtension +import com.android.build.gradle.BaseExtension import com.android.build.gradle.LibraryExtension +import com.android.build.gradle.api.BaseVariant +import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project @@ -13,34 +16,37 @@ import org.gradle.api.Project class BoosterPlugin : Plugin { override fun apply(project: Project) { - project.gradle.addListener(BoosterTransformTaskExecutionListener(project)) + project.extensions.findByName("android") ?: throw GradleException("$project is not an Android project") - when { - project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.dynamic-feature") -> project.getAndroid().let { android -> - android.registerTransform(BoosterTransform.newInstance(project)) + if (!GTE_V3_6) { + project.gradle.addListener(BoosterTransformTaskExecutionListener(project)) + } + + val android = project.getAndroid() + when (android) { + is AppExtension -> android.applicationVariants + is LibraryExtension -> android.libraryVariants + else -> emptyList() + }.takeIf>(Collection::isNotEmpty)?.let { variants -> + android.registerTransform(BoosterTransform.newInstance(project)) + if (project.state.executed) { + project.setup(variants) + } else { project.afterEvaluate { - loadVariantProcessors(project).let { processors -> - android.applicationVariants.forEach { variant -> - processors.forEach { processor -> - processor.process(variant) - } - } - } + project.setup(variants) } } - project.plugins.hasPlugin("com.android.library") -> project.getAndroid().let { android -> - android.registerTransform(BoosterTransform.newInstance(project)) - project.afterEvaluate { - loadVariantProcessors(project).let { processors -> - android.libraryVariants.forEach { variant -> - processors.forEach { processor -> - processor.process(variant) - } - } - } - } + } + } + + private fun Project.setup(variants: Collection) { + val processors = loadVariantProcessors(this) + variants.forEach { variant -> + processors.forEach { processor -> + processor.process(variant) } } } + } diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransform.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransform.kt index 991beab5f..172e61962 100644 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransform.kt +++ b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransform.kt @@ -3,11 +3,8 @@ package com.didiglobal.booster.gradle import com.android.build.api.transform.QualifiedContent import com.android.build.api.transform.Transform import com.android.build.api.transform.TransformInvocation -import com.android.build.gradle.BaseExtension import com.android.build.gradle.internal.pipeline.TransformManager -import com.didiglobal.booster.annotations.Priority import com.didiglobal.booster.gradle.internal.BoosterTransformV34 -import com.didiglobal.booster.transform.AbstractKlassPool import org.gradle.api.Project /** @@ -15,31 +12,15 @@ import org.gradle.api.Project * * @author johnsonlee */ -open class BoosterTransform protected constructor(val project: Project) : Transform() { +open class BoosterTransform protected constructor( + internal val parameter: TransformParameter +) : Transform() { - /* - * Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode - */ - internal val transformers = loadTransformers(project.buildscript.classLoader).sortedBy { - it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0 + internal val verifyEnabled by lazy { + parameter.properties[OPT_TRANSFORM_VERIFY]?.toString()?.toBoolean() ?: false } - internal val verifyEnabled = project.getProperty(OPT_TRANSFORM_VERIFY, false) - - private val android: BaseExtension = project.getAndroid() - - private lateinit var androidKlassPool: AbstractKlassPool - - init { - project.afterEvaluate { - androidKlassPool = object : AbstractKlassPool(android.bootClasspath) {} - } - } - - val bootKlassPool: AbstractKlassPool - get() = androidKlassPool - - override fun getName() = "booster" + override fun getName() = parameter.name override fun isIncremental() = !verifyEnabled @@ -48,18 +29,18 @@ open class BoosterTransform protected constructor(val project: Project) : Transf override fun getInputTypes(): MutableSet = TransformManager.CONTENT_CLASS override fun getScopes(): MutableSet = when { - transformers.isEmpty() -> mutableSetOf() - project.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT - project.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT - project.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES + parameter.transformers.isEmpty() -> mutableSetOf() + parameter.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT + parameter.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT + parameter.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES else -> TODO("Not an Android project") } override fun getReferencedScopes(): MutableSet = when { - transformers.isEmpty() -> when { - project.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT - project.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT - project.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES + parameter.transformers.isEmpty() -> when { + parameter.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT + parameter.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT + parameter.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES else -> TODO("Not an Android project") } else -> super.getReferencedScopes() @@ -78,9 +59,12 @@ open class BoosterTransform protected constructor(val project: Project) : Transf companion object { - fun newInstance(project: Project): BoosterTransform = when { - GTE_V3_4 -> BoosterTransformV34(project) - else -> BoosterTransform(project) + fun newInstance(project: Project, name: String = "booster"): BoosterTransform { + val parameter = project.newTransformParameter(name) + return when { + GTE_V3_4 -> BoosterTransformV34(parameter) + else -> BoosterTransform(parameter) + } } } diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformInvocation.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformInvocation.kt index cd108a25c..5aba2b3a7 100644 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformInvocation.kt +++ b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformInvocation.kt @@ -7,6 +7,7 @@ import com.android.build.api.transform.QualifiedContent import com.android.build.api.transform.Status.NOTCHANGED import com.android.build.api.transform.Status.REMOVED import com.android.build.api.transform.TransformInvocation +import com.android.build.gradle.BaseExtension import com.android.dex.DexFormat import com.didiglobal.booster.gradle.util.dex import com.didiglobal.booster.kotlinx.NCPU @@ -17,6 +18,7 @@ import com.didiglobal.booster.transform.AbstractKlassPool import com.didiglobal.booster.transform.ArtifactManager import com.didiglobal.booster.transform.Collector import com.didiglobal.booster.transform.TransformContext +import com.didiglobal.booster.transform.Transformer import com.didiglobal.booster.transform.artifacts import com.didiglobal.booster.transform.util.CompositeCollector import com.didiglobal.booster.transform.util.collect @@ -38,15 +40,30 @@ import java.util.concurrent.TimeUnit */ internal class BoosterTransformInvocation( private val delegate: TransformInvocation, - internal val transform: BoosterTransform + private val transform: BoosterTransform ) : TransformInvocation by delegate, TransformContext, ArtifactManager { - private val project = transform.project - private val outputs = CopyOnWriteArrayList() private val collectors = CopyOnWriteArrayList>() + /* + * Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode + */ + private val transformers: List = transform.parameter.transformers.map { + try { + it.getConstructor(ClassLoader::class.java).newInstance(transform.parameter.buildscript.classLoader) + } catch (e1: Throwable) { + try { + it.getConstructor().newInstance() + } catch (e2: Throwable) { + throw e2.apply { + addSuppressed(e1) + } + } + } + } + override val name: String = delegate.context.variantName override val projectDir: File = project.projectDir @@ -71,7 +88,13 @@ internal class BoosterTransformInvocation( } } - override val klassPool: AbstractKlassPool = object : AbstractKlassPool(compileClasspath, transform.bootKlassPool) {} + private val bootKlassPool by lazy { + object : AbstractKlassPool(project.getAndroid().bootClasspath) {} + } + + override val klassPool by lazy { + object : AbstractKlassPool(compileClasspath, bootKlassPool) {} + } override val applicationId = delegate.applicationId @@ -114,13 +137,13 @@ internal class BoosterTransformInvocation( } private fun onPreTransform() { - transform.transformers.forEach { + transformers.forEach { it.onPreTransform(this) } } private fun onPostTransform() { - transform.transformers.forEach { + transformers.forEach { it.onPostTransform(this) } } @@ -241,7 +264,7 @@ internal class BoosterTransformInvocation( } private fun ByteArray.transform(): ByteArray { - return transform.transformers.fold(this) { bytes, transformer -> + return transformers.fold(this) { bytes, transformer -> transformer.transform(this@BoosterTransformInvocation, bytes) } } diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformTaskExecutionListener.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformTaskExecutionListener.kt index d68d95539..a8de0efda 100644 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformTaskExecutionListener.kt +++ b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/BoosterTransformTaskExecutionListener.kt @@ -8,6 +8,8 @@ import org.gradle.api.Task import org.gradle.api.execution.TaskExecutionAdapter /** + * Only for AGP version lower than 3.6.0 + * * @author neighbWang */ class BoosterTransformTaskExecutionListener(private val project: Project) : TaskExecutionAdapter() { diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/ServiceLoader.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/ServiceLoader.kt index f163957fd..b8b0fa4d5 100644 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/ServiceLoader.kt +++ b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/ServiceLoader.kt @@ -11,45 +11,75 @@ internal interface ServiceLoader { fun load(vararg args: Any): List } -internal class ServiceLoaderFactory(private val classLoader: ClassLoader, private val service: Class) { +@Suppress("UNCHECKED_CAST") +private class ServiceLoaderImpl( + private val classLoader: ClassLoader, + private val service: Class, + private vararg val types: Class<*> +) : ServiceLoader { - fun newServiceLoader(vararg types: Class<*>) = object : ServiceLoader { + private val name = "META-INF/services/${service.name}" - @Suppress("UNCHECKED_CAST") - override fun load(vararg args: Any) = classLoader.getResources("META-INF/services/${service.name}")?.asSequence()?.map(::parse)?.flatten()?.toSet()?.map { provider -> + override fun load(vararg args: Any): List { + return lookup().map { provider -> try { - val providerClass = Class.forName(provider, false, classLoader) - if (!service.isAssignableFrom(providerClass)) { - throw ServiceConfigurationError("Provider $provider not a subtype") - } - try { - providerClass.getConstructor(*types).newInstance(*args) as T + provider.getConstructor(*types).newInstance(*args) as T } catch (e: NoSuchMethodException) { - providerClass.newInstance() as T + provider.newInstance() as T } } catch (e: ClassNotFoundException) { throw ServiceConfigurationError("Provider $provider not found") } - } ?: emptyList() + } + } + fun lookup(): Set> { + return classLoader.getResources(name)?.asSequence()?.map(::parse)?.flatten()?.toSet()?.mapNotNull { provider -> + try { + val providerClass = Class.forName(provider, false, classLoader) + if (!service.isAssignableFrom(providerClass)) { + throw ServiceConfigurationError("Provider $provider not a subtype") + } + providerClass as Class + } catch (e: Throwable) { + null + } + }?.toSet() ?: emptySet() } +} + +internal class ServiceLoaderFactory(private val classLoader: ClassLoader, private val service: Class) { + fun newServiceLoader(vararg types: Class<*>): ServiceLoader = ServiceLoaderImpl(classLoader, service, *types) +} +internal inline fun newServiceLoader(classLoader: ClassLoader, vararg types: Class<*>): ServiceLoader { + return ServiceLoaderFactory(classLoader, T::class.java).newServiceLoader(*types) } -internal inline fun newServiceLoader(classLoader: ClassLoader, vararg types: Class<*>) = ServiceLoaderFactory(classLoader, T::class.java).newServiceLoader(*types) +/** + * Load [Transformer]s with the specified [classLoader] + */ +@Throws(ServiceConfigurationError::class) +internal fun lookupTransformers(classLoader: ClassLoader): Set> { + return ServiceLoaderImpl(classLoader, Transformer::class.java, ClassLoader::class.java).lookup() +} /** * Load [Transformer]s with the specified [classLoader] */ @Throws(ServiceConfigurationError::class) -internal fun loadTransformers(classLoader: ClassLoader) = newServiceLoader(classLoader, ClassLoader::class.java).load(classLoader) +internal fun loadTransformers(classLoader: ClassLoader): List { + return newServiceLoader(classLoader, ClassLoader::class.java).load(classLoader) +} /** * Load [VariantProcessor]s with the specified [classLoader] */ @Throws(ServiceConfigurationError::class) -internal fun loadVariantProcessors(project: Project) = newServiceLoader(project.buildscript.classLoader, Project::class.java).load(project) +internal fun loadVariantProcessors(project: Project): List { + return newServiceLoader(project.buildscript.classLoader, Project::class.java).load(project) +} @Throws(ServiceConfigurationError::class) private fun parse(u: URL) = try { diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/TransformParameter.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/TransformParameter.kt new file mode 100644 index 000000000..86a87f172 --- /dev/null +++ b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/TransformParameter.kt @@ -0,0 +1,19 @@ +package com.didiglobal.booster.gradle + +import com.didiglobal.booster.transform.Transformer +import org.gradle.api.Project +import org.gradle.api.initialization.dsl.ScriptHandler +import org.gradle.api.plugins.PluginContainer +import java.io.Serializable + +data class TransformParameter( + val name: String, + val buildscript: ScriptHandler, + val plugins: PluginContainer, + val properties: Map, + val transformers: Set> +) : Serializable + +fun Project.newTransformParameter(name: String): TransformParameter { + return TransformParameter(name, buildscript, plugins, properties, lookupTransformers(buildscript.classLoader)) +} \ No newline at end of file diff --git a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/internal/BoosterTransformV34.kt b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/internal/BoosterTransformV34.kt index 7c1596b6d..68db710df 100644 --- a/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/internal/BoosterTransformV34.kt +++ b/booster-gradle-plugin/src/main/kotlin/com/didiglobal/booster/gradle/internal/BoosterTransformV34.kt @@ -2,9 +2,9 @@ package com.didiglobal.booster.gradle.internal import com.android.build.api.variant.VariantInfo import com.didiglobal.booster.gradle.BoosterTransform -import org.gradle.api.Project +import com.didiglobal.booster.gradle.TransformParameter -internal class BoosterTransformV34(project: Project) : BoosterTransform(project) { +internal class BoosterTransformV34(parameter: TransformParameter) : BoosterTransform(parameter) { @Suppress("UnstableApiUsage") override fun applyToVariant(variant: VariantInfo): Boolean { @@ -13,11 +13,11 @@ internal class BoosterTransformV34(project: Project) : BoosterTransform(project) @Suppress("UnstableApiUsage") private val VariantInfo.fullVariantEnabled: Boolean - get() = project.findProperty("booster.transform.${fullVariantName}.enabled")?.toString()?.toBoolean() ?: true + get() = parameter.properties["booster.transform.${fullVariantName}.enabled"]?.toString()?.toBoolean() ?: true @Suppress("UnstableApiUsage") private val VariantInfo.buildTypeEnabled: Boolean - get() = project.findProperty("booster.transform.${buildTypeName}.enabled")?.toString()?.toBoolean() ?: true + get() = parameter.properties["booster.transform.${buildTypeName}.enabled"]?.toString()?.toBoolean() ?: true }