Skip to content

Commit

Permalink
Remove usage of unstable and removed Android Gradle Plugin APIs.
Browse files Browse the repository at this point in the history
The AndroidComponentsExtension's methods androidTests() and unitTests() are removed from AGP 7.0, migrate away from their usages by using the new variant.androidTest and variant.unitTest methods. To support backward compatibility for users with AGP 4.2 reflect into the old and removed methods.

Furthermore to protect against unstable API changes this change adds a postsubmit Github Action to compile the Hilt Gradle plugin against the latest released Android Gradle Plugin. This combined with turning ON all warnings as error will help such that if an API is marked as @deprecated, then the build will fail. Due to all warnings as error being ON a few additional suppressed are added, all previous ones are commented and some trivial API usages from AGP were removed. In essence only APIs from the 'com.android.build.api' package are safe to use.

Fixes: #2618
RELNOTES=Fix compatibility issue with the Hilt Gradle Plugin and Android Gradle Plugin 7.0
PiperOrigin-RevId: 374291978
  • Loading branch information
danysantiago authored and Dagger Team committed May 17, 2021
1 parent 133e710 commit 371a2c3
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 49 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: '+'
7 changes: 5 additions & 2 deletions java/dagger/hilt/android/plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/

buildscript {
ext {
agp_version = System.getenv('AGP_VERSION') ?: "7.0.0-alpha15"
}
repositories {
google()
mavenCentral()
Expand All @@ -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'
Expand All @@ -67,6 +69,7 @@ tasks.withType(PluginUnderTestMetadata.class).named("pluginUnderTestMetadata").c
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
allWarningsAsErrors = true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ abstract class AggregateDepsTask : DefaultTask() {
abstract val testEnvironment: Property<Boolean>

@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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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<String>
val compiledClasses: ConfigurableFileCollection
val outputDir: DirectoryProperty
}

@Suppress("UnstableApiUsage") // Worker API
abstract class WorkerAction : WorkAction<Parameters> {
override fun execute() {
val outputDir = parameters.outputDir.asFile.get()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
36 changes: 36 additions & 0 deletions util/deploy-hilt-gradle-plugin.sh
Original file line number Diff line number Diff line change
@@ -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
28 changes: 0 additions & 28 deletions util/deploy-hilt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions util/deploy-to-maven-central.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions util/install-local-snapshot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions util/publish-snapshot-on-commit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down

0 comments on commit 371a2c3

Please sign in to comment.