1 /* <lambda>null2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.build.testConfiguration 18 19 import com.android.build.api.variant.ApkInstallGroup 20 import com.android.build.api.variant.ApkOutput 21 import javax.inject.Inject 22 import org.gradle.api.DefaultTask 23 import org.gradle.api.file.ConfigurableFileCollection 24 import org.gradle.api.file.DirectoryProperty 25 import org.gradle.api.file.FileSystemOperations 26 import org.gradle.api.file.RegularFileProperty 27 import org.gradle.api.provider.Property 28 import org.gradle.api.tasks.Input 29 import org.gradle.api.tasks.InputFile 30 import org.gradle.api.tasks.InputFiles 31 import org.gradle.api.tasks.Internal 32 import org.gradle.api.tasks.OutputDirectory 33 import org.gradle.api.tasks.OutputFile 34 import org.gradle.api.tasks.PathSensitive 35 import org.gradle.api.tasks.PathSensitivity 36 import org.gradle.api.tasks.SkipWhenEmpty 37 import org.gradle.api.tasks.TaskAction 38 import org.gradle.work.DisableCachingByDefault 39 40 /** Copy APKs (from ApkOutputProviders) needed for building androidTest.zip */ 41 @Suppress("UnstableApiUsage") // Working with ApkOutputProviders b/397701480 42 @DisableCachingByDefault(because = "Only filesystem operations") 43 abstract class CopyApksFromOutputProviderTask 44 @Inject 45 constructor(private val fileSystemOperations: FileSystemOperations) : DefaultTask() { 46 47 /** File existence check to determine whether to run this task. */ 48 @get:InputFiles 49 @get:SkipWhenEmpty 50 @get:PathSensitive(PathSensitivity.NONE) 51 abstract val androidTestSourceCode: ConfigurableFileCollection 52 53 /** ApkOutputProviders output, contains all App APKs. */ 54 @get:Internal abstract val apkOutput: Property<ApkOutput> 55 56 /** Some Variants includes test APK into ApkOutput, excluding it by using this parameter. */ 57 @get:InputFile 58 @get:PathSensitive(PathSensitivity.NONE) 59 abstract val excludeTestApk: RegularFileProperty 60 61 /** 62 * Filename prefix for all output apks. Required for producing unique filenames over all 63 * projects. 64 * 65 * Resulting filename: <outputFilenamesPrefix>-<groupIndex>-<fileIndex>-<originalFileName> 66 */ 67 @get:Input abstract val outputFilenamesPrefix: Property<String> 68 69 @get:OutputDirectory abstract val outputDirectory: DirectoryProperty 70 71 @get:OutputFile abstract val outputAppApksModel: RegularFileProperty 72 73 @TaskAction 74 fun createApks() { 75 val outputDir = outputDirectory.get() 76 77 // Cleanup old files - to remove stale APKs 78 fileSystemOperations.delete { it.delete(outputDir) } 79 80 val fileNamePrefix = outputFilenamesPrefix.get() 81 val testApkSha256 = sha256(excludeTestApk.get().asFile) 82 83 val resultApkGroups = 84 apkOutput.get().apkInstallGroups.mapIndexedNotNull { groupIndex, installGroup -> 85 processApkInstallGroup( 86 installGroup, 87 testApkSha256, 88 fileNamePrefix = "$fileNamePrefix-$groupIndex" 89 ) 90 } 91 92 val model = AppApksModel(resultApkGroups) 93 outputAppApksModel.get().asFile.writeText(model.toJson()) 94 } 95 96 private fun processApkInstallGroup( 97 installGroup: ApkInstallGroup, 98 excludeSha256: String, 99 fileNamePrefix: String 100 ): ApkFileGroup? { 101 val outputDir = outputDirectory.get() 102 val resultApkFiles = 103 installGroup.apks.mapIndexedNotNull { fileIndex, file -> 104 val inputFile = file.asFile 105 val fileSha256 = sha256(inputFile) 106 if (fileSha256 == excludeSha256) { 107 // Some Variants includes test APK into ApkOutput, filter it. 108 return@mapIndexedNotNull null 109 } 110 111 val outputFileName = "$fileNamePrefix-$fileIndex-${inputFile.name}" 112 val outputFile = outputDir.file(outputFileName).asFile 113 114 inputFile.copyTo(outputFile, overwrite = true) 115 116 return@mapIndexedNotNull ApkFile(name = outputFileName, sha256 = fileSha256) 117 } 118 return if (resultApkFiles.isEmpty()) { 119 null 120 } else { 121 ApkFileGroup(resultApkFiles) 122 } 123 } 124 } 125