| // Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import java.nio.file.Paths |
| import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
| |
| |
| plugins { |
| `kotlin-dsl` |
| id("dependencies-plugin") |
| } |
| |
| java { |
| sourceCompatibility = JvmCompatibility.sourceCompatibility |
| targetCompatibility = JvmCompatibility.targetCompatibility |
| toolchain { |
| languageVersion = JavaLanguageVersion.of(JvmCompatibility.release) |
| } |
| } |
| |
| dependencies { } |
| |
| val keepAnnoCompileTask = projectTask("keepanno", "compileJava") |
| val keepAnnoSourcesTask = projectTask("keepanno", "sourcesJar") |
| val mainDepsJarTask = projectTask("main", "depsJar") |
| val swissArmyKnifeTask = projectTask("main", "swissArmyKnife") |
| val r8WithRelocatedDepsTask = projectTask("main", "r8WithRelocatedDeps") |
| val mainSourcesTask = projectTask("main", "sourcesJar") |
| val resourceShrinkerSourcesTask = projectTask("resourceshrinker", "sourcesJar") |
| val javaTestBaseJarTask = projectTask("testbase", "testJar") |
| val javaTestBaseDepsJar = projectTask("testbase", "depsJar") |
| val java8TestJarTask = projectTask("tests_java_8", "testJar") |
| val java11TestJarTask = projectTask("tests_java_11", "testJar") |
| val java17TestJarTask = projectTask("tests_java_17", "testJar") |
| val java21TestJarTask = projectTask("tests_java_21", "testJar") |
| val bootstrapTestsDepsJarTask = projectTask("tests_bootstrap", "depsJar") |
| val bootstrapTestJarTask = projectTask("tests_bootstrap", "testJar") |
| val testsJava8SourceSetDependenciesTask = projectTask("tests_java_8", "sourceSetDependencyTask") |
| |
| tasks { |
| withType<Exec> { |
| doFirst { |
| println("Executing command: ${commandLine.joinToString(" ")}") |
| } |
| } |
| |
| withType<KotlinCompile> { |
| kotlinOptions { |
| jvmTarget = "17" |
| } |
| } |
| |
| "clean" { |
| dependsOn(gradle.includedBuild("tests_bootstrap").task(":clean")) |
| dependsOn(gradle.includedBuild("tests_java_8").task(":clean")) |
| dependsOn(gradle.includedBuild("tests_java_9").task(":clean")) |
| dependsOn(gradle.includedBuild("tests_java_10").task(":clean")) |
| dependsOn(gradle.includedBuild("tests_java_11").task(":clean")) |
| dependsOn(gradle.includedBuild("tests_java_17").task(":clean")) |
| dependsOn(gradle.includedBuild("tests_java_21").task(":clean")) |
| dependsOn(gradle.includedBuild("tests_java_22").task(":clean")) |
| } |
| |
| val packageTests by registering(Jar::class) { |
| dependsOn(java8TestJarTask) |
| dependsOn(java11TestJarTask) |
| dependsOn(java17TestJarTask) |
| dependsOn(java21TestJarTask) |
| dependsOn(bootstrapTestJarTask) |
| from(java8TestJarTask.outputs.files.map(::zipTree)) |
| from(java11TestJarTask.outputs.files.map(::zipTree)) |
| from(java17TestJarTask.outputs.files.map(::zipTree)) |
| from(java21TestJarTask.outputs.files.map(::zipTree)) |
| from(bootstrapTestJarTask.outputs.files.map(::zipTree)) |
| exclude("META-INF/*.kotlin_module", "**/*.kotlin_metadata") |
| destinationDirectory.set(getRoot().resolveAll("build", "libs")) |
| archiveFileName.set("r8tests.jar") |
| } |
| |
| val packageTestDeps by registering(Jar::class) { |
| dependsOn(bootstrapTestsDepsJarTask, javaTestBaseDepsJar) |
| from(bootstrapTestsDepsJarTask.outputs.getFiles().map(::zipTree)) |
| from(javaTestBaseDepsJar.outputs.getFiles().map(::zipTree)) |
| exclude("META-INF/*.kotlin_module", "**/*.kotlin_metadata") |
| duplicatesStrategy = DuplicatesStrategy.EXCLUDE |
| destinationDirectory.set(getRoot().resolveAll("build", "libs")) |
| archiveFileName.set("test_deps_all.jar") |
| } |
| |
| val packageTestBase by registering(Jar::class) { |
| dependsOn(javaTestBaseJarTask) |
| from(javaTestBaseJarTask.outputs.files.map(::zipTree)) |
| exclude("META-INF/*.kotlin_module", "**/*.kotlin_metadata") |
| destinationDirectory.set(getRoot().resolveAll("build", "libs")) |
| archiveFileName.set("r8test_base.jar") |
| } |
| |
| val packageTestBaseExcludeKeep by registering(Jar::class) { |
| dependsOn(packageTestBase) |
| from(zipTree(packageTestBase.getSingleOutputFile())) |
| // TODO(b/328353718): we have com.android.tools.r8.Keep in both test_base and main |
| exclude("com/android/tools/r8/Keep.class") |
| archiveFileName.set("r8test_base_no_keep.jar") |
| } |
| |
| |
| fun Exec.executeRelocator(jarProvider: TaskProvider<*>, artifactName: String) { |
| dependsOn(r8WithRelocatedDepsTask, jarProvider) |
| val outputJar = file(Paths.get("build", "libs", artifactName)) |
| outputs.file(outputJar) |
| val r8WithRelocatedDepsJar = r8WithRelocatedDepsTask.getSingleOutputFile() |
| val testJar = jarProvider.getSingleOutputFile() |
| inputs.files(r8WithRelocatedDepsJar, testJar) |
| commandLine = baseCompilerCommandLine( |
| r8WithRelocatedDepsJar, |
| "relocator", |
| listOf("--input", |
| "$testJar", |
| "--output", |
| "$outputJar", |
| "--map", |
| "kotlin.metadata.**->com.android.tools.r8.jetbrains.kotlin.metadata")) |
| } |
| |
| // When testing R8 lib with relocated deps we must relocate kotlin.metadata in the tests, since |
| // types from kotlin.metadata are used on the R8 main/R8 test boundary. |
| // |
| // This is not needed when testing R8 lib excluding deps since we simply include the deps on the |
| // classpath at runtime. |
| val relocateTestsForR8LibWithRelocatedDeps by registering(Exec::class) { |
| executeRelocator(packageTests, "r8tests-relocated.jar") |
| } |
| |
| val relocateTestBaseForR8LibWithRelocatedDeps by registering(Exec::class) { |
| executeRelocator(packageTestBase, "r8testbase-relocated.jar") |
| } |
| |
| fun Exec.generateKeepRulesForR8Lib( |
| targetJarProvider: Task, testJarProviders: List<TaskProvider<*>>, artifactName: String) { |
| dependsOn( |
| mainDepsJarTask, |
| packageTestDeps, |
| r8WithRelocatedDepsTask, |
| targetJarProvider) |
| testJarProviders.forEach(::dependsOn) |
| val mainDepsJar = mainDepsJarTask.getSingleOutputFile() |
| val r8WithRelocatedDepsJar = r8WithRelocatedDepsTask.getSingleOutputFile() |
| val targetJar = targetJarProvider.getSingleOutputFile() |
| val testDepsJar = packageTestDeps.getSingleOutputFile() |
| inputs.files(mainDepsJar, r8WithRelocatedDepsJar, targetJar, testDepsJar) |
| inputs.files(testJarProviders.map{it.getSingleOutputFile()}) |
| val output = file(Paths.get("build", "libs", artifactName)) |
| outputs.file(output) |
| val argList = mutableListOf("--keep-rules", |
| "--allowobfuscation", |
| "--lib", |
| "${getJavaHome(Jdk.JDK_21)}", |
| "--lib", |
| "$mainDepsJar", |
| "--lib", |
| "$testDepsJar", |
| "--target", |
| "$targetJar", |
| "--output", |
| "$output") |
| testJarProviders.forEach{ |
| argList.add("--source") |
| argList.add("${it.getSingleOutputFile()}") |
| } |
| commandLine = baseCompilerCommandLine( |
| listOf("-Dcom.android.tools.r8.tracereferences.obfuscateAllEnums"), |
| r8WithRelocatedDepsJar, |
| "tracereferences", |
| argList |
| ) |
| } |
| |
| val generateKeepRulesForR8LibWithRelocatedDeps by registering(Exec::class) { |
| generateKeepRulesForR8Lib( |
| r8WithRelocatedDepsTask, |
| listOf(relocateTestsForR8LibWithRelocatedDeps, relocateTestBaseForR8LibWithRelocatedDeps), |
| "generated-keep-rules-r8lib.txt") |
| } |
| |
| val generateKeepRulesForR8LibNoDeps by registering(Exec::class) { |
| generateKeepRulesForR8Lib( |
| swissArmyKnifeTask, |
| listOf(packageTests, packageTestBase), |
| "generated-keep-rules-r8lib-exclude-deps.txt") |
| } |
| |
| fun Exec.assembleR8Lib( |
| inputJarProvider: Task, |
| generatedKeepRulesProvider: TaskProvider<Exec>, |
| classpath: List<File>, |
| artifactName: String) { |
| dependsOn(generatedKeepRulesProvider, inputJarProvider, r8WithRelocatedDepsTask) |
| val inputJar = inputJarProvider.getSingleOutputFile() |
| val r8WithRelocatedDepsJar = r8WithRelocatedDepsTask.getSingleOutputFile() |
| val keepRuleFiles = listOf( |
| getRoot().resolveAll("src", "main", "keep.txt"), |
| getRoot().resolveAll("src", "main", "discard.txt"), |
| generatedKeepRulesProvider.getSingleOutputFile(), |
| // TODO(b/294351878): Remove once enum issue is fixed |
| getRoot().resolveAll("src", "main", "keep_r8resourceshrinker.txt")) |
| inputs.files(listOf(r8WithRelocatedDepsJar, inputJar).union(keepRuleFiles).union(classpath)) |
| val outputJar = getRoot().resolveAll("build", "libs", artifactName) |
| outputs.file(outputJar) |
| commandLine = createR8LibCommandLine( |
| r8WithRelocatedDepsJar, |
| inputJar, |
| outputJar, |
| keepRuleFiles, |
| excludingDepsVariant = classpath.isNotEmpty(), |
| debugVariant = false, |
| classpath = classpath) |
| } |
| |
| val assembleR8LibNoDeps by registering(Exec::class) { |
| dependsOn(mainDepsJarTask) |
| val mainDepsJar = mainDepsJarTask.getSingleOutputFile() |
| assembleR8Lib( |
| swissArmyKnifeTask, |
| generateKeepRulesForR8LibNoDeps, |
| listOf(mainDepsJar), |
| "r8lib-exclude-deps.jar") |
| } |
| |
| val assembleR8LibWithRelocatedDeps by registering(Exec::class) { |
| assembleR8Lib( |
| r8WithRelocatedDepsTask, |
| generateKeepRulesForR8LibWithRelocatedDeps, |
| listOf(), |
| "r8lib.jar") |
| } |
| |
| fun Task.generateTestKeepRulesForR8Lib( |
| r8LibJarProvider: TaskProvider<Exec>, artifactName: String) { |
| dependsOn(r8LibJarProvider) |
| val r8LibJar = r8LibJarProvider.getSingleOutputFile() |
| inputs.files(r8LibJar) |
| val output = rootProject.buildDir.resolveAll("libs", artifactName) |
| outputs.files(output) |
| doLast { |
| // TODO(b/299065371): We should be able to take in the partition map output. |
| output.writeText( |
| """-keep class ** { *; } |
| -dontshrink |
| -dontoptimize |
| -keepattributes * |
| -applymapping $r8LibJar.map |
| """) |
| } |
| } |
| |
| val generateTestKeepRulesR8LibWithRelocatedDeps by registering { |
| generateTestKeepRulesForR8Lib(assembleR8LibWithRelocatedDeps, "r8lib-tests-keep.txt") |
| } |
| |
| val generateTestKeepRulesR8LibNoDeps by registering { |
| generateTestKeepRulesForR8Lib(assembleR8LibNoDeps, "r8lib-exclude-deps-tests-keep.txt") |
| } |
| |
| fun Exec.rewriteTestsForR8Lib( |
| keepRulesFileProvider: TaskProvider<Task>, |
| r8JarProvider: Task, |
| testJarProvider: TaskProvider<*>, |
| artifactName: String, |
| addTestBaseClasspath: Boolean) { |
| dependsOn( |
| keepRulesFileProvider, |
| packageTestDeps, |
| relocateTestsForR8LibWithRelocatedDeps, |
| r8JarProvider, |
| r8WithRelocatedDepsTask, |
| testJarProvider, |
| packageTestBaseExcludeKeep) |
| val keepRulesFile = keepRulesFileProvider.getSingleOutputFile() |
| val r8Jar = r8JarProvider.getSingleOutputFile() |
| val r8WithRelocatedDepsJar = r8WithRelocatedDepsTask.getSingleOutputFile() |
| val testBaseJar = packageTestBaseExcludeKeep.getSingleOutputFile() |
| val testDepsJar = packageTestDeps.getSingleOutputFile() |
| val testJar = testJarProvider.getSingleOutputFile() |
| inputs.files(keepRulesFile, r8Jar, r8WithRelocatedDepsJar, testDepsJar, testJar) |
| val outputJar = getRoot().resolveAll("build", "libs", artifactName) |
| outputs.file(outputJar) |
| val args = mutableListOf( |
| "--classfile", |
| "--debug", |
| "--lib", |
| "${getJavaHome(Jdk.JDK_21)}", |
| "--classpath", |
| "$r8Jar", |
| "--classpath", |
| "$testDepsJar", |
| "--output", |
| "$outputJar", |
| "--pg-conf", |
| "$keepRulesFile", |
| "$testJar") |
| if (addTestBaseClasspath) { |
| args.add("--classpath") |
| args.add("$testBaseJar") |
| } |
| commandLine = baseCompilerCommandLine( |
| listOf("-Dcom.android.tools.r8.tracereferences.obfuscateAllEnums"), |
| r8WithRelocatedDepsJar, |
| "r8", |
| args) |
| } |
| |
| val rewriteTestsForR8LibWithRelocatedDeps by registering(Exec::class) { |
| rewriteTestsForR8Lib( |
| generateTestKeepRulesR8LibWithRelocatedDeps, |
| r8WithRelocatedDepsTask, |
| relocateTestsForR8LibWithRelocatedDeps, |
| "r8libtestdeps-cf.jar", |
| true) |
| } |
| |
| val rewriteTestBaseForR8LibWithRelocatedDeps by registering(Exec::class) { |
| rewriteTestsForR8Lib( |
| generateTestKeepRulesR8LibWithRelocatedDeps, |
| r8WithRelocatedDepsTask, |
| relocateTestBaseForR8LibWithRelocatedDeps, |
| "r8libtestbase-cf.jar", |
| false) |
| } |
| |
| val rewriteTestsForR8LibNoDeps by registering(Exec::class) { |
| rewriteTestsForR8Lib( |
| generateTestKeepRulesR8LibNoDeps, |
| swissArmyKnifeTask, |
| packageTests, |
| "r8lib-exclude-deps-testdeps-cf.jar", |
| true) |
| } |
| |
| val cleanUnzipTests by registering(Delete::class) { |
| dependsOn(packageTests) |
| val outputDir = file("${buildDir}/unpacked/test") |
| setDelete(outputDir) |
| } |
| |
| val unzipTests by registering(Copy::class) { |
| dependsOn(cleanUnzipTests, packageTests) |
| val outputDir = file("${buildDir}/unpacked/test") |
| from(zipTree(packageTests.getSingleOutputFile())) |
| into(outputDir) |
| } |
| |
| val unzipTestBase by registering(Copy::class) { |
| dependsOn(cleanUnzipTests, packageTestBase) |
| val outputDir = file("${buildDir}/unpacked/testbase") |
| from(zipTree(packageTestBase.getSingleOutputFile())) |
| into(outputDir) |
| } |
| |
| fun Copy.unzipRewrittenTestsForR8Lib( |
| rewrittenTestJarProvider: TaskProvider<Exec>, outDirName: String) { |
| dependsOn(rewrittenTestJarProvider) |
| val outputDir = file("$buildDir/unpacked/$outDirName") |
| val rewrittenTestJar = rewrittenTestJarProvider.getSingleOutputFile() |
| from(zipTree(rewrittenTestJar)) |
| into(outputDir) |
| } |
| |
| val cleanUnzipRewrittenTestsForR8LibWithRelocatedDeps by registering(Delete::class) { |
| val outputDir = file("${buildDir}/unpacked/rewrittentests-r8lib") |
| setDelete(outputDir) |
| } |
| |
| val unzipRewrittenTestsForR8LibWithRelocatedDeps by registering(Copy::class) { |
| dependsOn(cleanUnzipRewrittenTestsForR8LibWithRelocatedDeps) |
| unzipRewrittenTestsForR8Lib(rewriteTestsForR8LibWithRelocatedDeps, "rewrittentests-r8lib") |
| } |
| |
| val cleanUnzipRewrittenTestsForR8LibNoDeps by registering(Delete::class) { |
| val outputDir = file("${buildDir}/unpacked/rewrittentests-r8lib-exclude-deps") |
| setDelete(outputDir) |
| } |
| |
| val unzipRewrittenTestsForR8LibNoDeps by registering(Copy::class) { |
| dependsOn(cleanUnzipRewrittenTestsForR8LibNoDeps) |
| unzipRewrittenTestsForR8Lib( |
| rewriteTestsForR8LibNoDeps, "rewrittentests-r8lib-exclude-deps") |
| } |
| |
| fun Test.testR8Lib(r8Lib: TaskProvider<Exec>, unzipRewrittenTests: TaskProvider<Copy>) { |
| println("NOTE: Number of processors " + Runtime.getRuntime().availableProcessors()) |
| println("NOTE: Max parallel forks " + maxParallelForks) |
| dependsOn( |
| packageTestDeps, |
| r8Lib, |
| r8WithRelocatedDepsTask, |
| assembleR8LibNoDeps, |
| testsJava8SourceSetDependenciesTask, |
| rewriteTestBaseForR8LibWithRelocatedDeps, |
| unzipRewrittenTests, |
| unzipTests, |
| unzipTestBase, |
| gradle.includedBuild("shared").task(":downloadDeps")) |
| if (!project.hasProperty("no_internal")) { |
| dependsOn(gradle.includedBuild("shared").task(":downloadDepsInternal")) |
| } |
| val r8LibJar = r8Lib.getSingleOutputFile() |
| val r8LibMappingFile = file(r8LibJar.toString() + ".map") |
| val r8WithRelocatedDepsJar = r8WithRelocatedDepsTask.getSingleOutputFile() |
| configure(isR8Lib = true, r8Jar = r8WithRelocatedDepsJar, r8LibMappingFile = r8LibMappingFile) |
| |
| // R8lib should be used instead of the main output and all the tests in r8 should be mapped and |
| // exists in r8LibTestPath. |
| classpath = files( |
| packageTestDeps.get().getOutputs().getFiles(), |
| r8LibJar, |
| unzipRewrittenTests.get().getOutputs().getFiles(), |
| rewriteTestBaseForR8LibWithRelocatedDeps.getSingleOutputFile()) |
| testClassesDirs = unzipRewrittenTests.get().getOutputs().getFiles() |
| systemProperty("TEST_DATA_LOCATION", unzipTests.getSingleOutputFile()) |
| systemProperty("TESTBASE_DATA_LOCATION", unzipTestBase.getSingleOutputFile()) |
| |
| systemProperty( |
| "BUILD_PROP_KEEPANNO_RUNTIME_PATH", |
| keepAnnoCompileTask.getOutputs().getFiles().getAsPath().split(File.pathSeparator)[0]) |
| systemProperty("EXAMPLES_JAVA_11_JAVAC_BUILD_DIR", |
| getRoot().resolveAll("build", "test", "examplesJava11", "classes")) |
| systemProperty("BUILD_PROP_R8_RUNTIME_PATH", r8LibJar) |
| systemProperty("R8_DEPS", mainDepsJarTask.getSingleOutputFile()) |
| systemProperty("com.android.tools.r8.artprofilerewritingcompletenesscheck", "true") |
| systemProperty("R8_WITH_RELOCATED_DEPS", r8WithRelocatedDepsTask.outputs.files.singleFile) |
| |
| javaLauncher = getJavaLauncher(Jdk.JDK_21) |
| |
| reports.junitXml.outputLocation.set(getRoot().resolveAll("build", "test-results", "test")) |
| reports.html.outputLocation.set(getRoot().resolveAll("build", "reports", "tests", "test")) |
| } |
| |
| val testR8LibWithRelocatedDeps by registering(Test::class) { |
| testR8Lib(assembleR8LibWithRelocatedDeps, unzipRewrittenTestsForR8LibWithRelocatedDeps) |
| } |
| |
| val testR8LibNoDeps by registering(Test::class) { |
| testR8Lib(assembleR8LibNoDeps, unzipRewrittenTestsForR8LibNoDeps) |
| } |
| |
| val packageSources by registering(Jar::class) { |
| dependsOn(mainSourcesTask) |
| dependsOn(resourceShrinkerSourcesTask) |
| dependsOn(keepAnnoSourcesTask) |
| from(mainSourcesTask.outputs.files.map(::zipTree)) |
| from(resourceShrinkerSourcesTask.outputs.files.map(::zipTree)) |
| from(keepAnnoSourcesTask.outputs.files.map(::zipTree)) |
| archiveClassifier.set("sources") |
| archiveFileName.set("r8-src.jar") |
| destinationDirectory.set(getRoot().resolveAll("build", "libs")) |
| } |
| |
| test { |
| if (project.hasProperty("r8lib")) { |
| dependsOn(testR8LibWithRelocatedDeps) |
| } else if (project.hasProperty("r8lib_no_deps")) { |
| dependsOn(testR8LibNoDeps) |
| } else { |
| dependsOn(gradle.includedBuild("tests_java_8").task(":test")) |
| dependsOn(gradle.includedBuild("tests_java_17").task(":test")) |
| dependsOn(gradle.includedBuild("tests_java_21").task(":test")) |
| dependsOn(gradle.includedBuild("tests_bootstrap").task(":test")) |
| } |
| } |
| } |
| |
| fun Task.getSingleOutputFile(): File = getOutputs().getSingleOutputFile() |
| |
| fun TaskOutputs.getSingleOutputFile(): File = getFiles().getSingleFile() |
| |
| fun TaskProvider<*>.getSingleOutputFile(): File = get().getSingleOutputFile() |