Skip to content

Commit

Permalink
Merge pull request #100 from Banno/agp-7-beta
Browse files Browse the repository at this point in the history
Update to AGP 7 and Gradle 7.1
  • Loading branch information
joshschriever authored Aug 2, 2021
2 parents be0502b + bddc677 commit 5c30442
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/setup-java@v1
with:
java-version: 1.8
java-version: 11

- uses: actions/checkout@v2

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/setup-java@v1
with:
java-version: 1.8
java-version: 11

- uses: actions/checkout@v2

Expand Down
5 changes: 3 additions & 2 deletions gordon-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ val aapt2Version: String by project
dependencies {
implementation(gradleKotlinDsl())
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-html:0.7.3")

implementation("com.android.tools.build:gradle:$androidGradlePluginVersion")
implementation("com.android.tools.build:bundletool:1.6.0")
implementation("com.android.tools.build:bundletool:1.6.1")
implementation("com.google.guava:guava:30.1.1-jre")
implementation("org.smali:dexlib2:2.5.2")

implementation("io.arrow-kt:arrow-core:0.13.2")
Expand Down
2 changes: 1 addition & 1 deletion gordon-plugin/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
group=com.banno.gordon
version=1.7.1
version=1.8.0
53 changes: 53 additions & 0 deletions gordon-plugin/src/main/kotlin/com/banno/gordon/AndroidPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.banno.gordon

import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.DynamicFeatureExtension
import com.android.build.api.dsl.LibraryExtension
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.DynamicFeatureAndroidComponentsExtension
import com.android.build.api.variant.LibraryAndroidComponentsExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByName

internal fun Project.androidPlugin(): AndroidPlugin<*, *>? =
when (val androidExtension = extensions.findByName("android")) {
is ApplicationExtension -> AndroidPlugin.App(
androidExtension,
extensions.getByName<ApplicationAndroidComponentsExtension>("androidComponents")
)
is LibraryExtension -> AndroidPlugin.Library(
androidExtension,
extensions.getByName<LibraryAndroidComponentsExtension>("androidComponents")
)
is DynamicFeatureExtension -> AndroidPlugin.DynamicFeature(
androidExtension,
extensions.getByName<DynamicFeatureAndroidComponentsExtension>("androidComponents")
)
else -> null
}

internal sealed class AndroidPlugin<
out T : CommonExtension<*, *, *, *>,
out U : AndroidComponentsExtension<out T, *, *>
> {

abstract val androidExtension: T
abstract val componentsExtension: U

class App(
override val androidExtension: ApplicationExtension,
override val componentsExtension: ApplicationAndroidComponentsExtension
) : AndroidPlugin<ApplicationExtension, ApplicationAndroidComponentsExtension>()

class Library(
override val androidExtension: LibraryExtension,
override val componentsExtension: LibraryAndroidComponentsExtension
) : AndroidPlugin<LibraryExtension, LibraryAndroidComponentsExtension>()

class DynamicFeature(
override val androidExtension: DynamicFeatureExtension,
override val componentsExtension: DynamicFeatureAndroidComponentsExtension
) : AndroidPlugin<DynamicFeatureExtension, DynamicFeatureAndroidComponentsExtension>()
}

This file was deleted.

202 changes: 131 additions & 71 deletions gordon-plugin/src/main/kotlin/com/banno/gordon/GordonPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,106 +1,166 @@
package com.banno.gordon

import com.android.build.api.artifact.ArtifactType
import com.android.build.api.attributes.VariantAttr
import com.android.build.api.extension.AndroidComponentsExtension
import com.android.build.api.variant.Variant
import com.android.build.api.variant.VariantBuilder
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.variant.AndroidTest
import com.android.build.gradle.AppExtension
import com.android.build.gradle.TestedExtension
import com.android.build.gradle.api.ApkVariant
import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.internal.attributes.VariantAttr
import com.android.builder.model.SigningConfig
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Attribute
import org.gradle.api.file.RegularFile
import org.gradle.api.plugins.JavaBasePlugin.VERIFICATION_GROUP
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.getByName
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.register

class GordonPlugin : Plugin<Project> {

override fun apply(project: Project) {
val androidPluginType = project.androidPluginType()
val androidPlugin = project.androidPlugin()
?: error("Gordon plugin must be applied after applying the application, library, or dynamic-feature Android plugin")

val gordonExtension = project.extensions.create<GordonExtension>("gordon")

val componentsExtension = project.extensions.getByName<AndroidComponentsExtension<VariantBuilder, Variant>>("androidComponents")
val testedExtension = project.extensions.getByType<TestedExtension>()

componentsExtension
.androidTests { testVariantProperties ->
val variantTaskName = testVariantProperties.name
.capitalize()
.replace(Regex("AndroidTest$"), "")
.replace(Regex("Debug$"), "")

project.tasks.register<GordonTestTask>("gordon$variantTaskName") {
group = VERIFICATION_GROUP
val variantDescription = variantTaskName.takeIf { it.isNotBlank() }?.let { " for $it" } ?: ""
description = "Installs and runs instrumentation tests$variantDescription."

this.rootProjectBuildDirectory.set(project.rootProject.layout.buildDirectory)

val testedVariantProperties = testVariantProperties.testedVariant

this.instrumentationApkDir.set(testVariantProperties.artifacts.get(ArtifactType.APK))
this.instrumentationPackage.set(testVariantProperties.applicationId)
fun registerGordonTask(
androidTestVariant: AndroidTest,
configuration: (GordonTestTask) -> Unit
) {
val variantTaskName = androidTestVariant.name
.capitalize()
.replace(Regex("AndroidTest$"), "")
.replace(Regex("Debug$"), "")

project.tasks.register<GordonTestTask>("gordon$variantTaskName") {
group = VERIFICATION_GROUP
val variantDescription = variantTaskName.takeIf { it.isNotBlank() }?.let { " for $it" } ?: ""
description = "Installs and runs instrumentation tests$variantDescription."
configuration(this)
}
}

if (androidPluginType != AndroidPluginType.LIBRARY) {
this.applicationPackage.set(testedVariantProperties.applicationId)
}
fun configureGordonTask(
task: GordonTestTask,
androidTestVariant: AndroidTest,
applicationPackage: Provider<String>?,
dynamicFeatureModuleManifest: Provider<RegularFile>?,
dynamicFeatureModuleName: String?,
applicationAab: Provider<RegularFile>?,
applicationSigningConfig: SigningConfig?,
testInstrumentationRunnerArguments: Map<String, String>,
animationsDisabled: Boolean
) {
task.rootProjectBuildDirectory.set(project.rootProject.layout.buildDirectory)

task.instrumentationApkDir.set(androidTestVariant.artifacts.get(SingleArtifact.APK))
task.instrumentationPackage.set(androidTestVariant.applicationId)

applicationPackage?.let(task.applicationPackage::set)

dynamicFeatureModuleManifest?.let(task.dynamicFeatureModuleManifest::set)
dynamicFeatureModuleName?.let(task.dynamicFeatureModuleName::set)

applicationAab?.let(task.applicationAab::set)

applicationSigningConfig?.storeFile?.let(task.signingKeystoreFile::set)
task.signingConfigCredentials.set(
SigningConfigCredentials(
storePassword = applicationSigningConfig?.storePassword,
keyAlias = applicationSigningConfig?.keyAlias,
keyPassword = applicationSigningConfig?.keyPassword
)
)

task.androidInstrumentationRunnerOptions.set(
androidTestVariant.instrumentationRunner.map { instrumentationRunner ->
InstrumentationRunnerOptions(
testInstrumentationRunner = instrumentationRunner,
testInstrumentationRunnerArguments = testInstrumentationRunnerArguments,
animationsDisabled = animationsDisabled
)
}
)

task.poolingStrategy.set(gordonExtension.poolingStrategy)
task.tabletShortestWidthDp.set(gordonExtension.tabletShortestWidthDp)
task.retryQuota.set(gordonExtension.retryQuota)
task.installTimeoutMillis.set(gordonExtension.installTimeoutMillis)
task.testTimeoutMillis.set(gordonExtension.testTimeoutMillis)
task.extensionTestFilter.set(gordonExtension.testFilter)
task.extensionTestInstrumentationRunner.set(gordonExtension.testInstrumentationRunner)
}

if (androidPluginType == AndroidPluginType.DYNAMIC_FEATURE) {
this.dynamicFeatureModuleManifest.set(testedVariantProperties.artifacts.get(ArtifactType.MERGED_MANIFEST))
this.dynamicFeatureModuleName.set(project.name)
}
val testedExtension = project.extensions.getByType<TestedExtension>()

val testVariant = testedExtension.testVariants.single { it.name == testVariantProperties.name }
val testedVariant = testVariant.testedVariant
val (appAabProvider, appVariant) = when (androidPluginType) {
AndroidPluginType.LIBRARY ->
null to null
AndroidPluginType.APP ->
testedVariantProperties.artifacts.get(ArtifactType.BUNDLE) to testedVariant as ApplicationVariant
AndroidPluginType.DYNAMIC_FEATURE -> {
val (appProject, appVariant) = appDependencyOfFeature(project, testedVariant as ApkVariant)
dependsOn(appProject.tasks.named("bundle${appVariant.name.capitalize()}"))
appVariant.aabOutputFile(appProject) to appVariant
}
when (androidPlugin) {
is AndroidPlugin.App -> androidPlugin.componentsExtension.onVariants { applicationVariant ->
applicationVariant.androidTest?.let { androidTestVariant ->
registerGordonTask(androidTestVariant) { gordonTask ->
val testedVariant = testedExtension.testVariants.single { it.name == androidTestVariant.name }.testedVariant as ApkVariant

configureGordonTask(
task = gordonTask,
androidTestVariant = androidTestVariant,
applicationPackage = applicationVariant.applicationId,
dynamicFeatureModuleManifest = null,
dynamicFeatureModuleName = null,
applicationAab = applicationVariant.artifacts.get(SingleArtifact.BUNDLE),
applicationSigningConfig = testedVariant.signingConfig,
testInstrumentationRunnerArguments = testedVariant.mergedFlavor.testInstrumentationRunnerArguments,
animationsDisabled = testedExtension.testOptions.animationsDisabled
)
}

appAabProvider?.let(this.applicationAab::set)

appVariant?.signingConfig?.storeFile?.let(this.signingKeystoreFile::set)
this.signingConfigCredentials.set(
SigningConfigCredentials(
storePassword = appVariant?.signingConfig?.storePassword,
keyAlias = appVariant?.signingConfig?.keyAlias,
keyPassword = appVariant?.signingConfig?.keyPassword
}
}
is AndroidPlugin.DynamicFeature -> androidPlugin.componentsExtension.onVariants { dynamicFeatureVariant ->
dynamicFeatureVariant.androidTest?.let { androidTestVariant ->
registerGordonTask(androidTestVariant) { gordonTask ->
val testedVariant = testedExtension.testVariants.single { it.name == androidTestVariant.name }.testedVariant as ApkVariant

val (appProject, appVariant) = appDependencyOfFeature(project, testedVariant)
gordonTask.dependsOn(appProject.tasks.named("bundle${appVariant.name.capitalize()}"))
val applicationAab = appVariant.aabOutputFile(appProject)
val applicationSigningConfig = appVariant.signingConfig

configureGordonTask(
task = gordonTask,
androidTestVariant = androidTestVariant,
applicationPackage = dynamicFeatureVariant.applicationId,
dynamicFeatureModuleManifest = dynamicFeatureVariant.artifacts.get(SingleArtifact.MERGED_MANIFEST),
dynamicFeatureModuleName = project.name,
applicationAab = applicationAab,
applicationSigningConfig = applicationSigningConfig,
testInstrumentationRunnerArguments = testedVariant.mergedFlavor.testInstrumentationRunnerArguments,
animationsDisabled = testedExtension.testOptions.animationsDisabled
)
)

val instrumentationRunnerOptions = testVariantProperties.instrumentationRunner.map { instrumentationRunner ->
InstrumentationRunnerOptions(
testInstrumentationRunner = instrumentationRunner,
}
}
}
is AndroidPlugin.Library -> androidPlugin.componentsExtension.onVariants { libraryVariant ->
libraryVariant.androidTest?.let { androidTestVariant ->
registerGordonTask(androidTestVariant) { gordonTask ->
val testedVariant = testedExtension.testVariants.single { it.name == androidTestVariant.name }.testedVariant

configureGordonTask(
task = gordonTask,
androidTestVariant = androidTestVariant,
applicationPackage = null,
dynamicFeatureModuleManifest = null,
dynamicFeatureModuleName = null,
applicationAab = null,
applicationSigningConfig = null,
testInstrumentationRunnerArguments = testedVariant.mergedFlavor.testInstrumentationRunnerArguments,
animationsDisabled = testedExtension.testOptions.animationsDisabled
)
}
this.androidInstrumentationRunnerOptions.set(instrumentationRunnerOptions)

this.poolingStrategy.set(gordonExtension.poolingStrategy)
this.tabletShortestWidthDp.set(gordonExtension.tabletShortestWidthDp)
this.retryQuota.set(gordonExtension.retryQuota)
this.installTimeoutMillis.set(gordonExtension.installTimeoutMillis)
this.testTimeoutMillis.set(gordonExtension.testTimeoutMillis)
this.extensionTestFilter.set(gordonExtension.testFilter)
this.extensionTestInstrumentationRunner.set(gordonExtension.testInstrumentationRunner)
}
}
}
}

private fun ApplicationVariant.aabOutputFile(appProject: Project) = appProject.layout.buildDirectory.file(
Expand All @@ -119,7 +179,7 @@ class GordonPlugin : Plugin<Project> {
.filter { it.id is ProjectComponentIdentifier }
.associateBy { featureProject.rootProject.project((it.id as ProjectComponentIdentifier).projectPath) }
.entries
.single { (project, _) -> project.androidPluginType() == AndroidPluginType.APP }
.single { (project, _) -> project.plugins.hasPlugin("com.android.application") }
.let { (appProject, component) ->
val androidVariantAttributeKey = Attribute.of(VariantAttr.ATTRIBUTE.name, String::class.java)

Expand Down
8 changes: 4 additions & 4 deletions gordon-plugin/src/main/kotlin/com/banno/gordon/Xml.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ internal class XmlElement(
append(">")
append(text.escape())
} else {
appendln(">")
children.forEach { appendln(it.toString()) }
appendLine(">")
children.forEach { appendLine(it.toString()) }
append(TAB.repeat(indentation))
}
append("</$name>")
Expand All @@ -57,8 +57,8 @@ internal fun xmlDocument(
rootElementText: String?,
block: XmlElement.() -> Unit = {}
): String = StringBuilder().run {
appendln("<?xml version='1.0' encoding='UTF-8'?>")
appendln(XmlElement(name = rootElementName, text = rootElementText).apply(block).toString())
appendLine("<?xml version='1.0' encoding='UTF-8'?>")
appendLine(XmlElement(name = rootElementName, text = rootElementText).apply(block).toString())
toString()
}

Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
androidGradlePluginVersion=4.2.0
aapt2Version=4.2.0-7147631
androidGradlePluginVersion=7.0.0
aapt2Version=7.0.0-7396180
kotlinVersion=1.5.10
kotlinterVersion=3.4.4
gradlePluginPublishVersion=0.14.0
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip
Loading

0 comments on commit 5c30442

Please sign in to comment.