// Copyright (c) 2016, 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 org.gradle.internal.os.OperatingSystem import utils.Utils apply plugin: 'java' apply plugin: 'idea' apply plugin: 'jacoco' apply plugin: 'com.google.protobuf' apply from: 'copyAdditionalJctfCommonFiles.gradle' repositories { mavenCentral() } buildscript { repositories { mavenCentral() } dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1' } } // Custom source set for example tests and generated tests. sourceSets { test { java { srcDirs = [ 'src/test/java', 'build/generated/test/java', ] } } debugTestResources { java { srcDirs = ['src/test/debugTestResources'] } output.resourcesDir = 'build/classes/debugTestResources' } debugTestResourcesJava8 { java { srcDirs = ['src/test/debugTestResourcesJava8'] } output.resourcesDir = 'build/classes/debugTestResourcesJava8' } examples { java { srcDirs = ['src/test/examples', 'build/generated/source/proto/examples/javalite/' ] } proto { srcDirs = [ 'src/test/examples', ] } output.resourcesDir = 'build/classes/examples' } examplesAndroidN { java { srcDirs = ['src/test/examplesAndroidN'] } output.resourcesDir = 'build/classes/examplesAndroidN' } examplesAndroidO { java { srcDirs = ['src/test/examplesAndroidO'] } output.resourcesDir = 'build/classes/examplesAndroidO' } jctfCommon { java { srcDirs = [ 'third_party/jctf/Harness/src', 'third_party/jctf/LibTests/src/com/google/jctf/test/categories', 'third_party/jctf/LibTests/src/com/google/jctf/test/helper', 'third_party/jctf/LibTests/src/com/google/jctf/testHelpers', 'third_party/jctf/LibTests/src/org', 'build/additionalJctfCommonFiles' ] } resources { srcDirs = ['third_party/jctf/LibTests/resources'] } } jctfTests { java { srcDirs = [ 'third_party/jctf/LibTests/src/com/google/jctf/test/lib', // 'third_party/jctf/VMTests/src', ] } } } dependencies { compile 'net.sf.jopt-simple:jopt-simple:4.6' compile group: 'com.google.guava', name: 'guava', version: '19.0' compile group: 'it.unimi.dsi', name: 'fastutil', version: '7.2.0' compile group: 'org.apache.commons', name: 'commons-compress', version: '1.12' compile group: 'org.ow2.asm', name: 'asm', version: '5.1' compile group: 'org.ow2.asm', name: 'asm-commons', version: '5.1' compile group: 'org.ow2.asm', name: 'asm-tree', version: '5.1' compile group: 'org.ow2.asm', name: 'asm-util', version: '5.1' testCompile sourceSets.examples.output testCompile 'junit:junit:4.12' testCompile group: 'org.smali', name: 'smali', version: '2.2b4' testCompile files('third_party/jasmin/jasmin-2.4.jar') testCompile files('third_party/jdwp-tests/apache-harmony-jdwp-tests-host.jar') jctfCommonCompile 'junit:junit:4.12' jctfTestsCompile 'junit:junit:4.12' jctfTestsCompile sourceSets.jctfCommon.output examplesAndroidOCompile group: 'org.ow2.asm', name: 'asm', version: '5.1' examplesCompile 'com.google.protobuf:protobuf-lite:3.0.0' examplesRuntime 'com.google.protobuf:protobuf-lite:3.0.0' } protobuf { protoc { // Download from repositories artifact = 'com.google.protobuf:protoc:3.0.0' } plugins { javalite { artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' } } generateProtoTasks { all().each { task -> task.builtins { // Disable the java code generator, as we want javalite only. remove java } task.plugins { javalite {} } } } } def osString = OperatingSystem.current().isLinux() ? "linux" : OperatingSystem.current().isMacOsX() ? "mac" : "windows" def cloudDependencies = [ "tests" : [ "2017-07-07/art.tar.gz", "2016-12-19/art.tar.gz" ], "third_party": [ "android_jar/lib-v14.tar.gz", "android_jar/lib-v19.tar.gz", "android_jar/lib-v21.tar.gz", "android_jar/lib-v24.tar.gz", "android_jar/lib-v25.tar.gz", "android_jar/lib-v26.tar.gz", "proguard/proguard5.2.1.tar.gz", "gradle/gradle.tar.gz", "jdwp-tests.tar.gz", "jasmin.tar.gz", "jctf.tar.gz", "android_cts_baseline.tar.gz", ], // All dex-vms have a fixed OS of Linux, as they are only supported on Linux, and will be run in a Docker // container on other platforms where supported. "tools" : [ "linux/art.tar.gz", "linux/art-5.1.1.tar.gz", "linux/art-6.0.1.tar.gz", "linux/art-7.0.0.tar.gz", "linux/dalvik.tar.gz", "${osString}/dx.tar.gz", ] ] cloudDependencies.each { entry -> entry.value.each { entryFile -> task "download_deps_${entry.key}/${entryFile}"(type: Exec) { def gzFile = "${entry.key}/${entryFile}" def sha1File = "${gzFile}.sha1" inputs.file sha1File outputs.file gzFile List dlFromStorageArgs = ["-n", "-b", "r8-deps", "-u", "-s", "${sha1File}"] if (OperatingSystem.current().isWindows()) { executable "download_from_google_storage.bat" args dlFromStorageArgs } else { executable "bash" args "-c", "download_from_google_storage " + String.join(" ", dlFromStorageArgs) } } } } def x20Dependencies = [ "third_party": [ "gmail/gmail_android_170604.16.tar.gz", "gmscore/v4.tar.gz", "gmscore/v5.tar.gz", "gmscore/v6.tar.gz", "gmscore/v7.tar.gz", "gmscore/v8.tar.gz", "gmscore/gmscore_v9.tar.gz", "gmscore/gmscore_v10.tar.gz", "gmscore/latest.tar.gz", "photos/2017-06-06.tar.gz", "youtube/youtube.android_12.10.tar.gz", "youtube/youtube.android_12.17.tar.gz", "youtube/youtube.android_12.22.tar.gz", "proguardsettings.tar.gz", "proguard/proguard_internal_159423826.tar.gz", "framework.tar.gz", "goyt.tar.gz", ], ] x20Dependencies.each { entry -> entry.value.each { entryFile -> task "download_deps_${entry.key}/${entryFile}"(type: Exec) { def gzFile = "${entry.key}/${entryFile}" def sha1File = "${gzFile}.sha1" inputs.file sha1File outputs.file gzFile executable "bash" args "-c", "tools/download_from_x20.py ${sha1File}" } } } task downloadProguard { cloudDependencies.each { entry -> entry.value.each { entryFile -> if (entryFile.contains("proguard")) { dependsOn "download_deps_${entry.key}/${entryFile}" } } } } task downloadDx { cloudDependencies.each { entry -> entry.value.each { entryFile -> if (entryFile.contains("dx.tar")) { dependsOn "download_deps_${entry.key}/${entryFile}" } } } } task downloadAndroidCts { cloudDependencies.each { entry -> entry.value.each { entryFile -> if (entryFile.contains("android_cts_baseline")) { dependsOn "download_deps_${entry.key}/${entryFile}" } } } } task downloadDeps { cloudDependencies.each { entry -> entry.value.each { entryFile -> dependsOn "download_deps_${entry.key}/${entryFile}" } } if (!project.hasProperty('no_internal')) { x20Dependencies.each { entry -> entry.value.each { entryFile -> dependsOn "download_deps_${entry.key}/${entryFile}" } } } } allprojects { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } tasks.withType(JavaCompile) { options.compilerArgs << '-Xlint:unchecked' } compileJctfCommonJava { dependsOn 'copyAdditionalJctfCommonFiles' options.compilerArgs = ['-Xlint:none'] } compileJctfTestsJava { dependsOn 'jctfCommonClasses' options.compilerArgs = ['-Xlint:none'] } task R8(type: Jar) { from sourceSets.main.output baseName 'r8' manifest { attributes 'Main-Class': 'com.android.tools.r8.R8' } // In order to build without dependencies, pass the exclude_deps property using: // gradle -Pexclude_deps R8 if (!project.hasProperty('exclude_deps')) { // Also include dependencies from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } } task D8(type: Jar) { from sourceSets.main.output baseName 'd8' manifest { attributes 'Main-Class': 'com.android.tools.r8.D8' } // In order to build without dependencies, pass the exclude_deps property using: // gradle -Pexclude_deps D8 if (!project.hasProperty('exclude_deps')) { // Also include dependencies from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } } task CompatDx(type: Jar) { from sourceSets.main.output baseName 'compatdx' manifest { attributes 'Main-Class': 'com.android.tools.r8.compatdx.CompatDx' } // In order to build without dependencies, pass the exclude_deps property using: // gradle -Pexclude_deps CompatDx if (!project.hasProperty('exclude_deps')) { // Also include dependencies from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } } task D8Logger(type: Jar) { from sourceSets.main.output baseName 'd8logger' manifest { attributes 'Main-Class': 'com.android.tools.r8.D8Logger' } // In order to build without dependencies, pass the exclude_deps property using: // gradle -Pexclude_deps D8Logger if (!project.hasProperty('exclude_deps')) { // Also include dependencies from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } } task disasm(type: Jar) { from sourceSets.main.output baseName 'disasm' manifest { attributes 'Main-Class': 'com.android.tools.r8.Disassemble' } // In order to build without dependencies, pass the exclude_deps property using: // gradle -Pexclude_deps D8 if (!project.hasProperty('exclude_deps')) { // Also include dependencies from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } } task bisect(type: Jar) { from sourceSets.main.output baseName 'bisect' manifest { attributes 'Main-Class': 'com.android.tools.r8.bisect.Bisect' } // In order to build without dependencies, pass the exclude_deps property using: // gradle -Pexclude_deps R8 if (!project.hasProperty('exclude_deps')) { // Also include dependencies from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } } task DexSegments(type: Jar) { from sourceSets.main.output baseName 'dexsegments' manifest { attributes 'Main-Class': 'com.android.tools.r8.DexSegments' } // In order to build without dependencies, pass the exclude_deps property using: // gradle -Pexclude_deps DexSegments if (!project.hasProperty('exclude_deps')) { // Also include dependencies from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } } task sourceJar(type: Jar, dependsOn: classes) { classifier = 'src' from sourceSets.main.allSource } task jctfCommonJar(type: Jar) { from sourceSets.jctfCommon.output baseName 'jctfCommon' } artifacts { archives sourceJar } task createArtTests(type: Exec) { def outputDir = "build/generated/test/java/com/android/tools/r8/art" def createArtTestsScript = "tools/create_art_tests.py" inputs.file "tests/2017-07-07/art.tar.gz" inputs.file createArtTestsScript outputs.dir outputDir dependsOn downloadDeps commandLine "python", createArtTestsScript workingDir = projectDir } task createJctfTests(type: Exec) { def outputDir = "build/generated/test/java/com/android/tools/r8/jctf" def script = "tools/create_jctf_tests.py" inputs.file script outputs.dir outputDir dependsOn downloadDeps commandLine "python", script workingDir = projectDir } compileTestJava { dependsOn createArtTests dependsOn createJctfTests } task buildDebugInfoExamplesDex { def examplesDir = file("src/test/java") def hostJar = "debuginfo_examples.jar" def hostDexJar = "debuginfo_examples_dex.jar" task "compile_debuginfo_examples"(type: JavaCompile) { source = fileTree(dir: examplesDir, include: "com/android/tools/r8/debuginfo/*Test.java") destinationDir = file("build/test/debuginfo_examples/classes") classpath = sourceSets.main.compileClasspath sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 options.compilerArgs += ["-Xlint:-options"] } task "jar_debuginfo_examples"(type: Jar, dependsOn: "compile_debuginfo_examples") { archiveName = hostJar destinationDir = file("build/test/") from "build/test/debuginfo_examples/classes" include "**/*.class" } task "dex_debuginfo_examples"(type: Exec, dependsOn: ["jar_debuginfo_examples", "downloadDeps"]) { if (OperatingSystem.current().isWindows()) { executable file("tools/windows/dx/bin/dx.bat") } else { executable file("tools/linux/dx/bin/dx"); } args "--dex" args "--output=build/test/${hostDexJar}" args "build/test/${hostJar}" inputs.file file("build/test/${hostJar}") outputs.file file("build/test/${hostDexJar}") } dependsOn dex_debuginfo_examples } task buildDebugTestResourcesJars { def resourcesDir = file("src/test/debugTestResources") def hostJar = "debug_test_resources.jar" task "compile_debugTestResources"(type: JavaCompile) { source = fileTree(dir: resourcesDir, include: '**/*.java') destinationDir = file("build/test/debugTestResources/classes") classpath = sourceSets.main.compileClasspath sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 options.compilerArgs += ["-g", "-Xlint:-options"] } task "jar_debugTestResources"(type: Jar, dependsOn: "compile_debugTestResources") { archiveName = hostJar destinationDir = file("build/test/") from "build/test/debugTestResources/classes" include "**/*.class" } def java8ResourcesDir = file("src/test/debugTestResourcesJava8") def java8HostJar = "debug_test_resources_java8.jar" task "compile_debugTestResourcesJava8"(type: JavaCompile) { source = fileTree(dir: java8ResourcesDir, include: '**/*.java') destinationDir = file("build/test/debugTestResourcesJava8/classes") classpath = sourceSets.main.compileClasspath sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 options.compilerArgs += ["-g", "-Xlint:-options"] } task "jar_debugTestResourcesJava8"(type: Jar, dependsOn: "compile_debugTestResourcesJava8") { archiveName = java8HostJar destinationDir = file("build/test/") from "build/test/debugTestResourcesJava8/classes" include "**/*.class" } dependsOn downloadDeps dependsOn jar_debugTestResources dependsOn jar_debugTestResourcesJava8 } task buildExampleJars { dependsOn downloadProguard def examplesDir = file("src/test/examples") def protoSourceDir = file("build/generated/source/proto/examples/javalite") def proguardScript if (OperatingSystem.current().isWindows()) { proguardScript = "third_party/proguard/proguard5.2.1/bin/proguard.bat" } else { proguardScript = "third_party/proguard/proguard5.2.1/bin/proguard.sh" } task extractExamplesRuntime(type: Sync) { dependsOn configurations.examplesRuntime from configurations.examplesRuntime.collect { zipTree(it) } include "**/*.class" includeEmptyDirs false into "$buildDir/runtime/examples/" } task "compile_examples"(type: JavaCompile, dependsOn: "generateExamplesProto") { source examplesDir, protoSourceDir include "**/*.java" destinationDir = file("build/test/examples/classes") classpath = sourceSets.examples.compileClasspath sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 options.compilerArgs += ["-Xlint:-options"] } examplesDir.eachDir { dir -> def name = dir.getName(); def exampleOutputDir = file("build/test/examples"); def jarName = "${name}.jar" dependsOn "jar_example_${name}" dependsOn "extractExamplesRuntime" def runtimeDependencies = copySpec { } if (!fileTree(dir: dir, include: '**/*.proto').empty) { // If we have any proto use, we have to include those classes and the runtime. runtimeDependencies = copySpec { from "$buildDir/runtime/examples/" include "com/google/protobuf/**/*.class" } } // The "throwing" test verifies debugging/stack info on the post-proguarded output. def proguardConfigPath = "${dir}/proguard.cfg" if (new File(proguardConfigPath).exists()) { task "pre_proguard_example_${name}"(type: Jar, dependsOn: "compile_examples") { archiveName = "${name}_pre_proguard.jar" destinationDir = exampleOutputDir from "build/test/examples/classes" include name + "/**/*.class" with runtimeDependencies includeEmptyDirs false } def jarPath = files(tasks.getByPath("pre_proguard_example_${name}")).files.first(); def proguardJarPath = "${exampleOutputDir}/${jarName}" def proguardMapPath = "${exampleOutputDir}/${name}/${name}.map" task "jar_example_${name}"(type: Exec, dependsOn: "pre_proguard_example_${name}") { inputs.files tasks.getByPath("pre_proguard_example_${name}") inputs.file proguardConfigPath // Enable these to get stdout and stderr redirected to files... // standardOutput = new FileOutputStream('proguard.stdout') // errorOutput = new FileOutputStream('proguard.stderr') def proguardArguments = "-verbose -dontwarn java.** -injars ${jarPath}" + " -outjars ${proguardJarPath}" + " -include ${proguardConfigPath}" + " -printmapping ${proguardMapPath}" if (OperatingSystem.current().isWindows()) { executable "${proguardScript}" args "${proguardArguments}" } else { executable "bash" args "-c", "${proguardScript} '${proguardArguments}'" } outputs.file proguardJarPath } } else { task "jar_example_${name}"(type: Jar, dependsOn: "compile_examples") { archiveName = jarName destinationDir = exampleOutputDir from "build/test/examples/classes" include name + "/**/*.class" with runtimeDependencies includeEmptyDirs false } } } } task buildExampleAndroidNJars { dependsOn downloadDeps def examplesDir = file("src/test/examplesAndroidN") task "compile_examplesAndroidN"(type: JavaCompile) { source = fileTree(dir: examplesDir, include: '**/*.java') destinationDir = file("build/test/examplesAndroidN/classes") classpath = sourceSets.main.compileClasspath sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 options.compilerArgs += ["-Xlint:-options"] } examplesDir.eachDir { dir -> def name = dir.getName(); def exampleOutputDir = file("build/test/examplesAndroidN"); def jarName = "${name}.jar" dependsOn "jar_examplesAndroidN_${name}" task "jar_examplesAndroidN_${name}"(type: Jar, dependsOn: "compile_examplesAndroidN") { archiveName = jarName destinationDir = exampleOutputDir from "build/test/examplesAndroidN/classes" include "**/" + name + "/**/*.class" } } } task buildExampleAndroidOJars { dependsOn downloadDeps def examplesDir = file("src/test/examplesAndroidO") // NOTE: we want to enable a scenario when test needs to reference some // classes generated by legacy (1.6) Java compiler to test some specific // behaviour. To do so we compile all the java files located in sub-directory // called 'legacy' with Java 1.6, then compile the rest of the files with // Java 1.8 and a reference to previously generated 1.6 classes. // Compiling all classes in dirs 'legacy' with old Java version. task "compile_examplesAndroidO_Legacy"(type: JavaCompile) { source = fileTree(dir: examplesDir, include: '**/legacy/**/*.java') destinationDir = file("build/test/examplesAndroidOLegacy/classes") classpath = sourceSets.main.compileClasspath sourceCompatibility = JavaVersion.VERSION_1_6 targetCompatibility = JavaVersion.VERSION_1_6 options.compilerArgs += ["-Xlint:-options", "-parameters"] } // Compiling the rest of the files as Java 1.8 code. task "compile_examplesAndroidO"(type: JavaCompile) { dependsOn "compile_examplesAndroidO_Legacy" source = fileTree(dir: examplesDir, include: '**/*.java', exclude: '**/legacy/**/*.java') destinationDir = file("build/test/examplesAndroidO/classes") classpath = sourceSets.main.compileClasspath classpath += files("build/test/examplesAndroidOLegacy/classes") sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 options.compilerArgs += ["-Xlint:-options", "-parameters"] } examplesDir.eachDir { dir -> def name = dir.getName(); def destinationDir = file("build/test/examplesAndroidO/classes"); if (file("src/test/examplesAndroidO/" + name + "/TestGenerator.java").isFile()) { task "generate_examplesAndroidO_${name}"(type: JavaExec, dependsOn: "compile_examplesAndroidO") { main = name + ".TestGenerator" classpath = files(destinationDir, sourceSets.main.compileClasspath) args destinationDir } } else { task "generate_examplesAndroidO_${name}" () {} } } examplesDir.eachDir { dir -> def name = dir.getName(); def exampleOutputDir = file("build/test/examplesAndroidO"); def jarName = "${name}.jar" dependsOn "jar_examplesAndroidO_${name}" task "jar_examplesAndroidO_${name}"(type: Jar, dependsOn: ["compile_examplesAndroidO", "generate_examplesAndroidO_${name}"]) { archiveName = jarName destinationDir = exampleOutputDir from "build/test/examplesAndroidO/classes" // Java 1.8 classes from "build/test/examplesAndroidOLegacy/classes" // Java 1.6 classes include "**/" + name + "/**/*.class" // Do not include generator into the test runtime jar, it is not useful. // Otherwise, shrinking will need ASM jars. exclude "**/TestGenerator*" } } } task buildExamples { if (OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) { logger.lifecycle("WARNING: Testing (including building examples) is only partially supported on your " + "platform (" + OperatingSystem.current().getName() + ").") } else if (!OperatingSystem.current().isLinux()) { logger.lifecycle("WARNING: Testing (including building examples) is not supported on your platform. " + "It is fully supported on Linux and partially supported on Mac OS and Windows") return; } dependsOn buildDebugTestResourcesJars dependsOn buildExampleJars dependsOn buildExampleAndroidNJars dependsOn buildExampleAndroidOJars def examplesDir = file("src/test/examples") def noDexTests = [ "multidex", "multidex002", "multidex004", ] examplesDir.eachDir { dir -> def name = dir.getName(); if (!(name in noDexTests)) { dependsOn "dex_example_${name}" def exampleOutputDir = file("build/test/examples/" + name); def dexPath = file("${exampleOutputDir}") def debug = (name == "throwing") if (!dexPath.exists()) { dexPath.mkdirs() } task "dex_example_${name}"(type: dx.Dx, dependsOn: "jar_example_${name}") { source = files(tasks.getByPath("jar_example_${name}")).asFileTree destination = dexPath debug = debug } } } } task buildSmali { def smaliDir = file("src/test/smali") smaliDir.eachDirRecurse() { dir -> def name = dir.getName(); def relativeDir = smaliDir.toPath().relativize(dir.toPath()); def smaliOutputDir = file("build/test/smali/" + relativeDir); smaliOutputDir.mkdirs() outputs.dir smaliOutputDir def taskName = "smali_build_${relativeDir.toString().replace('/', '_')}" def smaliFiles = fileTree(dir: dir, include: '*.smali') def javaFiles = fileTree(dir: dir, include: '*.java') def destDir = smaliOutputDir; def destFile = destDir.toPath().resolve("${name}.dex").toFile() def intermediateFileName = "${name}-intermediate.dex"; def intermediateFile = destDir.toPath().resolve(intermediateFileName).toFile() if (javaFiles.empty) { if (!smaliFiles.empty) { dependsOn "${taskName}_smali" task "${taskName}_smali"(type: smali.Smali) { source = smaliFiles destination = destFile } } } else { dependsOn "${taskName}_dexmerger" task "${taskName}_smali"(type: smali.Smali) { source = smaliFiles destination = intermediateFile } task "${taskName}_java"(type: JavaCompile) { source = javaFiles destinationDir destDir classpath = sourceSets.main.compileClasspath sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 options.compilerArgs += ["-Xlint:-options"] } task "${taskName}_jar"(type: Jar, dependsOn: "${taskName}_java") { archiveName = "Test.jar" destinationDir = destDir from fileTree(dir: destDir, include: 'Test.class') } task "${taskName}_dx"(type: dx.Dx, dependsOn: "${taskName}_jar") { source = fileTree(dir: destDir, include: 'Test.jar') destination = destDir } task "${taskName}_dexmerger"( type: dx.DexMerger, dependsOn: ["${taskName}_dx", "${taskName}_smali"]) { source = fileTree(dir: destDir, include: ["classes.dex", intermediateFileName]) destination = destFile } } } } tasks.withType(Test) { def userDefinedCoresPerFork = System.getenv('R8_GRADLE_CORES_PER_FORK') def coresPerFork = userDefinedCoresPerFork ? userDefinedCoresPerFork.toInteger() : 3 // See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html. maxParallelForks = Runtime.runtime.availableProcessors().intdiv(coresPerFork) ?: 1 forkEvery = 0 // Use the Concurrent Mark Sweep GC (CMS) to keep memory usage at a resonable level. jvmArgs = ["-XX:+UseConcMarkSweepGC"] if (project.hasProperty('disable_assertions')) { enableAssertions = false } } task buildPreNJdwpTestsJar(type: Jar) { baseName = 'jdwp-tests-preN' from zipTree('third_party/jdwp-tests/apache-harmony-jdwp-tests-host.jar') // Exclude the classes containing java8 exclude 'org/apache/harmony/jpda/tests/jdwp/InterfaceType/*.class' exclude 'org/apache/harmony/jpda/tests/jdwp/ObjectReference/InvokeMethodDefault*.class' includeEmptyDirs = false } test { testLogging.exceptionFormat = 'full' if (project.hasProperty('print_test_stdout')) { testLogging.showStandardStreams = true } if (project.hasProperty('dex_vm')) { println "Running with non default vm: " + project.property('dex_vm') systemProperty 'dex_vm', project.property('dex_vm') if (project.property('dex_vm') == '5.1.1' || project.property('dex_vm') == '6.0.1') { // R8 and D8 compute the dex file version number based on the input. // Jack generates dex files with version 37 which art 5.1.1 and 6.0.1 will not run. // Therefore we skip the jack generated art tests with those art versions. exclude "com/android/tools/r8/art/jack/**" } } if (project.hasProperty('one_line_per_test')) { beforeTest { desc -> println "Start executing test ${desc.name} [${desc.className}]" } afterTest { desc, result -> println "Done executing test ${desc.name} [${desc.className}] with result: ${result.resultType}" } } if (project.hasProperty('no_internal')) { exclude "com/android/tools/r8/internal/**" } if (project.hasProperty('only_internal')) { include "com/android/tools/r8/internal/**" } if (project.hasProperty('tool')) { if (project.property('tool') == 'r8') { exclude "com/android/tools/r8/art/*/d8/**" exclude "com/android/tools/r8/jctf/d8/**" } else { assert(project.property('tool') == 'd8') exclude "com/android/tools/r8/art/*/r8/**" exclude "com/android/tools/r8/jctf/r8/**" } } if (!project.hasProperty('all_tests')) { exclude "com/android/tools/r8/art/dx/**" exclude "com/android/tools/r8/art/jack/**" } // TODO(tamaskenez) enable jctf on all_tests when consolidated if (!project.hasProperty('jctf') && !project.hasProperty('only_jctf')) { exclude "com/android/tools/r8/jctf/**" } if (project.hasProperty('only_jctf')) { include "com/android/tools/r8/jctf/**" } if (project.hasProperty('jctf_compile_only')) { println "JCTF: compiling only" systemProperty 'jctf_compile_only', '1' } if (OperatingSystem.current().isLinux() || OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) { if (OperatingSystem.current().isMacOsX()) { logger.lifecycle("WARNING: Testing in only partially supported on Mac OS. " + "Art only runs on Linux and tests requiring Art runs in a Docker container, which must be present. " + "See tools/docker/README.md for details.") } if (OperatingSystem.current().isWindows()) { logger.lifecycle("WARNING: Testing in only partially supported on Windows. " + "Art only runs on Linux and tests requiring Art will be skipped") } dependsOn downloadDeps dependsOn buildExamples dependsOn buildSmali dependsOn jctfCommonJar dependsOn jctfTestsClasses dependsOn buildDebugInfoExamplesDex dependsOn buildPreNJdwpTestsJar } else { logger.lifecycle("WARNING: Testing in not supported on your platform. Testing is only fully supported on " + "Linux and partially supported on Mac OS and Windows. Art does not run on other platforms.") } } // The Art tests we use for R8 are pre-build and downloaded from Google Cloud Storage. // // To build and upload a new set of the Art tests for use with R8 follow these steps: // // First of all an Android checkout is required. Currently it must be located // in $HOME/android/master. // // TODO(ricow): simplify this // // Before: update the checked in art, see scripts/update-host-art.sh // // 1. Get an android checkout in $HOME/android/master and apply the patch from // https://android-review.googlesource.com/#/c/294187/ // // 2. run the following commands in the Android checkout directory: // // source build/envsetup.sh // lunch aosp_angler-userdebug # or lunch aosp_angler-eng // m desugar // m -j30 test-art-host // DESUGAR=false ANDROID_COMPILE_WITH_JACK=false art/test.py --host -t 001-HelloWorld // // Without running the test.py command the classes.jar file used by desugar in // $HOME/android/master/out/host/common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/ // seems to be missing - there is probably also a make target to build it more directly // // 3. In the R8 project root directory, make sure we have a clean state before starting: // tools/gradle.py downloadDeps // tools/gradle.py clean // rm -rf tests/art // // 4. Now build in the R8 checkout (-P hack to not generate dirs when not running this target) // Make sure you have smali on your path, please use the build binary in the // out/host/linux-x86/bin directory of the android checkout. Currently this is version pre 2.2.1, // if that is updated the call to smali in "task "${smaliToDexTask}"(type: Exec)" below might // need to change as smali got a completely new command line interface in version 2.2.1. // // PATH=$HOME/android/master/out/host/linux-x86/bin:$PATH tools/gradle.py -Pandroid_source buildArtTests // // 4a. If any failures are produced in step 4, figure out what went wrong and add an entry in // skippedTests with an explanation. Rerun from step 3. // // 5. Run the tests: // tools/gradle.py clean // tools/test.py // // 5a. If any more tests fail, either fix the issue or add them to the failuresToTriage list (note // that you need to change "_" to "-" from stdout). Rerun from step 5 if anything was added to // failuresToTriage. // // 6. To upload a new version to Google Cloud Storage: // cd tests // upload_to_google_storage.py -a --bucket r8-deps art // // 7. Update the manifest file describing the Android repo used: // repo manifest -o /tools/linux/aosp_master_manifest.xml -r enum DexTool { JACK, DX } def androidCheckoutDir = file("${System.env.HOME}/android/master") def androidCheckoutJack = file("${androidCheckoutDir}/out/host/linux-x86/bin/jack"); def androidCheckoutJackServer = file("${androidCheckoutDir}/out/host/linux-x86/bin/jack-admin"); def artTestDir = file("${androidCheckoutDir}/art/test") if (project.hasProperty('android_source')) { task buildArtTests { outputs.upToDateWhen { false } def toBeTriaged = [ "903-hello-tagging", "904-object-allocation", "905-object-free", "906-iterate-heap", "907-get-loaded-classes", "908-gc-start-finish", "954-invoke-polymorphic-verifier", "955-methodhandles-smali", "596-monitor-inflation", ] def skippedTests = toBeTriaged + [ // This test produces no jar. "000-nop", // This does not build, as it tests the error when the application exceeds more // than 65536 methods "089-many-methods", // Requires some jack beta jar "956-methodhandles", ] def skippedTestsDx = [ // Tests with custom build scripts, where javac is not passed the options // -source 1.7 -target 1.7. "462-checker-inlining-across-dex-files", "556-invoke-super", "569-checker-pattern-replacement", // These tests use jack even when --build-with-javac-dx is specified. "004-JniTest", "048-reflect-v8", "146-bad-interface", "563-checker-invoke-super", "580-checker-string-fact-intrinsics", // java.lang.StringFactory "604-hot-static-interface", "957-methodhandle-transforms", "958-methodhandle-emulated-stackframe", "959-invoke-polymorphic-accessors", "961-default-iface-resolution-gen", "962-iface-static", "963-default-range-smali", "964-default-iface-init-gen", "965-default-verify", "966-default-conflict", "967-default-ame", "968-default-partial-compile-gen", "969-iface-super", "970-iface-super-resolution-gen", "971-iface-super", // These tests does not build with --build-with-javac-dx "004-NativeAllocations", // Javac error "031-class-attributes", "138-duplicate-classes-check", "157-void-class", // Javac error "580-checker-string-factory-intrinsics", "612-jit-dex-cache", "613-inlining-dex-cache", "900-hello-plugin", // --experimental agents "901-hello-ti-agent", // --experimental agents "902-hello-transformation", // --experimental agents "909-attach-agent", // --experimental agents "946-obsolete-throw", // -source 1.7 -target 1.7, but use lambda "950-redefine-intrinsic", // -source 1.7 -target 1.7, but use method references "951-threaded-obsolete", // -source 1.7 -target 1.7, but use lambda "960-default-smali", // --experimental default-methods // These tests force the build to use jack "953-invoke-polymorphic-compiler", "958-methodhandle-stackframe", ] def artTestBuildDir = file("${projectDir}/tests/art") if (androidCheckoutDir.exists()) { dependsOn downloadDeps artTestBuildDir.mkdirs() // Ensure Jack server is running. "${androidCheckoutJackServer} start-server".execute() artTestDir.eachDir { dir -> def name = dir.getName(); def markerFile = dir.toPath().resolve("info.txt").toFile(); if (markerFile.exists() && !(name in skippedTests)) { if (!(name in skippedTestsDx)) { dependsOn buildArtTest(androidCheckoutDir, artTestBuildDir, dir, DexTool.DX); } dependsOn buildArtTest(androidCheckoutDir, artTestBuildDir, dir, DexTool.JACK); } } } doFirst { if (!androidCheckoutDir.exists()) { throw new InvalidUserDataException( "This task requires an Android checkout in ${androidCheckoutDir}"); } else if (!androidCheckoutJack.exists() || !androidCheckoutJackServer.exists()) { throw new InvalidUserDataException( "This task requires that tools for host testing have been build in the " + "Android checkout in ${androidCheckoutDir}"); } } doLast { copy { from file("${androidCheckoutDir}/out/host/linux-x86/nativetest64") into file("${artTestBuildDir}/lib64") include 'lib*.so' } copy { from file("${androidCheckoutDir}/out/host/linux-x86/lib64") into file("${artTestBuildDir}/lib64") include 'libart.so' include 'libbacktrace.so' include 'libbase.so' include 'libc++.so' include 'libcutils.so' include 'liblz4.so' include 'liblzma.so' include 'libnativebridge.so' include 'libnativeloader.so' include 'libsigchain.so' include 'libunwind.so' include 'libziparchive.so' } copy { from file("${androidCheckoutDir}/out/host/linux-x86/nativetest") into file("${artTestBuildDir}/lib") include 'lib*.so' } copy { from file("${androidCheckoutDir}/out/host/linux-x86/lib") into file("${artTestBuildDir}/lib") include 'libart.so' include 'libbacktrace.so' include 'libbase.so' include 'libc++.so' include 'libcutils.so' include 'liblz4.so' include 'liblzma.so' include 'libnativebridge.so' include 'libnativeloader.so' include 'libsigchain.so' include 'libunwind.so' include 'libziparchive.so' } } } } def buildArtTest(androidCheckoutDir, artTestBuildDir, dir, dexTool) { def artTestDir = file("${androidCheckoutDir}/art/test") def artRunTestScript = file("${artTestDir}/run-test") def dxExecutable = new File("tools/linux/dx/bin/dx"); def dexMergerExecutable = Utils.dexMergerExecutable() def dexToolName = dexTool == DexTool.DX ? "dx" : "jack" def name = dir.getName() def buildTask = "build_art_test_${dexToolName}_${name}" def sanitizeTask = "sanitize_art_test_${dexToolName}_${name}" def copyCheckTask = "copy_check_art_test_${dexToolName}_${name}" def smaliToDexTask = "smali_to_dex_${dexToolName}_${name}" def buildInputs = fileTree(dir: dir, include: '**/*') def testDir = file("${artTestBuildDir}/${dexToolName}/${name}") def outputJar = testDir.toPath().resolve("${name}.jar").toFile() testDir.mkdirs() if (dexTool == DexTool.DX) { task "$buildTask"(type: Exec) { outputs.upToDateWhen { false } inputs.file buildInputs executable "${artRunTestScript}" args "--host" args "--build-only" args "--build-with-javac-dx" args "--output-path", "${testDir}" args "${name}" environment DX: "${dxExecutable.absolutePath}" environment DXMERGER: "${dexMergerExecutable.absolutePath}" environment ANDROID_BUILD_TOP: "${androidCheckoutDir}" outputs.file outputJar } } else { assert dexTool == DexTool.JACK def javaLibs = "${androidCheckoutDir}/out/host/common/obj/JAVA_LIBRARIES" def jackClasspath = "${javaLibs}/core-libart-hostdex_intermediates/classes.jack:" + "${javaLibs}/core-oj-hostdex_intermediates/classes.jack" task "$buildTask"(type: Exec) { outputs.upToDateWhen { false } inputs.file buildInputs executable "${artRunTestScript}" args "--host" args "--build-only" args "--output-path", "${testDir}" args "${name}" environment JACK: "${androidCheckoutDir}/out/host/linux-x86/bin/jack" environment JACK_CLASSPATH: jackClasspath environment DXMERGER: "${dexMergerExecutable.absolutePath}" environment ANDROID_BUILD_TOP: "${androidCheckoutDir}" outputs.file outputJar } } task "${sanitizeTask}"(type: Exec, dependsOn: buildTask) { outputs.upToDateWhen { false } executable "/bin/bash" args "-c" args "rm -rf ${testDir}/smali_*.dex ${testDir}/*-ex.dex ${testDir}/*-ex.jar" + " ${testDir}/classes-ex ${testDir}/check" } task "${smaliToDexTask}"(type: Exec) { workingDir "${testDir}/smali" executable "/bin/bash" // This is the command line options for smali prior to 2.2.1, where smali got a new // command line interface. args "-c", "smali a *.smali" // This is the command line options for smali 2.2.1 and later. // args "-c", "smali -o out.dex *.smali" } task "${copyCheckTask}"(type: Copy, dependsOn: sanitizeTask) { def smali_dir = file("${dir}/smali") outputs.upToDateWhen { false } if (smali_dir.exists() && dexTool == DexTool.DX) { dependsOn smaliToDexTask } from("${artTestDir}/${name}") { include 'check' } into testDir } return copyCheckTask } task javadocD8(type: Javadoc) { classpath = sourceSets.main.compileClasspath source = sourceSets.main.allJava include '**/com/android/tools/r8/BaseCommand.java' include '**/com/android/tools/r8/BaseOutput.java' include '**/com/android/tools/r8/CompilationException.java' include '**/com/android/tools/r8/CompilationMode.java' include '**/com/android/tools/r8/D8.java' include '**/com/android/tools/r8/D8Command.java' include '**/com/android/tools/r8/D8Output.java' include '**/com/android/tools/r8/Resource.java' }