1 /* 2 * 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.BuiltArtifactsLoader 20 import java.io.File 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.RegularFileProperty 26 import org.gradle.api.model.ObjectFactory 27 import org.gradle.api.provider.Property 28 import org.gradle.api.tasks.InputFiles 29 import org.gradle.api.tasks.Internal 30 import org.gradle.api.tasks.Optional 31 import org.gradle.api.tasks.OutputFile 32 import org.gradle.api.tasks.PathSensitive 33 import org.gradle.api.tasks.PathSensitivity 34 import org.gradle.api.tasks.SkipWhenEmpty 35 import org.gradle.api.tasks.TaskAction 36 import org.gradle.work.DisableCachingByDefault 37 38 /** Copy single APK (from AGP artifacts) needed for building androidTest.zip */ 39 @DisableCachingByDefault(because = "Only filesystem operations") 40 abstract class CopyApkFromArtifactsTask @Inject constructor(private val objects: ObjectFactory) : 41 DefaultTask() { 42 43 /** File existence check to determine whether to run this task. */ 44 @get:InputFiles 45 @get:SkipWhenEmpty 46 @get:PathSensitive(PathSensitivity.NONE) 47 abstract val androidTestSourceCode: ConfigurableFileCollection 48 49 @get:InputFiles 50 @get:Optional 51 @get:PathSensitive(PathSensitivity.NONE) 52 abstract val appFolder: DirectoryProperty 53 54 @get:InputFiles 55 @get:PathSensitive(PathSensitivity.NONE) 56 abstract val appFileCollection: ConfigurableFileCollection 57 58 @get:Internal abstract val appLoader: Property<BuiltArtifactsLoader> 59 60 @get:OutputFile abstract val outputAppApk: RegularFileProperty 61 62 @get:OutputFile abstract val outputAppApksModel: RegularFileProperty 63 64 @TaskAction createApksnull65 fun createApks() { 66 // Decides where to load the app apk from, depending on whether appFolder or 67 // appFileCollection has been set. 68 val appDir = 69 if (appFolder.isPresent && appFileCollection.files.isEmpty()) { 70 appFolder.get() 71 } else if (!appFolder.isPresent && appFileCollection.files.size == 1) { 72 objects.directoryProperty().also { it.set(appFileCollection.files.first()) }.get() 73 } else { 74 throw IllegalStateException( 75 """ 76 App apk not specified or both appFileCollection and appFolder specified. 77 """ 78 .trimIndent() 79 ) 80 } 81 82 val appApk = 83 appLoader.get().load(appDir) 84 ?: throw RuntimeException("Cannot load required APK for task: $name") 85 86 val appApkBuiltArtifact = appApk.elements.single() 87 val destinationApk = outputAppApk.get().asFile 88 File(appApkBuiltArtifact.outputFile).copyTo(destinationApk, overwrite = true) 89 90 val model = 91 singleFileAppApksModel(name = destinationApk.name, sha256 = sha256(destinationApk)) 92 outputAppApksModel.get().asFile.writeText(model.toJson()) 93 } 94 } 95