Add D8 based desugar command
Skeleton D8 desugar command taking the same arguments as Desugar together with simple test which invoke it directly.
RELNOTES: None.
PiperOrigin-RevId: 306993018
diff --git a/src/BUILD b/src/BUILD
index 42fe065..f2a1bae 100644
--- a/src/BUILD
+++ b/src/BUILD
@@ -161,7 +161,7 @@
# WARNING: Only adjust the number in `expect` if you are intentionally
# adding or removing embedded tools. Know that the more embedded tools there
# are in Bazel, the bigger the binary becomes and the slower Bazel starts.
- expect = 470,
+ expect = 500,
margin = 5, # percentage
)
diff --git a/src/test/java/com/google/devtools/build/android/r8/desugar/DesugarBasicTest.java b/src/test/java/com/google/devtools/build/android/r8/desugar/DesugarBasicTest.java
new file mode 100644
index 0000000..df4ca9d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/r8/desugar/DesugarBasicTest.java
@@ -0,0 +1,91 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.android.r8.desugar;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.objectweb.asm.Opcodes.V1_7;
+
+import com.google.devtools.build.android.r8.FileUtils;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.function.Consumer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+
+/** Basic test of D8 desugar */
+@RunWith(JUnit4.class)
+public class DesugarBasicTest {
+ private Path basic;
+ private Path desugared;
+
+ @Before
+ public void setup() {
+ // Jar file with the compiled Java code in the sub-package basic before desugaring.
+ basic = Paths.get(System.getProperty("DesugarBasicTest.testdata_basic"));
+ // Jar file with the compiled Java code in the sub-package basic after desugaring.
+ desugared = Paths.get(System.getProperty("DesugarBasicTest.testdata_basic_desugared"));
+ }
+
+ @Test
+ public void checkBeforeDesugar() throws Exception {
+ DesugarInfoCollector desugarInfoCollector = new DesugarInfoCollector();
+ forAllClasses(basic, desugarInfoCollector);
+ assertThat(desugarInfoCollector.getLargestMajorClassFileVersion()).isGreaterThan(V1_7);
+ assertThat(desugarInfoCollector.getNumberOfInvokeDynamic()).isGreaterThan(0);
+ assertThat(desugarInfoCollector.getNumberOfDefaultMethods()).isGreaterThan(0);
+ assertThat(desugarInfoCollector.getNumberOfDesugaredLambdas()).isEqualTo(0);
+ assertThat(desugarInfoCollector.getNumberOfCompanionClasses()).isEqualTo(0);
+ }
+
+ @Test
+ public void checkAfterDesugar() throws Exception {
+ DesugarInfoCollector desugarInfoCollector = new DesugarInfoCollector();
+ forAllClasses(desugared, desugarInfoCollector);
+ // TODO(b/153971249): The class file version of desugared class files should be Java 7.
+ // assertThat(lambdaUse.getMajorCfVersion()).isEqualTo(V1_7);
+ assertThat(desugarInfoCollector.getNumberOfInvokeDynamic()).isEqualTo(0);
+ assertThat(desugarInfoCollector.getNumberOfDefaultMethods()).isEqualTo(0);
+ assertThat(desugarInfoCollector.getNumberOfDesugaredLambdas()).isEqualTo(1);
+ assertThat(desugarInfoCollector.getNumberOfCompanionClasses()).isEqualTo(1);
+ }
+
+ private static void forAllClasses(Path jar, ClassVisitor classVisitor) throws Exception {
+ forAllClasses(
+ jar,
+ classReader ->
+ classReader.accept(classVisitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES));
+ }
+
+ private static void forAllClasses(Path jar, Consumer<ClassReader> classReader) throws Exception {
+
+ try (JarInputStream jarInputStream =
+ new JarInputStream(Files.newInputStream(jar, StandardOpenOption.READ))) {
+ JarEntry entry;
+ while ((entry = jarInputStream.getNextJarEntry()) != null) {
+ String entryName = entry.getName();
+ if (FileUtils.isClassFile(entryName)) {
+ classReader.accept(new ClassReader(jarInputStream));
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/android/r8/desugar/DesugarInfoCollector.java b/src/test/java/com/google/devtools/build/android/r8/desugar/DesugarInfoCollector.java
new file mode 100644
index 0000000..d60fabb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/r8/desugar/DesugarInfoCollector.java
@@ -0,0 +1,132 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.android.r8.desugar;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
+import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
+import static org.objectweb.asm.Opcodes.ASM7;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * ASM visitor to collect summary information from class files for checking that desugaring has been
+ * applied
+ */
+public class DesugarInfoCollector extends ClassVisitor {
+ private int largestClassFileVersion;
+ private int numberOfInvokeDynamic;
+ private int numberOfDefaultMethods;
+ private int numberOfDesugaredLambdas;
+ private int numberOfCompanionClasses;
+
+ private int currentClassAccess;
+
+ public DesugarInfoCollector() {
+ this(null);
+ }
+
+ public DesugarInfoCollector(ClassVisitor classVisitor) {
+ super(ASM7, classVisitor);
+ }
+
+ public int getNumberOfInvokeDynamic() {
+ return numberOfInvokeDynamic;
+ }
+
+ public int getNumberOfDefaultMethods() {
+ return numberOfDefaultMethods;
+ }
+
+ public int getLargestMajorClassFileVersion() {
+ return computeMajorClassFileVersion(largestClassFileVersion);
+ }
+
+ public int getNumberOfDesugaredLambdas() {
+ return numberOfDesugaredLambdas;
+ }
+
+ public int getNumberOfCompanionClasses() {
+ return numberOfCompanionClasses;
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ java.lang.String name,
+ java.lang.String signature,
+ java.lang.String superName,
+ java.lang.String[] interfaces) {
+ super.visit(version, access, name, signature, superName, interfaces);
+ assertThat(computeMinorClassFileVersion(version)).isEqualTo(0);
+ largestClassFileVersion = Math.max(version, largestClassFileVersion);
+ if (classNameFromBinaryName(name).startsWith("-$$Lambda$")) {
+ numberOfDesugaredLambdas++;
+ }
+ if (name.endsWith("$-CC")) {
+ numberOfCompanionClasses++;
+ }
+ currentClassAccess = access;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
+ if (isInterface(currentClassAccess) && !isAbstract(access)) {
+ numberOfDefaultMethods++;
+ }
+ return new LambdaUseMethodVisitor(api, mv);
+ }
+
+ private class LambdaUseMethodVisitor extends MethodVisitor {
+
+ LambdaUseMethodVisitor(int api, MethodVisitor methodVisitor) {
+ super(api, methodVisitor);
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+ super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+ numberOfInvokeDynamic++;
+ }
+ }
+
+ private static int computeMinorClassFileVersion(int version) {
+ return (version >> 16) & 0xffff;
+ }
+
+ private static int computeMajorClassFileVersion(int version) {
+ return version & 0xffff;
+ }
+
+ private static boolean isAbstract(int access) {
+ return (access & ACC_ABSTRACT) != 0;
+ }
+
+ private static boolean isInterface(int access) {
+ return (access & ACC_INTERFACE) != 0;
+ }
+
+ private static String classNameFromBinaryName(String name) {
+ int index = name.lastIndexOf('/');
+ if (index == -1) {
+ return name;
+ }
+ return name.substring(index + 1);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/android/r8/desugar/basic/A.java b/src/test/java/com/google/devtools/build/android/r8/desugar/basic/A.java
new file mode 100644
index 0000000..fe729c2
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/r8/desugar/basic/A.java
@@ -0,0 +1,16 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.android.r8.desugar.basic;
+
+class A implements I {}
diff --git a/src/test/java/com/google/devtools/build/android/r8/desugar/basic/I.java b/src/test/java/com/google/devtools/build/android/r8/desugar/basic/I.java
new file mode 100644
index 0000000..febb3cc
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/r8/desugar/basic/I.java
@@ -0,0 +1,20 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.android.r8.desugar.basic;
+
+interface I {
+ default void foo() {
+ System.out.println("I::foo");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/android/r8/desugar/basic/TestClass.java b/src/test/java/com/google/devtools/build/android/r8/desugar/basic/TestClass.java
new file mode 100644
index 0000000..4ccb66a
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/r8/desugar/basic/TestClass.java
@@ -0,0 +1,29 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.android.r8.desugar.basic;
+
+class TestClass {
+
+ public static void main(String[] args) {
+ Runnable runnable =
+ () -> {
+ System.out.println("Hello, world!");
+ };
+ runnable.run();
+
+ new A().foo();
+ }
+
+ private TestClass() {}
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/r8/Desugar.java
new file mode 100644
index 0000000..6da2d60
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/r8/Desugar.java
@@ -0,0 +1,426 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.android.r8;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.OutputMode;
+import com.google.devtools.build.android.Converters.ExistingPathConverter;
+import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionDocumentationCategory;
+import com.google.devtools.common.options.OptionEffectTag;
+import com.google.devtools.common.options.OptionMetadataTag;
+import com.google.devtools.common.options.OptionsBase;
+import com.google.devtools.common.options.OptionsParser;
+import com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.util.List;
+
+/** Desugar compatible wrapper based on D8 desugaring engine */
+public class Desugar {
+ /** Commandline options for {@link com.google.devtools.build.android.r8.Desugar}. */
+ public static class DesugarOptions extends OptionsBase {
+
+ @Option(
+ name = "input",
+ allowMultiple = true,
+ defaultValue = "",
+ category = "input",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ converter = ExistingPathConverter.class,
+ abbrev = 'i',
+ help =
+ "Input Jar or directory with classes to desugar (required, the n-th input is paired"
+ + " with the n-th output).")
+ public List<Path> inputJars;
+
+ @Option(
+ name = "classpath_entry",
+ allowMultiple = true,
+ defaultValue = "",
+ category = "input",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ converter = ExistingPathConverter.class,
+ help =
+ "Ordered classpath (Jar or directory) to resolve symbols in the --input Jar, like "
+ + "javac's -cp flag.")
+ public List<Path> classpath;
+
+ @Option(
+ name = "bootclasspath_entry",
+ allowMultiple = true,
+ defaultValue = "",
+ category = "input",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ converter = ExistingPathConverter.class,
+ help =
+ "Bootclasspath that was used to compile the --input Jar with, like javac's "
+ + "-bootclasspath flag (required).")
+ public List<Path> bootclasspath;
+
+ @Option(
+ name = "allow_empty_bootclasspath",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN})
+ public boolean allowEmptyBootclasspath;
+
+ @Option(
+ name = "only_desugar_javac9_for_lint",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "A temporary flag specifically for android lint, subject to removal anytime (DO NOT"
+ + " USE)")
+ public boolean onlyDesugarJavac9ForLint;
+
+ @Option(
+ name = "rewrite_calls_to_long_compare",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Rewrite calls to Long.compare(long, long) to the JVM instruction lcmp "
+ + "regardless of --min_sdk_version.",
+ category = "misc")
+ public boolean alwaysRewriteLongCompare;
+
+ @Option(
+ name = "output",
+ allowMultiple = true,
+ defaultValue = "",
+ category = "output",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ converter = PathConverter.class,
+ abbrev = 'o',
+ help =
+ "Output Jar or directory to write desugared classes into (required, the n-th output is "
+ + "paired with the n-th input, output must be a Jar if input is a Jar).")
+ public List<Path> outputJars;
+
+ @Option(
+ name = "verbose",
+ defaultValue = "false",
+ category = "misc",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ abbrev = 'v',
+ help = "Enables verbose debugging output.")
+ public boolean verbose;
+
+ @Option(
+ name = "min_sdk_version",
+ defaultValue = "1",
+ category = "misc",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Minimum targeted sdk version. If >= 24, enables default methods in interfaces.")
+ public int minSdkVersion;
+
+ @Option(
+ name = "emit_dependency_metadata_as_needed",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Whether to emit META-INF/desugar_deps as needed for later consistency checking.")
+ public boolean emitDependencyMetadata;
+
+ @Option(
+ name = "best_effort_tolerate_missing_deps",
+ defaultValue = "true",
+ category = "misc",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Whether to tolerate missing dependencies on the classpath in some cases. You should "
+ + "strive to set this flag to false.")
+ public boolean tolerateMissingDependencies;
+
+ @Option(
+ name = "desugar_supported_core_libs",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Enable core library desugaring, which requires configuration with related flags.")
+ public boolean desugarCoreLibs;
+
+ @Option(
+ name = "desugar_interface_method_bodies_if_needed",
+ defaultValue = "true",
+ category = "misc",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Rewrites default and static methods in interfaces if --min_sdk_version < 24. This "
+ + "only works correctly if subclasses of rewritten interfaces as well as uses of "
+ + "static interface methods are run through this tool as well.")
+ public boolean desugarInterfaceMethodBodiesIfNeeded;
+
+ @Option(
+ name = "desugar_try_with_resources_if_needed",
+ defaultValue = "true",
+ category = "misc",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Rewrites try-with-resources statements if --min_sdk_version < 19.")
+ public boolean desugarTryWithResourcesIfNeeded;
+
+ @Option(
+ name = "desugar_try_with_resources_omit_runtime_classes",
+ defaultValue = "false",
+ category = "misc",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Omits the runtime classes necessary to support try-with-resources from the output."
+ + " This property has effect only if --desugar_try_with_resources_if_needed is"
+ + " used.")
+ public boolean desugarTryWithResourcesOmitRuntimeClasses;
+
+ @Option(
+ name = "generate_base_classes_for_default_methods",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "If desugaring default methods, generate abstract base classes for them. "
+ + "This reduces default method stubs in hand-written subclasses.")
+ public boolean generateBaseClassesForDefaultMethods;
+
+ @Option(
+ name = "copy_bridges_from_classpath",
+ defaultValue = "false",
+ category = "misc",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Copy bridges from classpath to desugared classes.")
+ public boolean copyBridgesFromClasspath;
+
+ @Option(
+ name = "core_library",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Enables rewriting to desugar java.* classes.")
+ public boolean coreLibrary;
+
+ /** Type prefixes that we'll move to a custom package. */
+ @Option(
+ name = "rewrite_core_library_prefix",
+ defaultValue = "", // ignored
+ allowMultiple = true,
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Assume the given java.* prefixes are desugared.")
+ public List<String> rewriteCoreLibraryPrefixes;
+
+ /** Interfaces whose default and static interface methods we'll emulate. */
+ @Option(
+ name = "emulate_core_library_interface",
+ defaultValue = "", // ignored
+ allowMultiple = true,
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Assume the given java.* interfaces are emulated.")
+ public List<String> emulateCoreLibraryInterfaces;
+
+ /** Members that we will retarget to the given new owner. */
+ @Option(
+ name = "retarget_core_library_member",
+ defaultValue = "", // ignored
+ allowMultiple = true,
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Method invocations to retarget, given as \"class/Name#member->new/class/Name\". "
+ + "The new owner is blindly assumed to exist.")
+ public List<String> retargetCoreLibraryMembers;
+
+ /** Members not to rewrite. */
+ @Option(
+ name = "dont_rewrite_core_library_invocation",
+ defaultValue = "", // ignored
+ allowMultiple = true,
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Method invocations not to rewrite, given as \"class/Name#method\".")
+ public List<String> dontTouchCoreLibraryMembers;
+
+ @Option(
+ name = "preserve_core_library_override",
+ defaultValue = "", // ignored
+ allowMultiple = true,
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Core library methods given as \"class/Name#method\" whose overrides should be"
+ + " preserved. Typically this is useful when the given class itself isn't"
+ + " desugared.")
+ public List<String> preserveCoreLibraryOverrides;
+
+ /** Set to work around b/62623509 with JaCoCo versions prior to 0.7.9. */
+ // TODO(kmb): Remove when Android Studio doesn't need it anymore (see b/37116789)
+ @Option(
+ name = "legacy_jacoco_fix",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Consider setting this flag if you're using JaCoCo versions prior to 0.7.9 to work"
+ + " around issues with coverage instrumentation in default and static interface"
+ + " methods. This flag may be removed when no longer needed.")
+ public boolean legacyJacocoFix;
+
+ /** Convert Java 11 nest-based access control to bridge-based access control. */
+ @Option(
+ name = "desugar_nest_based_private_access",
+ defaultValue = "true",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Desugar JVM 11 native supported accessing private nest members with bridge method"
+ + " based accessors. This flag includes desugaring private interface methods.")
+ public boolean desugarNestBasedPrivateAccess;
+
+ /**
+ * Convert Java 9 invokedynamic-based string concatenations to StringBuilder-based
+ * concatenations. @see https://openjdk.java.net/jeps/280
+ */
+ @Option(
+ name = "desugar_indy_string_concat",
+ defaultValue = "true",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Desugar JVM 9 string concatenation operations to string builder based"
+ + " implementations.")
+ public boolean desugarIndifyStringConcat;
+
+ @Option(
+ name = "persistent_worker",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ metadataTags = {OptionMetadataTag.HIDDEN},
+ help = "Run as a Bazel persistent worker.")
+ public boolean persistentWorker;
+ }
+
+ private static DesugarOptions parseCommandLineOptions(String[] args) {
+ OptionsParser parser =
+ OptionsParser.builder()
+ .optionsClasses(DesugarOptions.class)
+ .allowResidue(false)
+ .argsPreProcessor(new ShellQuotedParamsFilePreProcessor(FileSystems.getDefault()))
+ .build();
+ parser.parseAndExitUponError(args);
+ DesugarOptions options = parser.getOptions(DesugarOptions.class);
+
+ return options;
+ }
+
+ private static void desugar(DesugarOptions options) throws CompilationFailedException {
+ D8.run(
+ D8Command.builder()
+ .addLibraryFiles(options.bootclasspath)
+ .addProgramFiles(options.inputJars)
+ .setMinApiLevel(options.minSdkVersion)
+ .setOutput(options.outputJars.get(0), OutputMode.ClassFile)
+ .build());
+ }
+
+ private static void validateOptions(DesugarOptions options) {
+ if (options.allowEmptyBootclasspath) {
+ throw new AssertionError("--allow_empty_bootclasspath is not supported");
+ }
+ if (options.onlyDesugarJavac9ForLint) {
+ throw new AssertionError("--only_desugar_javac9_for_lint is not supported");
+ }
+ if (options.alwaysRewriteLongCompare) {
+ throw new AssertionError("--rewrite_calls_to_long_compare has no effect");
+ }
+ if (options.emitDependencyMetadata) {
+ throw new AssertionError("--emit_dependency_metadata_as_needed is not supported");
+ }
+ if (!options.tolerateMissingDependencies) {
+ throw new AssertionError("--best_effort_tolerate_missing_deps must be enabled");
+ }
+ if (options.desugarCoreLibs) {
+ throw new AssertionError("--desugar_supported_core_libs is not supported");
+ }
+ if (!options.desugarInterfaceMethodBodiesIfNeeded) {
+ throw new AssertionError("--desugar_interface_method_bodies_if_needed must be enabled");
+ }
+ if (!options.desugarTryWithResourcesIfNeeded) {
+ throw new AssertionError("--desugar_try_with_resources_if_needed must be enabled");
+ }
+ if (options.desugarTryWithResourcesOmitRuntimeClasses) {
+ throw new AssertionError(
+ "--desugar_try_with_resources_omit_runtime_classes is not supported");
+ }
+ if (options.generateBaseClassesForDefaultMethods) {
+ throw new AssertionError("--generate_base_classes_for_default_methods is not supported");
+ }
+ if (options.copyBridgesFromClasspath) {
+ throw new AssertionError("--copy_bridges_from_classpath is not supported");
+ }
+ if (options.coreLibrary) {
+ throw new AssertionError("--core_library is not supported");
+ }
+ if (!options.rewriteCoreLibraryPrefixes.isEmpty()) {
+ throw new AssertionError("--rewrite_core_library_prefix is not supported");
+ }
+ if (!options.emulateCoreLibraryInterfaces.isEmpty()) {
+ throw new AssertionError("--emulate_core_library_interface is not supported");
+ }
+ if (!options.retargetCoreLibraryMembers.isEmpty()) {
+ throw new AssertionError("--retarget_core_library_member is not supported");
+ }
+ if (!options.dontTouchCoreLibraryMembers.isEmpty()) {
+ throw new AssertionError("--dont_rewrite_core_library_invocation is not supported");
+ }
+ if (!options.preserveCoreLibraryOverrides.isEmpty()) {
+ throw new AssertionError("--preserve_core_library_override is not supported");
+ }
+ if (options.legacyJacocoFix) {
+ throw new AssertionError("--legacy_jacoco_fix is not supported");
+ }
+ if (!options.desugarNestBasedPrivateAccess) {
+ throw new AssertionError("--desugar_nest_based_private_access must be enabled");
+ }
+ if (!options.desugarIndifyStringConcat) {
+ throw new AssertionError("--desugar_indy_string_concat must be enabled");
+ }
+ if (options.persistentWorker) {
+ throw new AssertionError("--persistent_worker is not supported");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ DesugarOptions options = parseCommandLineOptions(args);
+ validateOptions(options);
+
+ desugar(options);
+ }
+
+ private Desugar() {}
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/FileUtils.java b/src/tools/android/java/com/google/devtools/build/android/r8/FileUtils.java
index e54cfe0..51296e0 100644
--- a/src/tools/android/java/com/google/devtools/build/android/r8/FileUtils.java
+++ b/src/tools/android/java/com/google/devtools/build/android/r8/FileUtils.java
@@ -16,7 +16,8 @@
import com.google.common.base.Ascii;
import java.nio.file.Path;
-class FileUtils {
+/** File related utilities */
+public class FileUtils {
public static final String AAR_EXTENSION = ".aar";
public static final String APK_EXTENSION = ".apk";
public static final String CLASS_EXTENSION = ".class";
@@ -34,11 +35,11 @@
return hasExtension(path.getFileName().toString(), extension);
}
- static boolean isDexFile(Path path) {
+ public static boolean isDexFile(Path path) {
return hasExtension(path, DEX_EXTENSION);
}
- static boolean isClassFile(String name) {
+ public static boolean isClassFile(String name) {
name = Ascii.toLowerCase(name);
// Android does not support Java 9 module, thus skip module-info.
if (name.equals(MODULE_INFO_CLASS)) {
@@ -50,43 +51,43 @@
return name.endsWith(CLASS_EXTENSION);
}
- static boolean isClassFile(Path path) {
+ public static boolean isClassFile(Path path) {
return isClassFile(path.getFileName().toString());
}
- static boolean isJarFile(String name) {
+ public static boolean isJarFile(String name) {
return hasExtension(name, JAR_EXTENSION);
}
- static boolean isJarFile(Path path) {
+ public static boolean isJarFile(Path path) {
return hasExtension(path, JAR_EXTENSION);
}
- static boolean isZipFile(String name) {
+ public static boolean isZipFile(String name) {
return hasExtension(name, ZIP_EXTENSION);
}
- static boolean isZipFile(Path path) {
+ public static boolean isZipFile(Path path) {
return hasExtension(path, ZIP_EXTENSION);
}
- static boolean isApkFile(String name) {
+ public static boolean isApkFile(String name) {
return hasExtension(name, APK_EXTENSION);
}
- static boolean isApkFile(Path path) {
+ public static boolean isApkFile(Path path) {
return hasExtension(path, APK_EXTENSION);
}
- static boolean isAarFile(String name) {
+ public static boolean isAarFile(String name) {
return hasExtension(name, AAR_EXTENSION);
}
- static boolean isAarFile(Path path) {
+ public static boolean isAarFile(Path path) {
return hasExtension(path, AAR_EXTENSION);
}
- static boolean isArchive(Path path) {
+ public static boolean isArchive(Path path) {
return isApkFile(path) || isJarFile(path) || isZipFile(path) || isAarFile(path);
}