11package com.osacky.flank.gradle
22
3- import com.android.build.gradle.AppExtension
4- import com.android.build.gradle.TestedExtension
5- import com.android.build.gradle.internal.tasks.factory.dependsOn
6- import com.android.builder.model.TestOptions
3+ import com.android.build.api.dsl.ApplicationExtension
4+ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
5+ import com.android.build.api.variant.FilterConfiguration
6+ import com.android.build.api.variant.HasAndroidTest
77import com.osacky.flank.gradle.validation.checkForExclusionUsage
88import com.osacky.flank.gradle.validation.validateOptionsUsed
99import org.gradle.api.GradleException
1010import org.gradle.api.Project
1111import org.gradle.api.artifacts.Configuration
12+ import org.gradle.api.plugins.BasePluginExtension
1213import org.gradle.api.tasks.TaskContainer
1314import org.gradle.kotlin.dsl.create
1415import org.gradle.util.GradleVersion
@@ -34,7 +35,6 @@ class FladlePluginDelegate {
3435 }
3536
3637 private fun checkMinimumGradleVersion () {
37- // Gradle 4.9 is required because we use the lazy task configuration API.
3838 if (GRADLE_MIN_VERSION > GradleVersion .current()) {
3939 throw GradleException (" Fladle requires at minimum version $GRADLE_MIN_VERSION . Detected version ${GradleVersion .current()} ." )
4040 }
@@ -44,24 +44,22 @@ class FladlePluginDelegate {
4444 project : Project ,
4545 base : FlankGradleExtension ,
4646 ) {
47- if (GradleVersion .current() > GradleVersion .version(" 6.1" )) {
48- base.flankVersion.finalizeValueOnRead()
49- base.flankCoordinates.finalizeValueOnRead()
50- base.serviceAccountCredentials.finalizeValueOnRead()
47+ base.flankVersion.finalizeValueOnRead()
48+ base.flankCoordinates.finalizeValueOnRead()
49+ base.serviceAccountCredentials.finalizeValueOnRead()
50+
51+ // Register onVariants callbacks before afterEvaluate for APK path detection
52+ project.pluginManager.withPlugin(" com.android.application" ) {
53+ if (! base.debugApk.isPresent || ! base.instrumentationApk.isPresent) {
54+ findDebugAndInstrumentationApk(project, base)
55+ }
5156 }
57+
5258 project.afterEvaluate {
5359 // Add Flank dependency to Fladle Configuration
5460 // Must be done afterEvaluate otherwise extension values will not be set.
5561 project.dependencies.add(FLADLE_CONFIG , " ${base.flankCoordinates.get()} :${base.flankVersion.get()} " )
5662
57- // Only use automatic apk path detection for 'com.android.application' projects.
58- project.pluginManager.withPlugin(" com.android.application" ) {
59- // This doesn't work properly for multiple configs since they likely are inheriting the config from root already. See #60 https://github.com/runningcode/fladle/issues/60
60- if (! base.debugApk.isPresent || ! base.instrumentationApk.isPresent) {
61- findDebugAndInstrumentationApk(project, base)
62- }
63- }
64-
6563 tasks.apply {
6664 createTasksForConfig(base, base, project, " " )
6765
@@ -95,7 +93,7 @@ class FladlePluginDelegate {
9593
9694 val writeConfigProps = register(" writeConfigProps$name " , YamlConfigWriterTask ::class .java, base, config, name)
9795
98- writeConfigProps.dependsOn(validateFladle)
96+ writeConfigProps.configure { dependsOn(validateFladle) }
9997
10098 register(" printYml$name " ) {
10199 description = " Print the flank.yml file to the console."
@@ -136,17 +134,15 @@ class FladlePluginDelegate {
136134 }
137135 dependsOn(writeConfigProps)
138136 if (config.dependOnAssemble.isPresent && config.dependOnAssemble.get()) {
139- val testedExtension =
140- requireNotNull(project.extensions.findByType(TestedExtension ::class .java)) { " Could not find TestedExtension in ${project.name} " }
141- testedExtension.testVariants.configureEach {
142- if (testedVariant.isExpectedVariant(config)) {
143- if (testedVariant.assembleProvider.isPresent) {
144- dependsOn(testedVariant.assembleProvider)
145- }
146- if (assembleProvider.isPresent) {
147- dependsOn(assembleProvider)
148- }
149- }
137+ // Find assemble tasks by convention name pattern
138+ val variantName = config.variant.orNull
139+ if (variantName != null ) {
140+ val capitalizedVariant = variantName.capitalize()
141+ dependsOn(" assemble$capitalizedVariant " )
142+ dependsOn(" assemble${capitalizedVariant} AndroidTest" )
143+ } else {
144+ dependsOn(" assembleDebug" )
145+ dependsOn(" assembleDebugAndroidTest" )
150146 }
151147 }
152148 if (config.localResultsDir.hasValue) {
@@ -172,45 +168,79 @@ class FladlePluginDelegate {
172168 private fun automaticallyConfigureTestOrchestrator (
173169 project : Project ,
174170 config : FladleConfig ,
175- androidExtension : AppExtension ,
171+ androidExtension : ApplicationExtension ,
176172 ) {
177173 project.afterEvaluate {
174+ val execution = androidExtension.testOptions.execution.uppercase()
178175 val useOrchestrator =
179- androidExtension.testOptions.getExecutionEnum() == TestOptions . Execution . ANDROIDX_TEST_ORCHESTRATOR ||
180- androidExtension.testOptions.getExecutionEnum() == TestOptions . Execution . ANDROID_TEST_ORCHESTRATOR
176+ execution == " ANDROIDX_TEST_ORCHESTRATOR" ||
177+ execution == " ANDROID_TEST_ORCHESTRATOR"
181178 if (useOrchestrator) {
182179 log(" Automatically detected the use of Android Test Orchestrator" )
180+ config.useOrchestrator.set(true )
183181 }
184- config.useOrchestrator.set(useOrchestrator)
185182 }
186183 }
187184
188185 private fun findDebugAndInstrumentationApk (
189186 project : Project ,
190187 config : FladleConfig ,
191188 ) {
192- val baseExtension =
193- requireNotNull(project.extensions.findByType(AppExtension ::class .java)) { " Could not find AppExtension in ${project.name} " }
194- automaticallyConfigureTestOrchestrator(project, config, baseExtension)
195- baseExtension.testVariants.configureEach {
196- val appVariant = testedVariant
197- outputs.configureEach test@{
198- appVariant.outputs
199- .matching { it.isExpectedAbiOutput(config) }
200- .configureEach app@{
201- if (appVariant.isExpectedVariant(config)) {
202- if (! config.debugApk.isPresent) {
203- // Don't set debug apk if not already set. #172
204- project.log(" Configuring fladle.debugApk from variant ${this @app.name} " )
205- config.debugApk.set(this @app.outputFile.absolutePath)
206- }
207- if (! config.roboScript.isPresent && ! config.instrumentationApk.isPresent && ! config.sanityRobo.get()) {
208- // Don't set instrumentation apk if not already set. #172
209- project.log(" Configuring fladle.instrumentationApk from variant ${this @test.name} " )
210- config.instrumentationApk.set(this @test.outputFile.absolutePath)
211- }
212- }
189+ val androidExtension =
190+ requireNotNull(project.extensions.findByType(ApplicationExtension ::class .java)) { " Could not find ApplicationExtension in ${project.name} " }
191+ automaticallyConfigureTestOrchestrator(project, config, androidExtension)
192+
193+ val androidComponents =
194+ requireNotNull(project.extensions.findByType(ApplicationAndroidComponentsExtension ::class .java)) {
195+ " Could not find ApplicationAndroidComponentsExtension in ${project.name} "
196+ }
197+
198+ androidComponents.onVariants { variant ->
199+ if (! variant.isExpectedVariant(config)) return @onVariants
200+ val androidTest = (variant as ? HasAndroidTest )?.androidTest ? : return @onVariants
201+
202+ val buildType = variant.buildType ? : return @onVariants
203+ val flavorName = variant.productFlavors.joinToString(" " ) { it.second }
204+ val flavorPath = variant.productFlavors.joinToString(" /" ) { it.second }
205+ val archivesName = project.extensions.getByType(BasePluginExtension ::class .java).archivesName.get()
206+ val buildDir = project.layout.buildDirectory
207+
208+ // Test APK path
209+ val testApkDirPath = if (flavorPath.isNotEmpty()) " androidTest/$flavorPath /$buildType " else " androidTest/$buildType "
210+ val testApkFileName =
211+ if (flavorName.isNotEmpty()) {
212+ " $archivesName -$flavorName -$buildType -androidTest.apk"
213+ } else {
214+ " $archivesName -$buildType -androidTest.apk"
215+ }
216+ val testApkPath = buildDir.file(" outputs/apk/$testApkDirPath /$testApkFileName " ).get().asFile.absolutePath
217+
218+ variant.outputs.forEach { output ->
219+ if (! output.isExpectedAbiOutput(config)) return @forEach
220+
221+ val abiFilter = output.filters.firstOrNull { it.filterType == FilterConfiguration .FilterType .ABI }
222+ val abiName = abiFilter?.identifier
223+
224+ val appApkDirPath = if (flavorPath.isNotEmpty()) " $flavorPath /$buildType " else buildType
225+ val appApkFileName =
226+ buildString {
227+ append(archivesName)
228+ if (flavorName.isNotEmpty()) append(" -$flavorName " )
229+ if (abiName != null ) append(" -$abiName " )
230+ append(" -$buildType .apk" )
213231 }
232+ val appApkPath = buildDir.file(" outputs/apk/$appApkDirPath /$appApkFileName " ).get().asFile.absolutePath
233+
234+ if (! config.debugApk.isPresent) {
235+ // Don't set debug apk if not already set. #172
236+ project.log(" Configuring fladle.debugApk from variant ${variant.name} " )
237+ config.debugApk.set(appApkPath)
238+ }
239+ if (! config.roboScript.isPresent && ! config.instrumentationApk.isPresent && ! config.sanityRobo.get()) {
240+ // Don't set instrumentation apk if not already set. #172
241+ project.log(" Configuring fladle.instrumentationApk from variant ${variant.name} " )
242+ config.instrumentationApk.set(testApkPath)
243+ }
214244 }
215245 }
216246 }
@@ -219,7 +249,7 @@ class FladlePluginDelegate {
219249 get() = configurations.getByName(FLADLE_CONFIG )
220250
221251 companion object {
222- val GRADLE_MIN_VERSION : GradleVersion = GradleVersion .version(" 7.3 " )
252+ val GRADLE_MIN_VERSION : GradleVersion = GradleVersion .version(" 9.1 " )
223253 const val TASK_GROUP = " fladle"
224254 const val FLADLE_CONFIG = " fladle"
225255
0 commit comments