• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2021 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.hilt.android.plugin.util
18 
19 import com.android.build.api.AndroidPluginVersion
20 import com.android.build.api.instrumentation.AsmClassVisitorFactory
21 import com.android.build.api.instrumentation.FramesComputationMode
22 import com.android.build.api.instrumentation.InstrumentationParameters
23 import com.android.build.api.instrumentation.InstrumentationScope
24 import com.android.build.api.variant.AndroidComponentsExtension
25 import com.android.build.api.variant.ApplicationVariant
26 import com.android.build.api.variant.Component
27 import com.android.build.api.variant.LibraryVariant
28 import com.android.build.api.variant.Variant
29 import org.gradle.api.Project
30 
31 /**
32  * Compatibility version of [com.android.build.api.variant.AndroidComponentsExtension]
33  * - In AGP 4.2 its package is 'com.android.build.api.extension'
34  * - In AGP 7.0 its packages is 'com.android.build.api.variant'
35  */
36 sealed class AndroidComponentsExtensionCompat {
37 
38   /**
39    * A combined compatibility function of
40    * [com.android.build.api.variant.AndroidComponentsExtension.onVariants] that includes also
41    * [AndroidTest] and [UnitTest] variants.
42    */
43   abstract fun onAllVariants(block: (ComponentCompat) -> Unit)
44 
45   class Api70Impl(
46     private val actual: AndroidComponentsExtension<*, *, *>
47   ) : AndroidComponentsExtensionCompat() {
48 
49     private val componentInit: (component: Component) -> ComponentCompat = {
50       if (actual.pluginVersion < AndroidPluginVersion(7, 2)) {
51         ComponentCompat.Api70Impl(it)
52       } else {
53         ComponentCompat.Api72Impl(it)
54       }
55     }
56 
57     override fun onAllVariants(block: (ComponentCompat) -> Unit) {
58       actual.onVariants { variant ->
59         // Use reflection to get the AndroidTest component out of the variant because a binary
60         // incompatible change was introduced in AGP 7.0-beta05 that changed the return type of the
61         // method.
62         fun ApplicationVariant.getAndroidTest() =
63           this::class.java.getDeclaredMethod("getAndroidTest").invoke(this) as? Component
64         fun LibraryVariant.getAndroidTest() =
65           this::class.java.getDeclaredMethod("getAndroidTest").invoke(this) as? Component
66         block.invoke(componentInit(variant))
67         when (variant) {
68           is ApplicationVariant -> variant.getAndroidTest()
69           is LibraryVariant -> variant.getAndroidTest()
70           else -> null
71         }?.let { block.invoke(componentInit(it)) }
72         // Use reflection too to get the UnitTest component since in 7.2
73         // com.android.build.api.component.UnitTest was removed and replaced by
74         // com.android.build.api.variant.UnitTest causing the return type of Variant#getUnitTest()
75         // to change and break ABI.
76         fun Variant.getUnitTest() =
77           this::class.java.getDeclaredMethod("getUnitTest").invoke(this) as? Component
78         variant.getUnitTest()?.let { block.invoke(componentInit(it)) }
79       }
80     }
81   }
82 
83   class Api42Impl(private val actual: Any) : AndroidComponentsExtensionCompat() {
84 
85     private val extensionClazz =
86       Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
87 
88     private val variantSelectorClazz =
89       Class.forName("com.android.build.api.extension.VariantSelector")
90 
91     override fun onAllVariants(block: (ComponentCompat) -> Unit) {
92       val selector = extensionClazz.getDeclaredMethod("selector").invoke(actual)
93       val allSelector = variantSelectorClazz.getDeclaredMethod("all").invoke(selector)
94       val wrapFunction: (Any) -> Unit = {
95         block.invoke(ComponentCompat.Api42Impl(it))
96       }
97       listOf("onVariants", "androidTests", "unitTests").forEach { methodName ->
98         extensionClazz.getDeclaredMethod(
99           methodName, variantSelectorClazz, Function1::class.java
100         ).invoke(actual, allSelector, wrapFunction)
101       }
102     }
103   }
104 
105   companion object {
106     fun getAndroidComponentsExtension(project: Project): AndroidComponentsExtensionCompat {
107       return if (
108         findClass("com.android.build.api.variant.AndroidComponentsExtension") != null
109       ) {
110         val actualExtension = project.extensions.getByType(AndroidComponentsExtension::class.java)
111         Api70Impl(actualExtension)
112       } else {
113         val actualExtension = project.extensions.getByType(
114           Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
115         )
116         Api42Impl(actualExtension)
117       }
118     }
119   }
120 }
121 
122 /**
123  * Compatibility version of [com.android.build.api.variant.Component]
124  * - In AGP 4.2 its package is 'com.android.build.api.component'
125  * - In AGP 7.0 its packages is 'com.android.build.api.variant'
126  */
127 @Suppress("UnstableApiUsage") // ASM Pipeline APIs
128 sealed class ComponentCompat {
129 
130   /**
131    * Redeclaration of [com.android.build.api.variant.ComponentIdentity.name]
132    */
133   abstract val name: String
134 
135   /**
136    * Redeclaration of [com.android.build.api.variant.Component.transformClassesWith]
137    */
transformClassesWithnull138   abstract fun <ParamT : InstrumentationParameters> transformClassesWith(
139     classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
140     scope: InstrumentationScope,
141     instrumentationParamsConfig: (ParamT) -> Unit
142   )
143 
144   /**
145    * Redeclaration of [com.android.build.api.variant.Component.setAsmFramesComputationMode]
146    */
147   abstract fun setAsmFramesComputationMode(mode: FramesComputationMode)
148 
149   class Api72Impl(private val component: Component) : ComponentCompat() {
150 
151     override val name: String
152       get() = component.name
153 
154     override fun <ParamT : InstrumentationParameters> transformClassesWith(
155       classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
156       scope: InstrumentationScope,
157       instrumentationParamsConfig: (ParamT) -> Unit
158     ) {
159       component.instrumentation.transformClassesWith(
160         classVisitorFactoryImplClass, scope, instrumentationParamsConfig
161       )
162     }
163 
164     override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
165       component.instrumentation.setAsmFramesComputationMode(mode)
166     }
167   }
168 
169   class Api70Impl(private val component: Component) : ComponentCompat() {
170 
171     override val name: String
172       get() = component.name
173 
transformClassesWithnull174     override fun <ParamT : InstrumentationParameters> transformClassesWith(
175       classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
176       scope: InstrumentationScope,
177       instrumentationParamsConfig: (ParamT) -> Unit
178     ) {
179       Component::class.java.getDeclaredMethod(
180         "transformClassesWith",
181         Class::class.java,
182         InstrumentationScope::class.java,
183         Function1::class.java
184       ).invoke(component, classVisitorFactoryImplClass, scope, instrumentationParamsConfig)
185     }
186 
setAsmFramesComputationModenull187     override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
188       Component::class.java.getDeclaredMethod(
189         "setAsmFramesComputationMode",
190         FramesComputationMode::class.java
191       ).invoke(component, mode)
192     }
193   }
194 
195   class Api42Impl(private val actual: Any) : ComponentCompat() {
196 
197     private val componentClazz = Class.forName("com.android.build.api.component.Component")
198 
199     override val name: String
200       get() = componentClazz.getMethod("getName").invoke(actual) as String
201 
transformClassesWithnull202     override fun <ParamT : InstrumentationParameters> transformClassesWith(
203       classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
204       scope: InstrumentationScope,
205       instrumentationParamsConfig: (ParamT) -> Unit
206     ) {
207       componentClazz.getDeclaredMethod(
208         "transformClassesWith",
209         Class::class.java, InstrumentationScope::class.java, Function1::class.java
210       ).invoke(actual, classVisitorFactoryImplClass, scope, instrumentationParamsConfig)
211     }
212 
setAsmFramesComputationModenull213     override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
214       componentClazz.getDeclaredMethod(
215         "setAsmFramesComputationMode", FramesComputationMode::class.java
216       ).invoke(actual, mode)
217     }
218   }
219 }
220 
findClassnull221 fun findClass(fqName: String) = try {
222   Class.forName(fqName)
223 } catch (ex: ClassNotFoundException) {
224   null
225 }
226