Add support for building java 15 examples
This also adds the first JDK 15 example of using records.
Bug: 169692487
Bug: 141587937
Change-Id: I3ed7800dc16b9f08510d5bd77b658d5891b98707
diff --git a/build.gradle b/build.gradle
index be4d51e..197a5e5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -135,6 +135,11 @@
srcDirs = ['src/test/examplesJava11']
}
}
+ examplesJava15 {
+ java {
+ srcDirs = ['src/test/examplesJava15']
+ }
+ }
jdk11TimeTests {
java {
srcDirs = [
@@ -287,7 +292,7 @@
def r8DesugaredPath = "$buildDir/libs/r8desugared.jar"
def r8LibGeneratedKeepRulesPath = "$buildDir/generated/keep.txt"
def r8LibTestPath = "$buildDir/classes/r8libtest"
-def java11ClassFiles = "build/classes/java/mainJava11"
+def java11ClassFiles = "$buildDir/classes/java/mainJava11"
def osString = OperatingSystem.current().isLinux() ? "linux" :
OperatingSystem.current().isMacOsX() ? "mac" : "windows"
@@ -558,21 +563,37 @@
}
}
-tasks.named(sourceSets.examplesJava9.compileJavaTaskName).get().configure {
- def jdkDir = 'third_party/openjdk/openjdk-9.0.4/'
- options.fork = true
- options.forkOptions.jvmArgs = []
- if (OperatingSystem.current().isLinux()) {
- options.forkOptions.javaHome = file(jdkDir + 'linux')
- } else if (OperatingSystem.current().isMacOsX()) {
- options.forkOptions.javaHome = file(jdkDir + 'osx')
- } else {
- options.forkOptions.javaHome = file(jdkDir + 'windows')
+def setJdkCompilationWithCompatibility(String sourceSet, String javaHome, JavaVersion compatibility, boolean enablePreview) {
+ tasks.named(sourceSet).get().configure {
+ def jdkDir = "third_party/openjdk/${javaHome}/"
+ options.fork = true
+ options.forkOptions.jvmArgs = []
+ if (enablePreview) {
+ options.compilerArgs.add('--enable-preview')
+ }
+ if (OperatingSystem.current().isLinux()) {
+ options.forkOptions.javaHome = file(jdkDir + 'linux')
+ } else if (OperatingSystem.current().isMacOsX()) {
+ options.forkOptions.javaHome = file(jdkDir + 'osx')
+ } else {
+ options.forkOptions.javaHome = file(jdkDir + 'windows')
+ }
+ sourceCompatibility = compatibility
+ targetCompatibility = compatibility
}
- sourceCompatibility = JavaVersion.VERSION_1_9
- targetCompatibility = JavaVersion.VERSION_1_9
}
+setJdkCompilationWithCompatibility(
+ sourceSets.examplesJava9.compileJavaTaskName,
+ 'openjdk-9.0.4',
+ JavaVersion.VERSION_1_9,
+ false)
+setJdkCompilationWithCompatibility(
+ sourceSets.examplesJava15.compileJavaTaskName,
+ 'jdk-15',
+ JavaVersion.VERSION_15,
+ true)
+
def setJdk11CompilationWithCompatibility(String sourceSet, JavaVersion compatibility) {
tasks.named(sourceSet).get().configure {
def jdkDir = 'third_party/openjdk/jdk-11/'
@@ -717,6 +738,8 @@
}
task repackageSourcesNew(type: ShadowJar) {
+ // If this fails then remove all generated folders from
+ // build/classes/java/test that is not {com,dalvik}
from sourceSets.main.output
mergeServiceFiles(it)
baseName 'sources_main'
@@ -733,6 +756,7 @@
return tasks.create("r8Create${name}", ShadowJar) {
from consolidatedLicense.outputs.files
from sources
+ exclude "$buildDir/classes/**"
baseName baseNameName
classifier = null
version = null
@@ -1522,53 +1546,28 @@
}
}
-task buildExampleJava9Jars {
- def examplesDir = file("src/test/examplesJava9")
- examplesDir.eachDir { dir ->
- def name = dir.getName();
- def exampleOutputDir = file("build/test/examplesJava9");
- def jarName = "${name}.jar"
- dependsOn "jar_examplesJava9_${name}"
- task "jar_examplesJava9_${name}"(type: Jar) {
- archiveName = jarName
- destinationDir = exampleOutputDir
- from sourceSets.examplesJava9.output
- include "**/" + name + "/**/*.class"
+def buildExampleJarsCreateTask(javaVersion, sourceSet) {
+ return tasks.create("buildExample${javaVersion}Jars") {
+ def examplesDir = file("src/test/examples${javaVersion}")
+ examplesDir.eachDir { dir ->
+ def name = dir.getName();
+ def exampleOutputDir = file("build/test/examples${javaVersion}");
+ def jarName = "${name}.jar"
+ dependsOn "jar_examples${javaVersion}_${name}"
+ task "jar_examples${javaVersion}_${name}"(type: Jar) {
+ archiveName = jarName
+ destinationDir = exampleOutputDir
+ from sourceSet.output
+ include "**/" + name + "/**/*.class"
+ }
}
}
}
-task buildExampleJava10Jars {
- def examplesDir = file("src/test/examplesJava10")
- examplesDir.eachDir { dir ->
- def name = dir.getName();
- def exampleOutputDir = file("build/test/examplesJava10");
- def jarName = "${name}.jar"
- dependsOn "jar_examplesJava10_${name}"
- task "jar_examplesJava10_${name}"(type: Jar) {
- archiveName = jarName
- destinationDir = exampleOutputDir
- from sourceSets.examplesJava10.output
- include "**/" + name + "/**/*.class"
- }
- }
-}
-
-task buildExampleJava11Jars {
- def examplesDir = file("src/test/examplesJava11")
- examplesDir.eachDir { dir ->
- def name = dir.getName();
- def exampleOutputDir = file("build/test/examplesJava11");
- def jarName = "${name}.jar"
- dependsOn "jar_examplesJava11_${name}"
- task "jar_examplesJava11_${name}"(type: Jar) {
- archiveName = jarName
- destinationDir = exampleOutputDir
- from sourceSets.examplesJava11.output
- include "**/" + name + "/**/*.class"
- }
- }
-}
+buildExampleJarsCreateTask("Java9", sourceSets.examplesJava9)
+buildExampleJarsCreateTask("Java10", sourceSets.examplesJava10)
+buildExampleJarsCreateTask("Java11", sourceSets.examplesJava11)
+buildExampleJarsCreateTask("Java15", sourceSets.examplesJava15)
task provideArtFrameworksDependencies {
cloudDependencies.tools.forEach({ art ->
@@ -1688,6 +1687,7 @@
dependsOn buildExampleJava9Jars
dependsOn buildExampleJava10Jars
dependsOn buildExampleJava11Jars
+ dependsOn buildExampleJava15Jars
dependsOn buildExampleAndroidApi
def examplesDir = file("src/test/examples")
def noDexTests = [
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..41fabcc
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,5 @@
+# Copyright (c) 2020, 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.
+
+org.gradle.jvmargs=-Xmx2048M
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 902924a..a90d2bc 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -341,4 +341,11 @@
throwable.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
+
+ public static String capitalize(String stringToCapitalize) {
+ if (stringToCapitalize == null || stringToCapitalize.isEmpty()) {
+ return stringToCapitalize;
+ }
+ return stringToCapitalize.substring(0, 1).toUpperCase() + stringToCapitalize.substring(1);
+ }
}
diff --git a/src/test/examplesJava15/records/Main.java b/src/test/examplesJava15/records/Main.java
new file mode 100644
index 0000000..7be696d
--- /dev/null
+++ b/src/test/examplesJava15/records/Main.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2020, 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.
+
+package records;
+
+public class Main {
+
+ record Person(String name, int age) {}
+
+ public static void main(String[] args) {
+ Person janeDoe = new Person("Jane Doe", 42);
+ System.out.println(janeDoe.name);
+ System.out.println(janeDoe.age);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index db1ca7f..ac2fce4 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -27,6 +27,7 @@
JDK9("jdk9", 53),
JDK10("jdk10", 54),
JDK11("jdk11", 55),
+ JDK15("jdk15", 59),
;
private final String name;
@@ -67,6 +68,7 @@
private static final Path JDK9_PATH =
Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "openjdk-9.0.4");
private static final Path JDK11_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-11");
+ private static final Path JDK15_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-15");
public static CfRuntime getCheckedInJdk8() {
Path home;
@@ -107,6 +109,20 @@
return new CfRuntime(CfVm.JDK11, home);
}
+ // TODO(b/169692487): Add this to 'getCheckedInCfRuntimes' when we start having support for JDK15.
+ public static CfRuntime getCheckedInJdk15() {
+ Path home;
+ if (ToolHelper.isLinux()) {
+ home = JDK15_PATH.resolve("linux");
+ } else if (ToolHelper.isMac()) {
+ home = JDK15_PATH.resolve("osx");
+ } else {
+ assert ToolHelper.isWindows();
+ home = JDK15_PATH.resolve("windows");
+ }
+ return new CfRuntime(CfVm.JDK15, home);
+ }
+
public static List<CfRuntime> getCheckedInCfRuntimes() {
CfRuntime[] jdks =
new CfRuntime[] {getCheckedInJdk8(), getCheckedInJdk9(), getCheckedInJdk11()};
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java
new file mode 100644
index 0000000..723dfe6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2020, 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.
+
+package com.android.tools.r8.desugar.records;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.examples.jdk15.Records;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RecordsAttributeTest extends TestBase {
+
+ private final Backend backend;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(getTestParameters().withNoneRuntime().build(), Backend.values());
+ }
+
+ public RecordsAttributeTest(TestParameters parameters, Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(backend == Backend.CF);
+ testForJvm()
+ .addRunClasspathFiles(Records.jar())
+ .addVmArguments("--enable-preview")
+ .run(TestRuntime.getCheckedInJdk15(), Records.Main.typeName())
+ .assertSuccessWithOutputLines("Jane Doe", "42");
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ testForD8(backend)
+ .addProgramClassFileData(Records.Main.bytes(), Records.Main$Person.bytes())
+ .setMinApi(AndroidApiLevel.B)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(containsString("Unsupported class file version: 59")));
+ });
+ });
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ testForR8(backend)
+ .addProgramClassFileData(Records.Main.bytes(), Records.Main$Person.bytes())
+ .setMinApi(AndroidApiLevel.B)
+ .addKeepMainRule(Records.Main.typeName())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(containsString("Unsupported class file version: 59")));
+ });
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/JavaExampleClassProxy.java b/src/test/java/com/android/tools/r8/examples/JavaExampleClassProxy.java
new file mode 100644
index 0000000..6bbbfd1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/JavaExampleClassProxy.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2020, 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.
+
+package com.android.tools.r8.examples;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.zip.ZipFile;
+
+public class JavaExampleClassProxy {
+
+ private final String examplesFolder;
+ private final String binaryName;
+
+ public JavaExampleClassProxy(String examples, String binaryName) {
+ this.examplesFolder = examples;
+ this.binaryName = binaryName;
+ }
+
+ public static Path examplesJar(String examplesFolder) {
+ return Paths.get(ToolHelper.BUILD_DIR, "test", examplesFolder + ".jar");
+ }
+
+ public byte[] bytes() {
+ Path examplePath = examplesJar(examplesFolder);
+ if (!Files.exists(examplePath)) {
+ throw new RuntimeException(
+ "Could not find path "
+ + examplePath
+ + ". Build "
+ + examplesFolder
+ + " by running tools/gradle.py build"
+ + StringUtils.capitalize(examplesFolder));
+ }
+ try (ZipFile zipFile = new ZipFile(examplePath.toFile())) {
+ return ByteStreams.toByteArray(
+ zipFile.getInputStream(zipFile.getEntry(binaryName + ".class")));
+ } catch (IOException e) {
+ throw new RuntimeException("Could not read zip-entry from " + examplePath.toString(), e);
+ }
+ }
+
+ public String typeName() {
+ return DescriptorUtils.getJavaTypeFromBinaryName(binaryName);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk15/Records.java b/src/test/java/com/android/tools/r8/examples/jdk15/Records.java
new file mode 100644
index 0000000..9eafc56
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/jdk15/Records.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2020, 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.
+
+package com.android.tools.r8.examples.jdk15;
+
+import com.android.tools.r8.examples.JavaExampleClassProxy;
+import java.nio.file.Path;
+
+public class Records {
+
+ private static final String EXAMPLE_FILE = "examplesJava15/records";
+
+ public static final JavaExampleClassProxy Main =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "records/Main");
+ public static final JavaExampleClassProxy Main$Person =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "records/Main$Person");
+
+ public static Path jar() {
+ return JavaExampleClassProxy.examplesJar(EXAMPLE_FILE);
+ }
+}