From d69b00f152a6e7e3252d65b51ae635b8656e534f Mon Sep 17 00:00:00 2001 From: Dagger Team Date: Wed, 23 Dec 2020 10:52:32 -0800 Subject: [PATCH] Migrate HiltGradlePlugin to use AGP's new ASM pipeline for bytecode transformation. Updates the HiltGradlePlugin to use AGP's new ASM pipeline if the developer is on AGP 4.2.0+, otherwise the plugin will use the older transform APIs. Using the new APIs allows for the plugin to not need a custom task to transform local tests as this is now supported with the new pipeline. If the developer is using AGP 4.2.0 then having 'enableTransformForLocalTests' is no longer needed and a warning will be shown with the hopes of migrating user away and once 4.2 is stable completely removing and deprecating the option. AGP's new transform pipeline is available in AGP 4.2.0 which is currently in beta. Fixes: https://github.com/google/dagger/issues/2118 RELNOTES=Use AGP's new transform pipeline when developer is on AGP 4.2.0+. PiperOrigin-RevId: 348818076 --- .../example/gradle/simple/app/build.gradle | 4 - .../gradle/simple/BroadcastReceiverTest.java | 45 ++++ .../example/gradle/simple/build.gradle | 2 +- .../gradle/simple/feature/build.gradle | 4 - .../gradle/simpleKotlin/app/build.gradle | 4 - .../example/gradle/simpleKotlin/build.gradle | 2 +- java/dagger/hilt/android/plugin/build.gradle | 24 ++- .../plugin/AndroidEntryPointClassVisitor.kt | 197 ++++++++++++++++++ .../hilt/android/plugin/HiltExtension.kt | 3 + .../hilt/android/plugin/HiltGradlePlugin.kt | 50 ++++- .../android/plugin/util/SimpleAGPVersion.kt | 41 ++++ .../test/kotlin/GradleTransformTestRunner.kt | 19 +- .../src/test/kotlin/HiltGradlePluginTest.kt | 2 +- .../test/kotlin/IncrementalProcessorTest.kt | 6 +- .../test/kotlin/util/SimpleAGPVersionTest.kt | 40 ++++ .../dagger-android/simple/build.gradle | 5 +- .../hilt-android/simple/build.gradle | 2 +- .../hilt-android/simpleKotlin/build.gradle | 2 +- util/run-local-tests.sh | 4 +- 19 files changed, 417 insertions(+), 39 deletions(-) create mode 100644 java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt create mode 100644 java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt create mode 100644 java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt diff --git a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle index 71e225e326a..b3687ee63c0 100644 --- a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle +++ b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle @@ -47,10 +47,6 @@ android { } } -hilt { - enableTransformForLocalTests = true -} - dependencies { implementation project(':feature') implementation 'androidx.appcompat:appcompat:1.2.0' diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java index 1557b755d31..7585bbb1d6c 100644 --- a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java +++ b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java @@ -93,6 +93,22 @@ public void verifyBaseReceiverIsNotDoubleInjected() throws InterruptedException assertThat(receiver.onReceiveCalled).isEqualTo(1); } + @Test + public void verifyComplexReceiverInjectedValue() throws InterruptedException { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + TestReceiverFour receiver = new TestReceiverFour(); + IntentFilter intentFilter = new IntentFilter("test-action"); + context.registerReceiver(receiver, intentFilter); + + Intent intent = new Intent(); + intent.setAction("test-action"); + context.sendBroadcast(intent); + + receiver.latch.await(2, TimeUnit.SECONDS); + assertThat(receiver.injectedValue).isNotEmpty(); + } + /** Test receiver */ @AndroidEntryPoint static class TestReceiverOne extends BroadcastReceiver { @@ -139,6 +155,35 @@ public void onReceive(Context context, Intent intent) { } } + /** Complex-ish test receiver */ + @AndroidEntryPoint + static class TestReceiverFour extends BroadcastReceiver { + + final CountDownLatch latch = new CountDownLatch(1); + + @Inject String injectedValue; + + @Override + public void onReceive(Context context, Intent intent) { + // Weird code, but it tests that the exception table and stack table frames are correctly + // updated in a transformation. + boolean var0; + if (context != null) { + var0 = false; + Object var1 = context.getClass(); + try { + throw new IllegalStateException(); + } catch (IllegalStateException ex) { + var0 = true; + } + } else { + BroadcastReceiver myself = this; + var0 = false; + } + latch.countDown(); + } + } + /** Base test receiver */ abstract static class BaseReceiverAbstractMethod extends BroadcastReceiver { diff --git a/java/dagger/hilt/android/example/gradle/simple/build.gradle b/java/dagger/hilt/android/example/gradle/simple/build.gradle index 46342ed1d92..a60d47f819b 100644 --- a/java/dagger/hilt/android/example/gradle/simple/build.gradle +++ b/java/dagger/hilt/android/example/gradle/simple/build.gradle @@ -17,7 +17,7 @@ buildscript { ext { kotlin_version = '1.3.61' - agp_version = System.getenv('AGP_VERSION') ?: "4.1.1" + agp_version = "4.2.0-beta01" } repositories { google() diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle index 70fa1ea3055..462aefc1eea 100644 --- a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle +++ b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle @@ -39,10 +39,6 @@ kapt { correctErrorTypes true } -hilt { - enableTransformForLocalTests = true -} - dependencies { // This is api instead of implementation since Kotlin modules here consumed // by the app need to expose @kotlin.Metadata diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle index 5fc1923e34a..bff4797997c 100644 --- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle +++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle @@ -39,10 +39,6 @@ android { } } -hilt { - enableTransformForLocalTests = true -} - dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle index 17f228968fa..c54652c91a4 100644 --- a/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle +++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle @@ -17,7 +17,7 @@ buildscript { ext { kotlin_version = '1.3.61' - agp_version = System.getenv('AGP_VERSION') ?: "4.1.1" + agp_version = "4.2.0-beta01" } repositories { google() diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle index 4445af00712..cbeb142d605 100644 --- a/java/dagger/hilt/android/plugin/build.gradle +++ b/java/dagger/hilt/android/plugin/build.gradle @@ -22,7 +22,7 @@ buildscript { } plugins { - id 'org.jetbrains.kotlin.jvm' version '1.3.61' + id 'org.jetbrains.kotlin.jvm' version '1.4.20' id 'java-gradle-plugin' id 'maven-publish' } @@ -32,15 +32,33 @@ repositories { jcenter() } +configurations { + additionalTestPlugin { + canBeConsumed = false + canBeResolved = true + extendsFrom implementation + } +} + dependencies { implementation gradleApi() - implementation 'com.android.tools.build:gradle:3.6.3' - implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61' + compileOnly 'com.android.tools.build:gradle:4.2.0-beta01' + implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20' implementation 'org.javassist:javassist:3.26.0-GA' + implementation 'org.ow2.asm:asm:9.0' testImplementation gradleTestKit() testImplementation 'junit:junit:4.12' testImplementation 'com.google.truth:truth:1.0.1' + additionalTestPlugin 'com.android.tools.build:gradle:4.2.0-beta01' +} + +// Configure the generating task of plugin-under-test-metadata.properties to +// include additional dependencies for the injected plugin classpath that +// are not present in the main runtime dependencies. This allows us to test +// the desired AGP version while keeping a compileOnly dep on the main source. +tasks.withType(PluginUnderTestMetadata.class).named("pluginUnderTestMetadata").configure { + it.pluginClasspath.from(configurations.additionalTestPlugin) } // Create sources Jar from main kotlin sources diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt new file mode 100644 index 00000000000..b3c345a1191 --- /dev/null +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt @@ -0,0 +1,197 @@ +package dagger.hilt.android.plugin + +import com.android.build.api.instrumentation.AsmClassVisitorFactory +import com.android.build.api.instrumentation.ClassContext +import com.android.build.api.instrumentation.ClassData +import com.android.build.api.instrumentation.InstrumentationParameters +import java.io.File +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.FieldVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes + +/** + * ASM Adapter that transforms @AndroidEntryPoint-annotated classes to extend the Hilt + * generated android class, including the @HiltAndroidApp application class. + */ +@Suppress("UnstableApiUsage") +class AndroidEntryPointClassVisitor( + private val apiVersion: Int, + nextClassVisitor: ClassVisitor, + private val additionalClasses: File +) : ClassVisitor(Opcodes.ASM8, nextClassVisitor) { + + interface AndroidEntryPointParams : InstrumentationParameters { + @get:Input + val additionalClassesDir: Property + } + + abstract class Factory : AsmClassVisitorFactory { + override fun createClassVisitor( + classContext: ClassContext, + nextClassVisitor: ClassVisitor + ): ClassVisitor { + return AndroidEntryPointClassVisitor( + apiVersion = instrumentationContext.apiVersion.get(), + nextClassVisitor = nextClassVisitor, + additionalClasses = parameters.get().additionalClassesDir.get() + ) + } + + /** + * Check if a class should be transformed. + * + * Only classes that are an Android entry point should be transformed. + */ + override fun isInstrumentable(classData: ClassData) = + classData.classAnnotations.any { ANDROID_ENTRY_POINT_ANNOTATIONS.contains(it) } + } + + // The name of the Hilt generated superclass in it internal form. + // e.g. "my/package/Hilt_MyActivity" + lateinit var newSuperclassName: String + + lateinit var oldSuperclassName: String + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array? + ) { + val packageName = name.substringBeforeLast('/') + val className = name.substringAfterLast('/') + newSuperclassName = + packageName + "/Hilt_" + className.replace("$", "_") + oldSuperclassName = superName ?: error { "Superclass of $name is null!" } + super.visit(version, access, name, signature, newSuperclassName, interfaces) + } + + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array? + ): MethodVisitor { + val nextMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions) + val invokeSpecialVisitor = InvokeSpecialAdapter(apiVersion, nextMethodVisitor) + if (name == ON_RECEIVE_METHOD_NAME && + descriptor == ON_RECEIVE_METHOD_DESCRIPTOR && + hasOnReceiveBytecodeInjectionMarker() + ) { + return OnReceiveAdapter(apiVersion, invokeSpecialVisitor) + } + return invokeSpecialVisitor + } + + /** + * Adapter for super calls (e.g. super.onCreate()) that rewrites the owner reference of the + * invokespecial instruction to use the new superclass. + * + * The invokespecial instruction is emitted for code that between other things also invokes a + * method of a superclass of the current class. The opcode invokespecial takes two operands, each + * of 8 bit, that together represent an address in the constant pool to a method reference. The + * method reference is computed at compile-time by looking the direct superclass declaration, but + * at runtime the code behaves like invokevirtual, where as the actual method invoked is looked up + * based on the class hierarchy. + * + * However, it has been observed that on APIs 19 to 22 the Android Runtime (ART) jumps over the + * direct superclass and into the method reference class, causing unexpected behaviours. + * Therefore, this method performs the additional transformation to rewrite direct super call + * invocations to use a method reference whose class in the pool is the new superclass. Note that + * this is not necessary for constructor calls since the Javassist library takes care of those. + * + * @see: https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokespecial + * @see: https://source.android.com/devices/tech/dalvik/dalvik-bytecode + */ + inner class InvokeSpecialAdapter( + apiVersion: Int, + nextClassVisitor: MethodVisitor + ) : MethodVisitor(apiVersion, nextClassVisitor) { + override fun visitMethodInsn( + opcode: Int, + owner: String, + name: String, + descriptor: String, + isInterface: Boolean + ) { + if (opcode == Opcodes.INVOKESPECIAL && owner == oldSuperclassName) { + // Update the owner of all INVOKESPECIAL instructions, including those found in + // constructors. + super.visitMethodInsn(opcode, newSuperclassName, name, descriptor, isInterface) + } else { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) + } + } + } + + /** + * Method adapter for a BroadcastReceiver's onReceive method to insert a super call since with + * its new superclass, onReceive will no longer be abstract (it is implemented by Hilt generated + * receiver). + */ + inner class OnReceiveAdapter( + apiVersion: Int, + nextClassVisitor: MethodVisitor + ) : MethodVisitor(apiVersion, nextClassVisitor) { + override fun visitCode() { + super.visitCode() + super.visitIntInsn(Opcodes.ALOAD, 0) // Load 'this' + super.visitIntInsn(Opcodes.ALOAD, 1) // Load method param 1 (Context) + super.visitIntInsn(Opcodes.ALOAD, 2) // Load method param 2 (Intent) + super.visitMethodInsn( + Opcodes.INVOKESPECIAL, + newSuperclassName, + ON_RECEIVE_METHOD_NAME, + ON_RECEIVE_METHOD_DESCRIPTOR, + false + ) + } + } + + /** + * Check if Hilt generated class is a BroadcastReceiver with the marker field which means + * a super.onReceive invocation has to be inserted in the implementation. + */ + private fun hasOnReceiveBytecodeInjectionMarker() = + findAdditionalClassFile(newSuperclassName).inputStream().use { + var hasMarker = false + ClassReader(it).accept( + object : ClassVisitor(apiVersion) { + override fun visitField( + access: Int, + name: String, + descriptor: String, + signature: String?, + value: Any? + ): FieldVisitor? { + if (name == "onReceiveBytecodeInjectionMarker") { + hasMarker = true + } + return null + } + }, + ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES + ) + return@use hasMarker + } + + private fun findAdditionalClassFile(className: String) = + File(additionalClasses, "$className.class") + + companion object { + val ANDROID_ENTRY_POINT_ANNOTATIONS = setOf( + "dagger.hilt.android.AndroidEntryPoint", + "dagger.hilt.android.HiltAndroidApp" + ) + const val ON_RECEIVE_METHOD_NAME = "onReceive" + const val ON_RECEIVE_METHOD_DESCRIPTOR = + "(Landroid/content/Context;Landroid/content/Intent;)V" + } +} diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt index 4f6f57831e0..14665212990 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt @@ -23,6 +23,9 @@ interface HiltExtension { * If set to `true`, Hilt will register a transform task that will rewrite `@AndroidEntryPoint` * annotated classes before the host-side JVM tests run. You should enable this option if you are * running Robolectric UI tests as part of your JUnit tests. + * + * This flag is not necessary if when com.android.tools.build:gradle:4.2.0+ is used and will be + * deprecated in a future version. */ var enableTransformForLocalTests: Boolean } diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt index fc298515f29..cfc604a4579 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt @@ -16,9 +16,15 @@ package dagger.hilt.android.plugin +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.BaseExtension import com.android.build.gradle.TestedExtension import com.android.build.gradle.api.AndroidBasePlugin +import dagger.hilt.android.plugin.util.SimpleAGPVersion +import java.io.File import org.gradle.api.Plugin import org.gradle.api.Project @@ -48,14 +54,50 @@ class HiltGradlePlugin : Plugin { } private fun configureHilt(project: Project) { - val extension = project.extensions.create( + val hiltExtension = project.extensions.create( HiltExtension::class.java, "hilt", HiltExtensionImpl::class.java ) - configureTransform(project, extension) + if (SimpleAGPVersion.ANDROID_GRADLE_PLUGIN_VERSION < SimpleAGPVersion(4, 2)) { + // Configures bytecode transform using older APIs pre AGP 4.2 + configureTransform(project, hiltExtension) + } else { + // Configures bytecode transform using AGP 4.2 ASM pipeline. + configureTransformASM(project, hiltExtension) + } configureProcessorFlags(project) } - private fun configureTransform(project: Project, extension: HiltExtension) { + @Suppress("UnstableApiUsage") + private fun configureTransformASM(project: Project, hiltExtension: HiltExtension) { + var warnAboutLocalTestsFlag = false + fun registerTransform(androidComponent: Component) { + if (hiltExtension.enableTransformForLocalTests && !warnAboutLocalTestsFlag) { + project.logger.warn( + "The Hilt configuration option 'enableTransformForLocalTests' is no longer necessary " + + "when com.android.tools.build:gradle:4.2.0+ is used." + ) + warnAboutLocalTestsFlag = true + } + androidComponent.transformClassesWith( + classVisitorFactoryImplClass = AndroidEntryPointClassVisitor.Factory::class.java, + scope = InstrumentationScope.PROJECT + ) { params -> + val classesDir = + File(project.buildDir, "intermediates/javac/${androidComponent.name}/classes") + params.additionalClassesDir.set(classesDir) + } + androidComponent.setAsmFramesComputationMode( + FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS + ) + } + + val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) + androidComponents.onVariants { registerTransform(it) } + androidComponents.androidTest { registerTransform(it) } + androidComponents.unitTest { registerTransform(it) } + } + + private fun configureTransform(project: Project, hiltExtension: HiltExtension) { val androidExtension = project.extensions.findByType(BaseExtension::class.java) ?: throw error("Android BaseExtension not found.") androidExtension.registerTransform(AndroidEntryPointTransform()) @@ -66,7 +108,7 @@ class HiltGradlePlugin : Plugin { HiltTransformTestClassesTask.create( project = project, unitTestVariant = unitTestVariant, - extension = extension + extension = hiltExtension ) } } diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt new file mode 100644 index 00000000000..1580431243b --- /dev/null +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt @@ -0,0 +1,41 @@ +package dagger.hilt.android.plugin.util + +import com.android.Version + +/** + * Simple Android Gradle Plugin version class since there is no public API one. b/175816217 + */ +internal data class SimpleAGPVersion( + val major: Int, + val minor: Int, +) : Comparable { + + override fun compareTo(other: SimpleAGPVersion): Int { + return compareValuesBy( + this, + other, + compareBy(SimpleAGPVersion::major).thenBy(SimpleAGPVersion::minor) + ) { it } + } + + companion object { + + val ANDROID_GRADLE_PLUGIN_VERSION by lazy { parse(Version.ANDROID_GRADLE_PLUGIN_VERSION) } + + fun parse(version: String?) = + tryParse(version) ?: error("Unable to parse AGP version: $version") + + private fun tryParse(version: String?): SimpleAGPVersion? { + if (version == null) { + return null + } + + val parts = version.split('.') + if (parts.size == 1) { + return SimpleAGPVersion(parts[0].toInt(), 0) + } + + return SimpleAGPVersion(parts[0].toInt(), parts[1].toInt()) + } + } +} diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt index f2f8ba06961..4ba3bcb6bb3 100644 --- a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt +++ b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt @@ -91,7 +91,7 @@ class GradleTransformTestRunner(val tempFolder: TemporaryFolder) { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:4.2.0-beta01' } } @@ -101,13 +101,13 @@ class GradleTransformTestRunner(val tempFolder: TemporaryFolder) { } android { - compileSdkVersion 29 - buildToolsVersion "29.0.2" + compileSdkVersion 30 + buildToolsVersion "30.0.2" defaultConfig { applicationId "plugin.test" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 } compileOptions { @@ -177,15 +177,14 @@ class GradleTransformTestRunner(val tempFolder: TemporaryFolder) { fun getOutput() = buildResult.output // Finds a transformed file. The srcFilePath is relative to the app's package. - fun getTransformedFile(srcFilePath: String) = File( - projectRoot, - "build/intermediates/transforms/AndroidEntryPointTransform/debug" - ).listFiles()?.first { it.isDirectory }?.let { transformedDir -> - File(transformedDir, srcFilePath).also { + fun getTransformedFile(srcFilePath: String): File { + val parentDir = + File(projectRoot, "build/intermediates/asm_instrumented_project_classes/debug") + return File(parentDir, srcFilePath).also { if (!it.exists()) { error("Unable to find transformed class ${it.path}") } } - } ?: error("Unable to find transformed output directory.") + } } } diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt index c0cf71ea9cc..89a16c467cd 100644 --- a/java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt +++ b/java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt @@ -342,6 +342,6 @@ class HiltGradlePluginTest { companion object { const val TRANSFORM_TASK_NAME = - ":transformClassesWithAndroidEntryPointTransformForDebug" + ":transformDebugClassesWithAsm" } } diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt index 631a58fa0dd..a003ab5164e 100644 --- a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt +++ b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt @@ -111,13 +111,13 @@ class IncrementalProcessorTest { } android { - compileSdkVersion 29 - buildToolsVersion "29.0.2" + compileSdkVersion 30 + buildToolsVersion "30.0.2" defaultConfig { applicationId "hilt.simple" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 } compileOptions { diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt new file mode 100644 index 00000000000..75292b83c58 --- /dev/null +++ b/java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt @@ -0,0 +1,40 @@ +package util + +import com.google.common.truth.Truth.assertThat +import dagger.hilt.android.plugin.util.SimpleAGPVersion +import org.junit.Test + +class SimpleAGPVersionTest { + + @Test + fun parsing() { + assertThat(SimpleAGPVersion.parse("4.2")) + .isEqualTo(SimpleAGPVersion(4, 2)) + assertThat(SimpleAGPVersion.parse("4.2.1")) + .isEqualTo(SimpleAGPVersion(4, 2)) + assertThat(SimpleAGPVersion.parse("7.0.0-alpha01")) + .isEqualTo(SimpleAGPVersion(7, 0)) + } + + @Test + fun comparing() { + assertThat(SimpleAGPVersion(4, 2)) + .isEqualTo(SimpleAGPVersion(4, 2)) + assertThat(SimpleAGPVersion(4, 2)) + .isGreaterThan(SimpleAGPVersion(3, 4)) + assertThat(SimpleAGPVersion(4, 2)) + .isLessThan(SimpleAGPVersion(7, 0)) + + assertThat(SimpleAGPVersion.parse("4.2.1")) + .isEqualTo(SimpleAGPVersion.parse("4.2.2")) + assertThat(SimpleAGPVersion.parse("4.2.1")) + .isGreaterThan(SimpleAGPVersion.parse("3.4.1")) + assertThat(SimpleAGPVersion.parse("4.2.1")) + .isLessThan(SimpleAGPVersion.parse("7.0.1")) + + assertThat(SimpleAGPVersion.parse("4.2.1")) + .isLessThan(SimpleAGPVersion.parse("7.0.0-alpha01")) + assertThat(SimpleAGPVersion.parse("7.0.0-alpha01")) + .isEqualTo(SimpleAGPVersion.parse("7.0.0-alpha02")) + } +} diff --git a/javatests/artifacts/dagger-android/simple/build.gradle b/javatests/artifacts/dagger-android/simple/build.gradle index f8313339219..b3f69968544 100644 --- a/javatests/artifacts/dagger-android/simple/build.gradle +++ b/javatests/artifacts/dagger-android/simple/build.gradle @@ -15,12 +15,15 @@ */ buildscript { + ext { + agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta01" + } repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath "com.android.tools.build:gradle:$agp_version" } } diff --git a/javatests/artifacts/hilt-android/simple/build.gradle b/javatests/artifacts/hilt-android/simple/build.gradle index 539137a429b..32913e31716 100644 --- a/javatests/artifacts/hilt-android/simple/build.gradle +++ b/javatests/artifacts/hilt-android/simple/build.gradle @@ -18,7 +18,7 @@ buildscript { ext { dagger_version = 'LOCAL-SNAPSHOT' kotlin_version = '1.3.61' - agp_version = System.getenv('AGP_VERSION') ?: "4.1.1" + agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta01" } repositories { google() diff --git a/javatests/artifacts/hilt-android/simpleKotlin/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/build.gradle index 17f228968fa..04cf8ada22a 100644 --- a/javatests/artifacts/hilt-android/simpleKotlin/build.gradle +++ b/javatests/artifacts/hilt-android/simpleKotlin/build.gradle @@ -17,7 +17,7 @@ buildscript { ext { kotlin_version = '1.3.61' - agp_version = System.getenv('AGP_VERSION') ?: "4.1.1" + agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta01" } repositories { google() diff --git a/util/run-local-tests.sh b/util/run-local-tests.sh index 9ea08d847f8..0a886c8c514 100755 --- a/util/run-local-tests.sh +++ b/util/run-local-tests.sh @@ -25,7 +25,9 @@ done # Run gradle tests with different versions of Android Gradle Plugin -readonly AGP_VERSIONS=("4.0.2" "3.6.4") +# At least latest stable and upcoming versions, this list can't be too long +# or else we timeout CI job. +readonly AGP_VERSIONS=("4.2.0-beta01" "4.1.0") readonly ANDROID_GRADLE_PROJECTS=( "java/dagger/example/gradle/android/simple" "javatests/artifacts/dagger-android/simple"