| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.build.api.apiTest |
| |
| import com.android.build.api.apiTest.VariantApiBaseTest.ScriptingLanguage |
| |
| /** |
| * repository of Gradle tasks and Android related artifacts like build files or manifest files that |
| * can be used to assemble tests. |
| */ |
| @Suppress("ClassNameDiffersFromFileName") |
| class TestingElements(val language: ScriptingLanguage) { |
| |
| fun addGitVersionTask(builder: VariantApiBaseTest.GivenBuilder) { |
| builder.addSource( |
| constructFilePath("GitVersion"), |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.tasks.OutputFile |
| import org.gradle.api.tasks.TaskAction |
| ${getGitVersionTask()} |
| """ |
| ) |
| } |
| |
| fun addManifestProducerTask(builder: VariantApiBaseTest.GivenBuilder) { |
| builder.addSource( |
| constructFilePath("ManifestProducerTask"), |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.tasks.InputFile |
| import org.gradle.api.tasks.OutputFile |
| import org.gradle.api.tasks.TaskAction |
| ${getManifestProducerTask()} |
| """ |
| |
| ) |
| } |
| |
| fun addManifestTransformerTask(builder: VariantApiBaseTest.GivenBuilder) { |
| builder.addSource( |
| constructFilePath("ManifestTransformerTask"), |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.tasks.InputFile |
| import org.gradle.api.tasks.OutputFile |
| import org.gradle.api.tasks.TaskAction |
| ${getManifestTransformerTask()} |
| """ |
| ) |
| } |
| |
| fun addManifestVerifierTask(builder: VariantApiBaseTest.GivenBuilder) { |
| builder.addSource( |
| constructFilePath("VerifyManifestTask"), |
| getManifestVerifierTask() |
| ) |
| } |
| |
| fun addCopyApksTask(builder: VariantApiBaseTest.GivenBuilder) { |
| builder.addSource( |
| constructFilePath("CopyApksTask"), |
| """ |
| import java.io.Serializable |
| import java.io.File |
| import javax.inject.Inject |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.file.Directory |
| import org.gradle.api.file.DirectoryProperty |
| import org.gradle.api.tasks.InputFiles |
| import org.gradle.api.tasks.Internal |
| import org.gradle.api.tasks.OutputDirectory |
| import org.gradle.api.tasks.TaskAction |
| import org.gradle.api.provider.Property |
| import org.gradle.workers.WorkParameters |
| import org.gradle.workers.WorkerExecutor |
| import org.gradle.workers.WorkAction |
| import com.android.build.api.artifact.ArtifactType |
| import com.android.build.api.artifact.ArtifactKind |
| import com.android.build.api.artifact.Artifact |
| import com.android.build.api.artifact.Artifact.Replaceable |
| import com.android.build.api.artifact.Artifact.ContainsMany |
| import com.android.build.api.artifact.ArtifactTransformationRequest |
| import com.android.build.api.variant.BuiltArtifact |
| |
| ${getCopyApksTask()} |
| """ |
| ) |
| } |
| |
| fun addCommonAndroidBuildLogic() = |
| """ |
| compileSdkVersion(29) |
| defaultConfig { |
| minSdkVersion(21) |
| targetSdkVersion(29) |
| }""" |
| |
| fun addManifest(builder: VariantApiBaseTest.GivenBuilder) { |
| builder.manifest = |
| // language=xml |
| """<?xml version="1.0" encoding="utf-8"?> |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| package="com.android.build.example.minimal"> |
| <application android:label="Minimal"> |
| <activity android:name="MainActivity"> |
| <intent-filter> |
| <action android:name="android.intent.action.MAIN" /> |
| <category android:name="android.intent.category.LAUNCHER" /> |
| </intent-filter> |
| </activity> |
| </application> |
| </manifest> |
| """.trimIndent() |
| } |
| |
| fun addMainActivity(builder: VariantApiBaseTest.GivenBuilder) = |
| when(language) { |
| ScriptingLanguage.Kotlin -> |
| builder.addSource( |
| constructFilePath("com/android/build/example/minimal/MainActivity"), |
| //language=kotlin |
| """ |
| package com.android.build.example.minimal |
| |
| import android.app.Activity |
| import android.os.Bundle |
| import android.widget.TextView |
| |
| class MainActivity : Activity() { |
| override fun onCreate(savedInstanceState: Bundle?) { |
| super.onCreate(savedInstanceState) |
| val label = TextView(this) |
| label.setText("Hello world!") |
| setContentView(label) |
| } |
| } |
| """) |
| ScriptingLanguage.Groovy -> |
| builder.addSource( |
| constructFilePath("com/android/build/example/minimal/MainActivity"), |
| // language=java |
| """ |
| package com.android.build.example.minimal; |
| |
| import android.app.Activity; |
| import android.os.Bundle; |
| import android.widget.TextView; |
| |
| public class MainActivity extends Activity |
| { |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| TextView label = new TextView(this); |
| label.setText("Hello world!"); |
| |
| setContentView(label); |
| } |
| } |
| """) |
| } |
| |
| private fun constructFilePath(relativePath: String) = |
| when(language) { |
| ScriptingLanguage.Kotlin -> "src/main/kotlin/$relativePath.kt" |
| ScriptingLanguage.Groovy -> "src/main/java/$relativePath.groovy" |
| } |
| |
| |
| |
| fun getGitVersionTask() = |
| when(language) { |
| ScriptingLanguage.Kotlin -> |
| // language=kotlin |
| """ |
| abstract class GitVersionTask: DefaultTask() { |
| |
| @get:OutputFile |
| abstract val gitVersionOutputFile: RegularFileProperty |
| |
| @ExperimentalStdlibApi |
| @TaskAction |
| fun taskAction() { |
| |
| // this would be the code to get the tip of tree version, |
| // val firstProcess = ProcessBuilder("git","rev-parse --short HEAD").start() |
| // val error = firstProcess.errorStream.readBytes().decodeToString() |
| // if (error.isNotBlank()) { |
| // System.err.println("Git error : ${'$'}error") |
| // } |
| // var gitVersion = firstProcess.inputStream.readBytes().decodeToString() |
| |
| // but here, we are just hardcoding : |
| gitVersionOutputFile.get().asFile.writeText("1234") |
| } |
| } |
| """ |
| ScriptingLanguage.Groovy -> |
| // language=groovy |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.tasks.OutputFile |
| import org.gradle.api.tasks.TaskAction |
| |
| abstract class GitVersionTask extends DefaultTask { |
| |
| @OutputFile |
| abstract RegularFileProperty getGitVersionOutputFile() |
| |
| @TaskAction |
| void taskAction() { |
| // this would be the code to get the tip of tree version, |
| // String gitVersion = "git rev-parse --short HEAD".execute().text.trim() |
| // if (gitVersion.isEmpty()) { |
| // gitVersion="12" |
| //} |
| getGitVersionOutputFile().get().asFile.write("1234") |
| } |
| } |
| """ |
| } |
| |
| fun getManifestProducerTask() = |
| when(language) { |
| ScriptingLanguage.Kotlin -> |
| // language=kotlin |
| """ |
| abstract class ManifestProducerTask: DefaultTask() { |
| @get:InputFile |
| abstract val gitInfoFile: RegularFileProperty |
| |
| @get:OutputFile |
| abstract val outputManifest: RegularFileProperty |
| |
| @TaskAction |
| fun taskAction() { |
| |
| val gitVersion = gitInfoFile.get().asFile.readText() |
| val manifest = ""${'"'}<?xml version="1.0" encoding="utf-8"?> |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| package="com.android.build.example.minimal" |
| android:versionName="${'$'}{gitVersion}" |
| android:versionCode="1" > |
| <application android:label="Minimal"> |
| <activity android:name="MainActivity"> |
| <intent-filter> |
| <action android:name="android.intent.action.MAIN" /> |
| <category android:name="android.intent.category.LAUNCHER" /> |
| </intent-filter> |
| </activity> |
| </application> |
| </manifest> |
| ""${'"'} |
| println("Writes to " + outputManifest.get().asFile.absolutePath) |
| outputManifest.get().asFile.writeText(manifest) |
| } |
| } |
| """ |
| ScriptingLanguage.Groovy -> |
| // language=groovy |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.tasks.InputFile |
| import org.gradle.api.tasks.OutputFile |
| import org.gradle.api.tasks.TaskAction |
| |
| abstract class ManifestProducerTask extends DefaultTask { |
| @InputFile |
| abstract RegularFileProperty getGitInfoFile() |
| |
| @OutputFile |
| abstract RegularFileProperty getOutputManifest() |
| |
| @TaskAction |
| void taskAction() { |
| String manifest = '''<?xml version=\"1.0\" encoding=\"utf-8\"?> |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| package="com.android.build.example.minimal" |
| android:versionName="${'$'}{new String(getGitInfoFile().get().asFile.readBytes())}" |
| android:versionCode="1" > |
| |
| <application android:label="Minimal"> |
| <activity android:name="MainActivity"> |
| <intent-filter> |
| <action android:name="android.intent.action.MAIN" /> |
| <category android:name="android.intent.category.LAUNCHER" /> |
| </intent-filter> |
| </activity> |
| </application> |
| </manifest> |
| ''' |
| println("Writes to " + getOutputManifest().get().getAsFile().getAbsolutePath()) |
| getOutputManifest().get().getAsFile().write(manifest) |
| } |
| } |
| """ |
| } |
| |
| fun getManifestTransformerTask() = |
| when(language) { |
| ScriptingLanguage.Kotlin -> |
| // language=kotlin |
| """ |
| abstract class ManifestTransformerTask: DefaultTask() { |
| |
| @get:InputFile |
| abstract val gitInfoFile: RegularFileProperty |
| |
| @get:InputFile |
| abstract val mergedManifest: RegularFileProperty |
| |
| @get:OutputFile |
| abstract val updatedManifest: RegularFileProperty |
| |
| @TaskAction |
| fun taskAction() { |
| |
| val gitVersion = gitInfoFile.get().asFile.readText() |
| var manifest = mergedManifest.asFile.get().readText() |
| manifest = manifest.replace("android:versionCode=\"1\"", "android:versionCode=\"${'$'}{gitVersion}\"") |
| println("Writes to " + updatedManifest.get().asFile.getAbsolutePath()) |
| updatedManifest.get().asFile.writeText(manifest) |
| } |
| } |
| """ |
| ScriptingLanguage.Groovy -> |
| // language=groovy |
| """ |
| import org.gradle.api.file.RegularFileProperty |
| |
| abstract class ManifestTransformerTask extends DefaultTask { |
| |
| @InputFile |
| abstract RegularFileProperty getGitInfoFile() |
| |
| @InputFile |
| abstract RegularFileProperty getMergedManifest() |
| |
| @OutputFile |
| abstract RegularFileProperty getUpdatedManifest() |
| |
| @TaskAction |
| void taskAction() { |
| String gitVersion = new String(getGitInfoFile().get().asFile.readBytes()) |
| String manifest = new String(getMergedManifest().get().asFile.readBytes()) |
| manifest = manifest.replace("android:versionCode=\"1\"", |
| "android:versionCode=\""+ gitVersion +"\"") |
| getUpdatedManifest().get().asFile.write(manifest) |
| } |
| } |
| """ |
| } |
| |
| private fun getManifestVerifierTask() = |
| when(language) { |
| ScriptingLanguage.Kotlin -> |
| // language=kotlin |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.DirectoryProperty |
| import org.gradle.api.tasks.InputFiles |
| import org.gradle.api.tasks.TaskAction |
| import java.io.ByteArrayOutputStream |
| import java.io.PrintStream |
| |
| import com.android.build.api.variant.BuiltArtifactsLoader |
| import org.gradle.api.provider.Property |
| import org.gradle.api.tasks.Internal |
| import java.io.File |
| |
| abstract class VerifyManifestTask: DefaultTask() { |
| |
| @get:InputFiles |
| abstract val apkFolder: DirectoryProperty |
| |
| @get:Internal |
| abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader> |
| |
| @TaskAction |
| fun taskAction() { |
| val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get()) |
| ?: throw RuntimeException("Cannot load APKs") |
| if (builtArtifacts.elements.size != 1) |
| throw RuntimeException("Expected one APK !") |
| val apk = File(builtArtifacts.elements.single().outputFile).toPath() |
| println("Insert code to verify manifest file in ${'$'}{apk}") |
| println("SUCCESS") |
| } |
| } |
| """ |
| ScriptingLanguage.Groovy -> |
| """ |
| |
| """.trimIndent() |
| } |
| |
| fun getDisplayApksTask()= |
| when(language) { |
| ScriptingLanguage.Kotlin -> |
| // language=kotlin |
| """ |
| abstract class DisplayApksTask: DefaultTask() { |
| |
| @get:InputFiles |
| abstract val apkFolder: DirectoryProperty |
| |
| @get:Internal |
| abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader> |
| |
| @TaskAction |
| fun taskAction() { |
| |
| val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get()) |
| ?: throw RuntimeException("Cannot load APKs") |
| builtArtifacts.elements.forEach { |
| println("Got an APK at ${ '$' }{it.outputFile}") |
| } |
| } |
| } |
| """ |
| ScriptingLanguage.Groovy -> |
| // language=groovy |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.Directory |
| import org.gradle.api.file.DirectoryProperty |
| import org.gradle.api.tasks.InputFiles |
| import org.gradle.api.tasks.TaskAction |
| import java.io.ByteArrayOutputStream |
| import java.io.PrintStream |
| |
| import com.android.build.api.variant.BuiltArtifactsLoader |
| import com.android.build.api.artifact.ArtifactType |
| import com.android.build.api.variant.BuiltArtifacts |
| import org.gradle.api.provider.Property |
| import org.gradle.api.tasks.Internal |
| |
| abstract class DisplayApksTask extends DefaultTask { |
| |
| @InputFiles |
| abstract DirectoryProperty getApkFolder() |
| |
| @Internal |
| abstract Property<BuiltArtifactsLoader> getBuiltArtifactsLoader() |
| |
| @TaskAction |
| void taskAction() { |
| |
| BuiltArtifacts artifacts = getBuiltArtifactsLoader().get().load(getApkFolder().get()) |
| if (artifacts == null) { |
| throw new RuntimeException("Cannot load APKs") |
| } |
| artifacts.elements.forEach { |
| println("Got an APK at ${'$'}{it.outputFile}") |
| } |
| } |
| } |
| """ |
| } |
| |
| fun getCopyApksTask()= |
| when(language) { |
| ScriptingLanguage.Groovy -> |
| // language=groovy |
| """ |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.Directory |
| import org.gradle.api.file.DirectoryProperty |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.provider.Property |
| import org.gradle.api.tasks.InputFiles |
| import org.gradle.api.tasks.TaskAction |
| import org.gradle.workers.WorkerExecutor |
| import com.android.build.api.variant.BuiltArtifact |
| import com.android.build.api.artifact.ArtifactTransformationRequest |
| import org.gradle.api.tasks.Internal |
| |
| import java.nio.file.Files |
| |
| interface WorkItemParameters extends WorkParameters, Serializable { |
| RegularFileProperty getInputApkFile() |
| RegularFileProperty getOutputApkFile() |
| } |
| |
| abstract class WorkItem implements WorkAction<WorkItemParameters> { |
| |
| WorkItemParameters workItemParameters |
| |
| @Inject |
| WorkItem(WorkItemParameters parameters) { |
| this.workItemParameters = parameters |
| } |
| |
| void execute() { |
| Files.copy( |
| workItemParameters.getInputApkFile().getAsFile().get().toPath(), |
| workItemParameters.getOutputApkFile().get().getAsFile().toPath()) |
| } |
| } |
| |
| abstract class CopyApksTask extends DefaultTask { |
| |
| WorkerExecutor workers |
| |
| @Inject |
| CopyApksTask(WorkerExecutor workerExecutor) { |
| this.workers = workerExecutor |
| } |
| |
| @InputFiles |
| abstract DirectoryProperty getApkFolder() |
| |
| @OutputDirectory |
| abstract DirectoryProperty getOutFolder() |
| |
| @Internal |
| abstract Property<ArtifactTransformationRequest<CopyApksTask>> getTransformationRequest() |
| |
| @TaskAction |
| void taskAction() { |
| |
| transformationRequest.get().submit( |
| this, |
| workers.noIsolation(), |
| WorkItem, |
| WorkItemParameters, { |
| BuiltArtifact builtArtifact, |
| Directory outputLocation, |
| WorkItemParameters param -> |
| File inputFile = new File(builtArtifact.outputFile) |
| param.getInputApkFile().set(inputFile) |
| param.getOutputApkFile().set(new File(outputLocation.asFile, inputFile.name)) |
| param.getOutputApkFile().get().getAsFile() |
| } |
| ) |
| } |
| } |
| """ |
| ScriptingLanguage.Kotlin -> |
| // language=kotlin |
| """ |
| interface WorkItemParameters: WorkParameters, Serializable { |
| val inputApkFile: RegularFileProperty |
| val outputApkFile: RegularFileProperty |
| } |
| |
| abstract class WorkItem @Inject constructor(private val workItemParameters: WorkItemParameters) |
| : WorkAction<WorkItemParameters> { |
| override fun execute() { |
| workItemParameters.inputApkFile.asFile.get().copyTo( |
| workItemParameters.outputApkFile.get().asFile) |
| } |
| } |
| abstract class CopyApksTask @Inject constructor(private val workers: WorkerExecutor): DefaultTask() { |
| |
| @get:InputFiles |
| abstract val apkFolder: DirectoryProperty |
| |
| @get:OutputDirectory |
| abstract val outFolder: DirectoryProperty |
| |
| @get:Internal |
| abstract val transformationRequest: Property<ArtifactTransformationRequest<CopyApksTask>> |
| |
| @TaskAction |
| fun taskAction() { |
| |
| transformationRequest.get().submit( |
| this, |
| workers.noIsolation(), |
| WorkItem::class.java, |
| WorkItemParameters::class.java) { |
| builtArtifact: BuiltArtifact, |
| outputLocation: Directory, |
| param: WorkItemParameters -> |
| val inputFile = File(builtArtifact.outputFile) |
| param.inputApkFile.set(inputFile) |
| param.outputApkFile.set(File(outputLocation.asFile, inputFile.name)) |
| param.outputApkFile.get().asFile |
| } |
| } |
| } |
| """ |
| } |
| } |