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); }