diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 617d568ccb8..0bce159bddf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -265,3 +265,46 @@ jobs: with: name: androidTests-report-api-${{ matrix.api-level }} path: ${{ github.workspace }}/**/build/reports/androidTests/connected/* + build-gradle-plugin-latest-agp: + name: 'Build Hilt Gradle plugin against latest AGP version' + # We only run this on master push (essentially a postsubmit) since we + # don't want this job to prevent merges + if: github.event_name == 'push' && github.repository == 'google/dagger' && github.ref == 'refs/heads/master' + needs: bazel-test + runs-on: ubuntu-latest + steps: + - name: 'Install Java ${{ env.USE_JAVA_VERSION }}' + uses: actions/setup-java@v1 + with: + java-version: '${{ env.USE_JAVA_VERSION }}' + - name: 'Check out repository' + uses: actions/checkout@v2 + - name: 'Cache local Maven repository' + uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + !~/.m2/repository/com/google/dagger + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: 'Cache Bazel files' + uses: actions/cache@v2 + with: + path: ~/.cache/bazel + key: ${{ runner.os }}-bazel-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-bazel- + - name: 'Cache Gradle files' + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: 'Build and install Hilt Gradle plugin local snapshot' + run: ./util/deploy-hilt-gradle-plugin.sh "install:install-file" "LOCAL-SNAPSHOT" + env: + AGP_VERSION: '+' diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle index 573f8157512..6c17f66ffba 100644 --- a/java/dagger/hilt/android/plugin/build.gradle +++ b/java/dagger/hilt/android/plugin/build.gradle @@ -15,6 +15,9 @@ */ buildscript { + ext { + agp_version = System.getenv('AGP_VERSION') ?: "7.0.0-alpha15" + } repositories { google() mavenCentral() @@ -38,13 +41,12 @@ configurations { additionalTestPlugin { canBeConsumed = false canBeResolved = true - extendsFrom implementation } } dependencies { implementation gradleApi() - compileOnly 'com.android.tools.build:gradle:4.2.0' + compileOnly "com.android.tools.build:gradle:$agp_version" compileOnly 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20' implementation 'org.javassist:javassist:3.26.0-GA' implementation 'org.ow2.asm:asm:9.0' @@ -67,6 +69,7 @@ tasks.withType(PluginUnderTestMetadata.class).named("pluginUnderTestMetadata").c compileKotlin { kotlinOptions { jvmTarget = "1.8" + allWarningsAsErrors = true } } 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 21a108000c8..f435d115642 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 @@ -36,6 +36,7 @@ import dagger.hilt.android.plugin.task.HiltTransformTestClassesTask import dagger.hilt.android.plugin.util.AggregatedPackagesTransform 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 @@ -229,7 +230,7 @@ class HiltGradlePlugin @Inject constructor( project.dependencies.add(compileOnlyConfigName, artifactView.files) } - @Suppress("UnstableApiUsage") + @Suppress("UnstableApiUsage") // ASM Pipeline APIs private fun configureBytecodeTransformASM(project: Project, hiltExtension: HiltExtension) { var warnAboutLocalTestsFlag = false fun registerTransform(androidComponent: Component) { @@ -255,8 +256,10 @@ class HiltGradlePlugin @Inject constructor( val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) androidComponents.onVariants { registerTransform(it) } - androidComponents.androidTests { registerTransform(it) } - androidComponents.unitTests { registerTransform(it) } + androidComponents.allTestVariants( + onAndroidTest = { registerTransform(it) }, + onUnitTest = { registerTransform(it) } + ) } private fun configureBytecodeTransform(project: Project, hiltExtension: HiltExtension) { diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/AggregateDepsTask.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/AggregateDepsTask.kt index 685347cb644..d0bde562271 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/AggregateDepsTask.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/AggregateDepsTask.kt @@ -38,7 +38,7 @@ abstract class AggregateDepsTask : DefaultTask() { abstract val testEnvironment: Property @TaskAction - internal fun taskAction(inputs: InputChanges) { + internal fun taskAction(@Suppress("UNUSED_PARAMETER") inputs: InputChanges) { // TODO(danysantiago): Use Worker API, https://docs.gradle.org/current/userguide/worker_api.html val componentTrees = ComponentTreeDepsAggregator( logger = logger, diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/HiltTransformTestClassesTask.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/HiltTransformTestClassesTask.kt index f7fd140e249..2d15af8ad63 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/HiltTransformTestClassesTask.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/HiltTransformTestClassesTask.kt @@ -43,7 +43,6 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper /** * Task that transform classes used by host-side unit tests. See b/37076369 */ -@Suppress("UnstableApiUsage") abstract class HiltTransformTestClassesTask @Inject constructor( private val workerExecutor: WorkerExecutor ) : DefaultTask() { @@ -54,12 +53,14 @@ abstract class HiltTransformTestClassesTask @Inject constructor( @get:OutputDirectory abstract val outputDir: DirectoryProperty + @Suppress("UnstableApiUsage") // Worker API internal interface Parameters : WorkParameters { val name: Property val compiledClasses: ConfigurableFileCollection val outputDir: DirectoryProperty } + @Suppress("UnstableApiUsage") // Worker API abstract class WorkerAction : WorkAction { override fun execute() { val outputDir = parameters.outputDir.asFile.get() @@ -90,6 +91,7 @@ abstract class HiltTransformTestClassesTask @Inject constructor( @TaskAction fun transformClasses() { + @Suppress("UnstableApiUsage") // Worker API workerExecutor.noIsolation().submit(WorkerAction::class.java) { it.compiledClasses.from(compiledClasses) it.outputDir.set(outputDir) @@ -127,19 +129,25 @@ abstract class HiltTransformTestClassesTask @Inject constructor( // registerPreJavacGeneratedBytecode() API that would have otherwise given us a key to get // a classpath up to the generated bytecode associated with the key. val inputClasspath = - project.objects.fileCollection().from(unitTestVariant.getCompileClasspath(null)) + project.files(unitTestVariant.getCompileClasspath(null)) // Find the test sources Java compile task and add its output directory into our input // classpath file collection. This also makes the transform task depend on the test compile // task. val testCompileTaskProvider = unitTestVariant.javaCompileProvider - inputClasspath.from(testCompileTaskProvider.map { it.destinationDirectory }) + inputClasspath.from(testCompileTaskProvider.map { + @Suppress("UnstableApiUsage") // Lazy property APIs + it.destinationDirectory + }) // Similarly, if the Kotlin plugin is configured, find the test sources Kotlin compile task // and add its output directory to our input classpath file collection. project.plugins.withType(KotlinBasePluginWrapper::class.java) { val kotlinCompileTaskProvider = getCompileKotlin(unitTestVariant, project) - inputClasspath.from(kotlinCompileTaskProvider.map { it.destinationDirectory }) + inputClasspath.from(kotlinCompileTaskProvider.map { + @Suppress("UnstableApiUsage") // Lazy property APIs + it.destinationDirectory + }) } // Create and configure the transform task. @@ -152,7 +160,7 @@ abstract class HiltTransformTestClassesTask @Inject constructor( ) // Map the transform task's output to a file collection. val outputFileCollection = - project.objects.fileCollection().from(hiltTransformProvider.map { it.outputDir }) + project.files(hiltTransformProvider.map { it.outputDir }) // Configure test classpath by appending the transform output file collection to the start of // the test classpath so they override the original ones. This also makes test task (the one diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AndroidGradleCompat.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AndroidGradleCompat.kt new file mode 100644 index 00000000000..395e85bf79e --- /dev/null +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AndroidGradleCompat.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2021 The Dagger Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.ApplicationVariant +import com.android.build.api.variant.LibraryVariant + +/** + * 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') + */ +@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) } + } + } 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) + } +} + +fun findClass(fqName: String) = try { + Class.forName(fqName) +} catch (ex: ClassNotFoundException) { + null +} diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt index 5b5e1e2cbf1..c83d6354dd1 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt @@ -1,6 +1,5 @@ package dagger.hilt.android.plugin.util -import com.android.SdkConstants import java.io.File import java.io.InputStream import java.util.Properties @@ -9,13 +8,13 @@ import java.util.zip.ZipInputStream import org.gradle.api.Project /* Checks if a file is a .class file. */ -fun File.isClassFile() = this.isFile && this.extension == SdkConstants.EXT_CLASS +fun File.isClassFile() = this.isFile && this.extension == "class" /* Checks if a Zip entry is a .class file. */ -fun ZipEntry.isClassFile() = !this.isDirectory && this.name.endsWith(SdkConstants.DOT_CLASS) +fun ZipEntry.isClassFile() = !this.isDirectory && this.name.endsWith(".class") /* Checks if a file is a .jar file. */ -fun File.isJarFile() = this.isFile && this.extension == SdkConstants.EXT_JAR +fun File.isJarFile() = this.isFile && this.extension == "jar" /* Executes the given [block] function over each [ZipEntry] in this [ZipInputStream]. */ fun ZipInputStream.forEachZipEntry(block: (InputStream, ZipEntry) -> Unit) = use { 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 index 339c83e1af3..cb442b23424 100644 --- 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 @@ -46,11 +46,5 @@ internal data class SimpleAGPVersion( return SimpleAGPVersion(parts[0].toInt(), parts[1].toInt()) } - - private fun findClass(fqName: String) = try { - Class.forName(fqName) - } catch (ex: ClassNotFoundException) { - null - } } } diff --git a/util/deploy-hilt-gradle-plugin.sh b/util/deploy-hilt-gradle-plugin.sh new file mode 100644 index 00000000000..468f569b33b --- /dev/null +++ b/util/deploy-hilt-gradle-plugin.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +set -eu + +readonly MVN_GOAL="$1" +readonly VERSION_NAME="$2" +shift 2 +readonly EXTRA_MAVEN_ARGS=("$@") + +# Builds and deploy the Gradle plugin. +_deploy_plugin() { + local plugindir=java/dagger/hilt/android/plugin + ./$plugindir/gradlew -p $plugindir --no-daemon clean \ + publishAllPublicationsToMavenRepository -PPublishVersion="$VERSION_NAME" + local outdir=$plugindir/build/repo/com/google/dagger/hilt-android-gradle-plugin/$VERSION_NAME + # When building '-SNAPSHOT' versions in gradle, the filenames replaces + # '-SNAPSHOT' with timestamps, so we need to disambiguate by finding each file + # to deploy. See: https://stackoverflow.com/questions/54182823/ + local suffix + if [[ "$VERSION_NAME" == *"-SNAPSHOT" ]]; then + # Gets the timestamp part out of the name to be used as suffix. + # Timestamp format is ########.######-#. + suffix=$(find $outdir -name "*.pom" | grep -Eo '[0-9]{8}\.[0-9]{6}-[0-9]{1}') + else + suffix=$VERSION_NAME + fi + mvn "$MVN_GOAL" \ + -Dfile="$(find $outdir -name "*-$suffix.jar")" \ + -DpomFile="$(find $outdir -name "*-$suffix.pom")" \ + -Dsources="$(find $outdir -name "*-$suffix-sources.jar")" \ + -Djavadoc="$(find $outdir -name "*-$suffix-javadoc.jar")" \ + "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}" +} + +# Gradle Plugin is built with Gradle, but still deployed via Maven (mvn) +_deploy_plugin \ No newline at end of file diff --git a/util/deploy-hilt.sh b/util/deploy-hilt.sh index 3c25c10b772..d5e40a2a609 100755 --- a/util/deploy-hilt.sh +++ b/util/deploy-hilt.sh @@ -58,31 +58,3 @@ _deploy \ java/dagger/hilt/pom.xml \ java/dagger/hilt/artifact-core-src.jar \ java/dagger/hilt/artifact-core-javadoc.jar - -# Builds and deploy the Gradle plugin. -_deploy_plugin() { - local plugindir=java/dagger/hilt/android/plugin - ./$plugindir/gradlew -p $plugindir --no-daemon clean \ - publishAllPublicationsToMavenRepository -PPublishVersion="$VERSION_NAME" - local outdir=$plugindir/build/repo/com/google/dagger/hilt-android-gradle-plugin/$VERSION_NAME - # When building '-SNAPSHOT' versions in gradle, the filenames replaces - # '-SNAPSHOT' with timestamps, so we need to disambiguate by finding each file - # to deploy. See: https://stackoverflow.com/questions/54182823/ - local suffix - if [[ "$VERSION_NAME" == *"-SNAPSHOT" ]]; then - # Gets the timestamp part out of the name to be used as suffix. - # Timestamp format is ########.######-#. - suffix=$(find $outdir -name "*.pom" | grep -Eo '[0-9]{8}\.[0-9]{6}-[0-9]{1}') - else - suffix=$VERSION_NAME - fi - mvn "$MVN_GOAL" \ - -Dfile="$(find $outdir -name "*-$suffix.jar")" \ - -DpomFile="$(find $outdir -name "*-$suffix.pom")" \ - -Dsources="$(find $outdir -name "*-$suffix-sources.jar")" \ - -Djavadoc="$(find $outdir -name "*-$suffix-javadoc.jar")" \ - "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}" -} - -# Gradle Plugin is built with Gradle, but still deployed via Maven (mvn) -_deploy_plugin diff --git a/util/deploy-to-maven-central.sh b/util/deploy-to-maven-central.sh index 1a71f5feda2..fcc2423a909 100755 --- a/util/deploy-to-maven-central.sh +++ b/util/deploy-to-maven-central.sh @@ -36,6 +36,13 @@ bash $(dirname $0)/deploy-hilt.sh \ "-Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/" \ "-Dgpg.keyname=${KEY}" +bash $(dirname $0)/deploy-hilt-gradle-plugin.sh \ + "gpg:sign-and-deploy-file" \ + "$VERSION_NAME" \ + "-DrepositoryId=sonatype-nexus-staging" \ + "-Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/" \ + "-Dgpg.keyname=${KEY}" + # Note: we detach from head before making any sed changes to avoid commiting # a particular version to master. git checkout --detach diff --git a/util/install-local-snapshot.sh b/util/install-local-snapshot.sh index be6030c7407..4a43ed54537 100755 --- a/util/install-local-snapshot.sh +++ b/util/install-local-snapshot.sh @@ -12,6 +12,10 @@ bash $(dirname $0)/deploy-hilt.sh \ "install:install-file" \ "LOCAL-SNAPSHOT" +bash $(dirname $0)/deploy-hilt-gradle-plugin.sh \ + "install:install-file" \ + "LOCAL-SNAPSHOT" + echo -e "Installed local snapshot" verify_version_file() { diff --git a/util/publish-snapshot-on-commit.sh b/util/publish-snapshot-on-commit.sh index 63cc3eaa1e4..6fd6f3123c7 100755 --- a/util/publish-snapshot-on-commit.sh +++ b/util/publish-snapshot-on-commit.sh @@ -21,6 +21,13 @@ if [ "$GITHUB_REPOSITORY" == "google/dagger" ] && \ "-Durl=https://oss.sonatype.org/content/repositories/snapshots" \ "--settings=$(dirname $0)/settings.xml" + bash $(dirname $0)/deploy-hilt-gradle-plugin.sh \ + "deploy:deploy-file" \ + "HEAD-SNAPSHOT" \ + "-DrepositoryId=sonatype-nexus-snapshots" \ + "-Durl=https://oss.sonatype.org/content/repositories/snapshots" \ + "--settings=$(dirname $0)/settings.xml" + echo -e "Published maven snapshot" else echo -e "Not publishing snapshot for branch=${$GITHUB_REF}"