Skip to content

Commit

Permalink
Migrate to using AGP's new variant API in the 'variant' package.
Browse files Browse the repository at this point in the history
This change adds compatibility classes to support registering an ASM transform in AGP 4.2, 7.0 and 7.1. For 4.2 reflection has to be used for all invocations of the newer variant API since their package in 4.2 is different and its not possible to reference the classes when compiling against a newer version. For 7.0 an onwards, the API are stable and can be referenced directly.

Fixes #2700

RELNOTES=Fix an incompatibility in the Hilt Gradle Plugin with AGP 7.0-beta04 and 7.1-alpha03, be aware that with this fix the HiltGradlePlugin will not work with other older alpha/beta versions of AGP 7.0 and 7.1 other than the ones mentioned in these notes.
PiperOrigin-RevId: 383895392
  • Loading branch information
danysantiago authored and Dagger Team committed Jul 9, 2021
1 parent a168754 commit be2f89a
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 44 deletions.
2 changes: 1 addition & 1 deletion java/dagger/hilt/android/plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

buildscript {
ext {
agp_version = System.getenv('AGP_VERSION') ?: "7.0.0-beta01"
agp_version = System.getenv('AGP_VERSION') ?: "7.0.0-beta04"
}
repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ package dagger.hilt.android.plugin

import com.android.build.api.attributes.BuildTypeAttr
import com.android.build.api.attributes.ProductFlavorAttr
import com.android.build.api.component.Component
import com.android.build.api.extension.AndroidComponentsExtension
import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.gradle.AppExtension
Expand All @@ -34,9 +32,10 @@ import com.android.build.gradle.api.UnitTestVariant
import dagger.hilt.android.plugin.task.AggregateDepsTask
import dagger.hilt.android.plugin.task.HiltTransformTestClassesTask
import dagger.hilt.android.plugin.util.AggregatedPackagesTransform
import dagger.hilt.android.plugin.util.AndroidComponentsExtensionCompat.Companion.getAndroidComponentsExtension
import dagger.hilt.android.plugin.util.ComponentCompat
import dagger.hilt.android.plugin.util.CopyTransform
import dagger.hilt.android.plugin.util.SimpleAGPVersion
import dagger.hilt.android.plugin.util.allTestVariants
import dagger.hilt.android.plugin.util.getSdkPath
import java.io.File
import javax.inject.Inject
Expand Down Expand Up @@ -234,7 +233,7 @@ class HiltGradlePlugin @Inject constructor(
@Suppress("UnstableApiUsage") // ASM Pipeline APIs
private fun configureBytecodeTransformASM(project: Project, hiltExtension: HiltExtension) {
var warnAboutLocalTestsFlag = false
fun registerTransform(androidComponent: Component) {
fun registerTransform(androidComponent: ComponentCompat) {
if (hiltExtension.enableTransformForLocalTests && !warnAboutLocalTestsFlag) {
project.logger.warn(
"The Hilt configuration option 'enableTransformForLocalTests' is no longer necessary " +
Expand All @@ -254,13 +253,7 @@ class HiltGradlePlugin @Inject constructor(
FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
)
}

val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.onVariants { registerTransform(it) }
androidComponents.allTestVariants(
onAndroidTest = { registerTransform(it) },
onUnitTest = { registerTransform(it) }
)
getAndroidComponentsExtension(project).onAllVariants { registerTransform(it) }
}

private fun configureBytecodeTransform(project: Project, hiltExtension: HiltExtension) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,154 @@

package dagger.hilt.android.plugin.util

import com.android.build.api.AndroidPluginVersion
import com.android.build.api.component.AndroidTest
import com.android.build.api.component.UnitTest
import com.android.build.api.extension.AndroidComponentsExtension
import com.android.build.api.extension.VariantSelector
import com.android.build.api.variant.Component
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.instrumentation.AsmClassVisitorFactory
import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.instrumentation.InstrumentationParameters
import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.api.variant.ApplicationVariant
import com.android.build.api.variant.LibraryVariant
import org.gradle.api.Project

/**
* Compatibility method to go over each Variant as Component (the newer
* 'android.build.api.variant.Variant' version, not the older one
* 'com.android.build.gradle.api.BaseVariant')
* Compatibility version of [com.android.build.api.variant.AndroidComponentsExtension]
* - In AGP 4.2 its package is 'com.android.build.api.extension'
* - In AGP 7.0 its packages is 'com.android.build.api.variant'
*/
@Suppress("UnstableApiUsage")
fun AndroidComponentsExtension<*, *, *>.allTestVariants(
onAndroidTest: (AndroidTest) -> Unit,
onUnitTest: (UnitTest) -> Unit
) {
if (
findClass("com.android.build.api.AndroidPluginVersion") != null &&
this.pluginVersion >= AndroidPluginVersion(7, 0).beta(1)
) {
this.onVariants { variant ->
when (variant) {
is ApplicationVariant -> variant.androidTest
is LibraryVariant -> variant.androidTest
else -> null
}?.let { onAndroidTest(it) }
variant.unitTest?.let { onUnitTest(it) }
sealed class AndroidComponentsExtensionCompat {

/**
* A combined compatibility function of
* [com.android.build.api.variant.AndroidComponentsExtension.onVariants] that includes also
* [AndroidTest] and [UnitTest] variants.
*/
abstract fun onAllVariants(block: (ComponentCompat) -> Unit)

class Api70Impl(
private val actual: AndroidComponentsExtension<*, *, *>
) : AndroidComponentsExtensionCompat() {
override fun onAllVariants(block: (ComponentCompat) -> Unit) {
actual.onVariants { variant ->
when (variant) {
is ApplicationVariant -> variant.androidTest
is LibraryVariant -> variant.androidTest
else -> null
}?.let { block.invoke(ComponentCompat.Api70Impl(it)) }
variant.unitTest?.let { block.invoke(ComponentCompat.Api70Impl(it)) }
}
}
}

class Api42Impl(private val actual: Any) : AndroidComponentsExtensionCompat() {

private val extensionClazz =
Class.forName("com.android.build.api.extension.AndroidComponentsExtension")

private val variantSelectorClazz =
Class.forName("com.android.build.api.extension.VariantSelector")

override fun onAllVariants(block: (ComponentCompat) -> Unit) {
val selector = extensionClazz.getDeclaredMethod("selector").invoke(actual)
val allSelector = variantSelectorClazz.getDeclaredMethod("all").invoke(selector)
val wrapFunction: (Any) -> Unit = {
block.invoke(ComponentCompat.Api42Impl(it))
}
listOf("onVariants", "androidTests", "unitTests").forEach { methodName ->
extensionClazz.getDeclaredMethod(
methodName, variantSelectorClazz, Function1::class.java
).invoke(actual, allSelector, wrapFunction)
}
}
}

companion object {
fun getAndroidComponentsExtension(project: Project): AndroidComponentsExtensionCompat {
return if (
findClass("com.android.build.api.variant.AndroidComponentsExtension") != null
) {
val actualExtension = project.extensions.getByType(AndroidComponentsExtension::class.java)
Api70Impl(actualExtension)
} else {
val actualExtension = project.extensions.getByType(
Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
)
Api42Impl(actualExtension)
}
}
}
}

/**
* Compatibility version of [com.android.build.api.variant.Component]
* - In AGP 4.2 its package is 'com.android.build.api.component'
* - In AGP 7.0 its packages is 'com.android.build.api.variant'
*/
@Suppress("UnstableApiUsage") // ASM Pipeline APIs
sealed class ComponentCompat {

/**
* Redeclaration of [com.android.build.api.variant.ComponentIdentity.name]
*/
abstract val name: String

/**
* Redeclaration of [com.android.build.api.variant.Component.transformClassesWith]
*/
abstract fun <ParamT : InstrumentationParameters> transformClassesWith(
classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
scope: InstrumentationScope,
instrumentationParamsConfig: (ParamT) -> Unit
)

/**
* Redeclaration of [com.android.build.api.variant.Component.setAsmFramesComputationMode]
*/
abstract fun setAsmFramesComputationMode(mode: FramesComputationMode)

class Api70Impl(private val component: Component) : ComponentCompat() {

override val name: String
get() = component.name

override fun <ParamT : InstrumentationParameters> transformClassesWith(
classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
scope: InstrumentationScope,
instrumentationParamsConfig: (ParamT) -> Unit
) {
component.transformClassesWith(
classVisitorFactoryImplClass, scope, instrumentationParamsConfig
)
}

override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
component.setAsmFramesComputationMode(mode)
}
}

class Api42Impl(private val actual: Any) : ComponentCompat() {

private val componentClazz = Class.forName("com.android.build.api.component.Component")

override val name: String
get() = componentClazz.getMethod("getName").invoke(actual) as String

override fun <ParamT : InstrumentationParameters> transformClassesWith(
classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
scope: InstrumentationScope,
instrumentationParamsConfig: (ParamT) -> Unit
) {
componentClazz.getDeclaredMethod(
"transformClassesWith",
Class::class.java, InstrumentationScope::class.java, Function1::class.java
).invoke(actual, classVisitorFactoryImplClass, scope, instrumentationParamsConfig)
}

override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
componentClazz.getDeclaredMethod(
"setAsmFramesComputationMode", FramesComputationMode::class.java
).invoke(actual, mode)
}
} else {
// This methods were removed in 7.0.0-beta01 but are available in 4.2.0
AndroidComponentsExtension::class.java.getDeclaredMethod(
"androidTests", VariantSelector::class.java, Function1::class.java
).invoke(this, this.selector().all(), onAndroidTest)
AndroidComponentsExtension::class.java.getDeclaredMethod(
"unitTests", VariantSelector::class.java, Function1::class.java
).invoke(this, this.selector().all(), onUnitTest)
}
}

Expand Down

0 comments on commit be2f89a

Please sign in to comment.