Skip to content

Commit 03d0dc1

Browse files
liutikasGerrit Code Review
authored and
Gerrit Code Review
committed
Merge "Move to only building APKs that are required for testing" into androidx-main
2 parents 12ade05 + 89ccd88 commit 03d0dc1

File tree

5 files changed

+165
-163
lines changed

5 files changed

+165
-163
lines changed

buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt

-54
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@ import androidx.build.checkapi.KmpApiTaskConfig
2929
import androidx.build.checkapi.LibraryApiTaskConfig
3030
import androidx.build.checkapi.configureProjectForApiTasks
3131
import androidx.build.dependencies.KOTLIN_VERSION
32-
import androidx.build.dependencyTracker.AffectedModuleDetector
3332
import androidx.build.docs.AndroidXKmpDocsImplPlugin
3433
import androidx.build.gradle.isRoot
3534
import androidx.build.license.configureExternalDependencyLicenseCheck
3635
import androidx.build.resources.configurePublicResourcesStub
3736
import androidx.build.sbom.validateAllArchiveInputsRecognized
3837
import androidx.build.studio.StudioTask
3938
import androidx.build.testConfiguration.addAppApkToTestConfigGeneration
40-
import androidx.build.testConfiguration.addToTestZips
4139
import androidx.build.testConfiguration.configureTestConfigGeneration
4240
import com.android.build.api.artifact.SingleArtifact
4341
import com.android.build.api.dsl.ManagedVirtualDevice
@@ -54,7 +52,6 @@ import com.android.build.gradle.LibraryPlugin
5452
import com.android.build.gradle.TestExtension
5553
import com.android.build.gradle.TestPlugin
5654
import com.android.build.gradle.TestedExtension
57-
import com.android.build.gradle.internal.tasks.ListingFileRedirectTask
5855
import java.io.File
5956
import java.time.Duration
6057
import java.util.Locale
@@ -702,44 +699,13 @@ class AndroidXImplPlugin @Inject constructor(val componentFactory: SoftwareCompo
702699
project.configureTestConfigGeneration(this)
703700
project.configureFtlRunner()
704701

705-
val buildTestApksTask = project.rootProject.tasks.named(BUILD_TEST_APKS_TASK)
706-
when (this) {
707-
is TestedExtension -> testVariants
708-
// app module defines variants for test module
709-
is TestExtension -> applicationVariants
710-
else -> throw IllegalStateException("Unsupported plugin type")
711-
}.all { variant ->
712-
buildTestApksTask.configure {
713-
it.dependsOn(variant.assembleProvider)
714-
}
715-
variant.configureApkZipping(project)
716-
}
717-
718702
// AGP warns if we use project.buildDir (or subdirs) for CMake's generated
719703
// build files (ninja build files, CMakeCache.txt, etc.). Use a staging directory that
720704
// lives alongside the project's buildDir.
721705
externalNativeBuild.cmake.buildStagingDirectory =
722706
File(project.buildDir, "../nativeBuildStaging")
723707
}
724708

725-
/**
726-
* Configures the ZIP_TEST_CONFIGS_WITH_APKS_TASK to include the test apk if applicable
727-
*/
728-
@Suppress("DEPRECATION") // ApkVariant
729-
private fun com.android.build.gradle.api.ApkVariant.configureApkZipping(
730-
project: Project
731-
) {
732-
packageApplicationProvider.get().let { packageTask ->
733-
AffectedModuleDetector.configureTaskGuard(packageTask)
734-
addToTestZips(project, packageTask)
735-
}
736-
// This task needs to be guarded by AffectedModuleDetector due to guarding test
737-
// APK building above. It can only be removed if we stop using AMD for test APKs.
738-
project.tasks.withType(ListingFileRedirectTask::class.java).forEach {
739-
AffectedModuleDetector.configureTaskGuard(it)
740-
}
741-
}
742-
743709
private fun LibraryExtension.configureAndroidLibraryOptions(
744710
project: Project,
745711
androidXExtension: AndroidXExtension
@@ -850,17 +816,6 @@ class AndroidXImplPlugin @Inject constructor(val componentFactory: SoftwareCompo
850816

851817
project.addAppApkToTestConfigGeneration()
852818
project.addAppApkToFtlRunner()
853-
854-
val buildTestApksTask = project.rootProject.tasks.named(BUILD_TEST_APKS_TASK)
855-
applicationVariants.all { variant ->
856-
// Using getName() instead of name due to b/150427408
857-
if (variant.buildType.name == "debug") {
858-
buildTestApksTask.configure {
859-
it.dependsOn(variant.assembleProvider)
860-
}
861-
}
862-
variant.configureApkZipping(project)
863-
}
864819
}
865820

866821
private fun Project.configureDependencyVerification(
@@ -1001,7 +956,6 @@ class AndroidXImplPlugin @Inject constructor(val componentFactory: SoftwareCompo
1001956
}
1002957

1003958
companion object {
1004-
const val BUILD_TEST_APKS_TASK = "buildTestApks"
1005959
const val CREATE_LIBRARY_BUILD_INFO_FILES_TASK = "createLibraryBuildInfoFiles"
1006960
const val GENERATE_TEST_CONFIGURATION_TASK = "GenerateTestConfiguration"
1007961
const val ZIP_TEST_CONFIGS_WITH_APKS_TASK = "zipTestConfigsWithApks"
@@ -1097,14 +1051,6 @@ private fun Project.configureJavaCompilationWarnings(androidXExtension: AndroidX
10971051
}
10981052
}
10991053

1100-
/**
1101-
* Guarantees unique names for the APKs, and modifies some of the suffixes. The APK name is used
1102-
* to determine what gets run by our test runner
1103-
*/
1104-
fun String.renameApkForTesting(projectPath: String): String {
1105-
return "${projectPath.asFilenamePrefix()}_$this"
1106-
}
1107-
11081054
fun Project.hasBenchmarkPlugin(): Boolean {
11091055
return this.plugins.hasPlugin(BenchmarkPlugin::class.java)
11101056
}

buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,6 @@ abstract class AndroidXRootImplPlugin : Plugin<Project> {
136136
}
137137
}
138138

139-
tasks.register(AndroidXImplPlugin.BUILD_TEST_APKS_TASK)
140-
141139
// NOTE: this task is used by the Github CI as well. If you make any changes here,
142140
// please update the .github/workflows files as well, if necessary.
143141
project.tasks.register(
@@ -150,6 +148,7 @@ abstract class AndroidXRootImplPlugin : Plugin<Project> {
150148
it.entryCompression = ZipEntryCompression.STORED
151149
// Archive is greater than 4Gb :O
152150
it.isZip64 = true
151+
it.isReproducibleFileOrder = true
153152
}
154153
project.tasks.register(
155154
ZIP_CONSTRAINED_TEST_CONFIGS_WITH_APKS_TASK, Zip::class.java
@@ -161,6 +160,7 @@ abstract class AndroidXRootImplPlugin : Plugin<Project> {
161160
it.entryCompression = ZipEntryCompression.STORED
162161
// Archive is greater than 4Gb :O
163162
it.isZip64 = true
163+
it.isReproducibleFileOrder = true
164164
}
165165

166166
AffectedModuleDetector.configure(gradle, this)

buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt

+51-43
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
package androidx.build.testConfiguration
1818

1919
import androidx.build.dependencyTracker.ProjectSubset
20-
import androidx.build.renameApkForTesting
21-
import com.android.build.api.variant.BuiltArtifact
2220
import com.android.build.api.variant.BuiltArtifacts
2321
import com.android.build.api.variant.BuiltArtifactsLoader
2422
import java.io.File
@@ -78,18 +76,6 @@ abstract class GenerateMediaTestConfigurationTask : DefaultTask() {
7876
@get:Input
7977
abstract val affectedModuleDetectorSubset: Property<ProjectSubset>
8078

81-
@get:Input
82-
abstract val clientToTPath: Property<String>
83-
84-
@get:Input
85-
abstract val clientPreviousPath: Property<String>
86-
87-
@get:Input
88-
abstract val serviceToTPath: Property<String>
89-
90-
@get:Input
91-
abstract val servicePreviousPath: Property<String>
92-
9379
@get:Input
9480
abstract val minSdk: Property<Int>
9581

@@ -117,39 +103,60 @@ abstract class GenerateMediaTestConfigurationTask : DefaultTask() {
117103
@get:OutputFile
118104
abstract val jsonClientToTServiceToTServiceTests: RegularFileProperty
119105

106+
@get:OutputFile
107+
abstract val previousClientApk: RegularFileProperty
108+
109+
@get:OutputFile
110+
abstract val totClientApk: RegularFileProperty
111+
112+
@get:OutputFile
113+
abstract val previousServiceApk: RegularFileProperty
114+
115+
@get:OutputFile
116+
abstract val totServiceApk: RegularFileProperty
117+
120118
@TaskAction
121119
fun generateAndroidTestZip() {
122-
val clientToTApk = resolveApk(clientToTFolder, clientToTLoader)
123-
val clientPreviousApk = resolveApk(clientPreviousFolder, clientPreviousLoader)
124-
val serviceToTApk = resolveApk(serviceToTFolder, serviceToTLoader)
125-
val servicePreviousApk = resolveApk(
126-
servicePreviousFolder, servicePreviousLoader
120+
val clientToTApk = totClientApk.get().asFile
121+
val clientToTSha256 = copyApkAndGetSha256(clientToTFolder, clientToTLoader, clientToTApk)
122+
val clientPreviousApk = previousClientApk.get().asFile
123+
val clientPreviousSha256 = copyApkAndGetSha256(
124+
clientPreviousFolder, clientPreviousLoader, clientPreviousApk
127125
)
126+
val serviceToTApk = totServiceApk.get().asFile
127+
val serviceToTSha256 = copyApkAndGetSha256(
128+
serviceToTFolder, serviceToTLoader, serviceToTApk
129+
)
130+
val servicePreviousApk = previousServiceApk.get().asFile
131+
val servicePreviousSha256 = copyApkAndGetSha256(
132+
servicePreviousFolder, servicePreviousLoader, servicePreviousApk
133+
)
134+
128135
writeConfigFileContent(
129-
clientApk = clientToTApk,
130-
serviceApk = serviceToTApk,
131-
clientPath = clientToTPath.get(),
132-
servicePath = serviceToTPath.get(),
136+
clientApkName = clientToTApk.name,
137+
serviceApkName = serviceToTApk.name,
138+
clientApkSha256 = clientToTSha256,
139+
serviceApkSha256 = serviceToTSha256,
133140
jsonClientOutputFile = jsonClientToTServiceToTClientTests,
134141
jsonServiceOutputFile = jsonClientToTServiceToTServiceTests,
135142
isClientPrevious = false,
136143
isServicePrevious = false
137144
)
138145
writeConfigFileContent(
139-
clientApk = clientToTApk,
140-
serviceApk = servicePreviousApk,
141-
clientPath = clientToTPath.get(),
142-
servicePath = servicePreviousPath.get(),
146+
clientApkName = clientToTApk.name,
147+
serviceApkName = servicePreviousApk.name,
148+
clientApkSha256 = clientToTSha256,
149+
serviceApkSha256 = servicePreviousSha256,
143150
jsonClientOutputFile = jsonClientToTServicePreviousClientTests,
144151
jsonServiceOutputFile = jsonClientToTServicePreviousServiceTests,
145152
isClientPrevious = false,
146153
isServicePrevious = true
147154
)
148155
writeConfigFileContent(
149-
clientApk = clientPreviousApk,
150-
serviceApk = serviceToTApk,
151-
clientPath = clientPreviousPath.get(),
152-
servicePath = serviceToTPath.get(),
156+
clientApkName = clientPreviousApk.name,
157+
serviceApkName = serviceToTApk.name,
158+
clientApkSha256 = clientPreviousSha256,
159+
serviceApkSha256 = serviceToTSha256,
153160
jsonClientOutputFile = jsonClientPreviousServiceToTClientTests,
154161
jsonServiceOutputFile = jsonClientPreviousServiceToTServiceTests,
155162
isClientPrevious = true,
@@ -165,26 +172,27 @@ abstract class GenerateMediaTestConfigurationTask : DefaultTask() {
165172
?: throw RuntimeException("Cannot load required APK for task: $name")
166173
}
167174

168-
private fun BuiltArtifact.resolveName(path: String): String {
169-
return outputFile.substringAfterLast("/").renameApkForTesting(path)
175+
private fun copyApkAndGetSha256(
176+
apkFolder: DirectoryProperty,
177+
apkLoader: Property<BuiltArtifactsLoader>,
178+
destination: File
179+
): String {
180+
val artifacts = apkLoader.get().load(apkFolder.get())
181+
?: throw RuntimeException("Cannot load required APK for task: $name")
182+
File(artifacts.elements.single().outputFile).copyTo(destination, overwrite = true)
183+
return sha256(destination)
170184
}
171185

172186
private fun writeConfigFileContent(
173-
clientApk: BuiltArtifacts,
174-
serviceApk: BuiltArtifacts,
175-
clientPath: String,
176-
servicePath: String,
187+
clientApkName: String,
188+
serviceApkName: String,
189+
clientApkSha256: String,
190+
serviceApkSha256: String,
177191
jsonClientOutputFile: RegularFileProperty,
178192
jsonServiceOutputFile: RegularFileProperty,
179193
isClientPrevious: Boolean,
180194
isServicePrevious: Boolean,
181195
) {
182-
val clientBuiltArtifact = clientApk.elements.single()
183-
val serviceBuiltArtifact = serviceApk.elements.single()
184-
val clientApkName = clientBuiltArtifact.resolveName(clientPath)
185-
val clientApkSha256 = sha256(File(clientBuiltArtifact.outputFile))
186-
val serviceApkName = serviceBuiltArtifact.resolveName(servicePath)
187-
val serviceApkSha256 = sha256(File(serviceBuiltArtifact.outputFile))
188196
createOrFail(jsonClientOutputFile).writeText(
189197
buildMediaJson(
190198
configName = jsonClientOutputFile.asFile.get().name,

buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt

+35-14
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@
1717
package androidx.build.testConfiguration
1818

1919
import androidx.build.dependencyTracker.ProjectSubset
20-
import androidx.build.renameApkForTesting
2120
import com.android.build.api.variant.BuiltArtifactsLoader
2221
import java.io.File
2322
import org.gradle.api.DefaultTask
23+
import org.gradle.api.GradleException
2424
import org.gradle.api.file.DirectoryProperty
2525
import org.gradle.api.file.RegularFileProperty
2626
import org.gradle.api.provider.ListProperty
2727
import org.gradle.api.provider.Property
28-
import org.gradle.api.tasks.CacheableTask
2928
import org.gradle.api.tasks.Input
3029
import org.gradle.api.tasks.InputFiles
3130
import org.gradle.api.tasks.Internal
@@ -34,14 +33,15 @@ import org.gradle.api.tasks.OutputFile
3433
import org.gradle.api.tasks.PathSensitive
3534
import org.gradle.api.tasks.PathSensitivity
3635
import org.gradle.api.tasks.TaskAction
36+
import org.gradle.work.DisableCachingByDefault
3737

3838
/**
3939
* Writes a configuration file in
4040
* <a href=https://source.android.com/devices/tech/test_infra/tradefed/testing/through-suite/android-test-structure>AndroidTest.xml</a>
4141
* format that gets zipped alongside the APKs to be tested.
4242
* This config gets ingested by Tradefed.
4343
*/
44-
@CacheableTask
44+
@DisableCachingByDefault(because = "Doesn't benefit from caching")
4545
abstract class GenerateTestConfigurationTask : DefaultTask() {
4646

4747
@get:InputFiles
@@ -52,10 +52,6 @@ abstract class GenerateTestConfigurationTask : DefaultTask() {
5252
@get:Internal
5353
abstract val appLoader: Property<BuiltArtifactsLoader>
5454

55-
@get:Input
56-
@get:Optional
57-
abstract val appProjectPath: Property<String>
58-
5955
@get:InputFiles
6056
@get:PathSensitive(PathSensitivity.RELATIVE)
6157
abstract val testFolder: DirectoryProperty
@@ -96,6 +92,18 @@ abstract class GenerateTestConfigurationTask : DefaultTask() {
9692
@get:OutputFile
9793
abstract val constrainedOutputXml: RegularFileProperty
9894

95+
@get:OutputFile
96+
abstract val outputTestApk: RegularFileProperty
97+
98+
@get:OutputFile
99+
abstract val constrainedOutputTestApk: RegularFileProperty
100+
101+
@get:[OutputFile Optional]
102+
abstract val outputAppApk: RegularFileProperty
103+
104+
@get:[OutputFile Optional]
105+
abstract val constrainedOutputAppApk: RegularFileProperty
106+
99107
@TaskAction
100108
fun generateAndroidTestZip() {
101109
writeConfigFileContent(
@@ -125,9 +133,13 @@ abstract class GenerateTestConfigurationTask : DefaultTask() {
125133
?: throw RuntimeException("Cannot load required APK for task: $name")
126134
// We don't need to check hasBenchmarkPlugin because benchmarks shouldn't have test apps
127135
val appApkBuiltArtifact = appApk.elements.single()
128-
val appName = appApkBuiltArtifact.outputFile.substringAfterLast("/")
129-
.renameApkForTesting(appProjectPath.get())
130-
configBuilder.appApkName(appName)
136+
val destinationApk = if (isConstrained) {
137+
constrainedOutputAppApk.get().asFile
138+
} else {
139+
outputAppApk.get().asFile
140+
}
141+
File(appApkBuiltArtifact.outputFile).copyTo(destinationApk, overwrite = true)
142+
configBuilder.appApkName(destinationApk.name)
131143
.appApkSha256(sha256(File(appApkBuiltArtifact.outputFile)))
132144
}
133145
configBuilder.additionalApkKeys(additionalApkKeys.get())
@@ -181,16 +193,25 @@ abstract class GenerateTestConfigurationTask : DefaultTask() {
181193
val testApk = testLoader.get().load(testFolder.get())
182194
?: throw RuntimeException("Cannot load required APK for task: $name")
183195
val testApkBuiltArtifact = testApk.elements.single()
184-
val testName = testApkBuiltArtifact.outputFile
185-
.substringAfterLast("/")
186-
.renameApkForTesting(testProjectPath.get())
187-
configBuilder.testApkName(testName)
196+
val destinationApk = if (isConstrained) {
197+
constrainedOutputTestApk.get().asFile
198+
} else {
199+
outputTestApk.get().asFile
200+
}
201+
File(testApkBuiltArtifact.outputFile).copyTo(destinationApk, overwrite = true)
202+
configBuilder.testApkName(destinationApk.name)
188203
.applicationId(testApk.applicationId)
189204
.minSdk(minSdk.get().toString())
190205
.testRunner(testRunner.get())
191206
.testApkSha256(sha256(File(testApkBuiltArtifact.outputFile)))
192207
createOrFail(outputFile).writeText(configBuilder.buildXml())
193208
if (!isConstrained) {
209+
if (!outputJson.asFile.get().name.startsWith("_")) {
210+
// Prefixing json file names with _ allows us to collocate these files
211+
// inside of the androidTest.zip to make fetching them less expensive.
212+
throw GradleException("json output file names are expected to use _ prefix to, " +
213+
"currently set to ${outputJson.asFile.get().name}")
214+
}
194215
createOrFail(outputJson).writeText(configBuilder.buildJson())
195216
}
196217
}

0 commit comments

Comments
 (0)