| // 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 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(bootstrapTestJarTask) | |
| from(java8TestJarTask.outputs.files.map(::zipTree)) | |
| from(java11TestJarTask.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", | |
| "${org.gradle.internal.jvm.Jvm.current().getJavaHome()}", | |
| "--lib", | |
| "$mainDepsJar", | |
| "--lib", | |
| "$testDepsJar", | |
| "--target", | |
| "$targetJar", | |
| "--output", | |
| "$output") | |
| testJarProviders.forEach{ | |
| argList.add("--source") | |
| argList.add("${it.getSingleOutputFile()}") | |
| } | |
| commandLine = baseCompilerCommandLine( | |
| 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") | |
| } | |
| val resourceshrinkercli by registering(Exec::class) { | |
| dependsOn(r8WithRelocatedDepsTask) | |
| val r8 = r8WithRelocatedDepsTask.getSingleOutputFile() | |
| val keepTxt = getRoot().resolveAll("src", "main", "resourceshrinker_cli.txt") | |
| val cliKeep = getRoot().resolveAll("src", "main", "keep_r8resourceshrinker.txt") | |
| inputs.files(keepTxt, cliKeep) | |
| val output = file(Paths.get("build", "libs", "resourceshrinkercli.jar")) | |
| outputs.file(output) | |
| commandLine = createR8LibCommandLine( | |
| r8, | |
| r8, | |
| output, | |
| listOf(keepTxt, cliKeep), | |
| false, | |
| false) | |
| } | |
| 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", | |
| "${org.gradle.internal.jvm.Jvm.current().getJavaHome()}", | |
| "--classpath", | |
| "$r8Jar", | |
| "--classpath", | |
| "$testDepsJar", | |
| "--output", | |
| "$outputJar", | |
| "--pg-conf", | |
| "$keepRulesFile", | |
| "$testJar") | |
| if (addTestBaseClasspath) { | |
| args.add("--classpath") | |
| args.add("$testBaseJar") | |
| } | |
| commandLine = baseCompilerCommandLine( | |
| 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) | |
| 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_bootstrap").task(":test")) | |
| } | |
| } | |
| } | |
| fun Task.getSingleOutputFile(): File = getOutputs().getSingleOutputFile() | |
| fun TaskOutputs.getSingleOutputFile(): File = getFiles().getSingleFile() | |
| fun TaskProvider<*>.getSingleOutputFile(): File = get().getSingleOutputFile() |