Add desugared lib in command line
Bug: 142621961
Change-Id: I0f74750bbe234c2aebbbaaa06a759fc7af80ca8e
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 792f8b9..1099cf9 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -30,7 +30,8 @@
"--classpath",
"--min-api",
"--main-dex-list",
- "--main-dex-list-output");
+ "--main-dex-list-output",
+ "--desugared-lib");
private static final String APK_EXTENSION = ".apk";
private static final String JAR_EXTENSION = ".jar";
@@ -124,6 +125,8 @@
" # merging.",
" --file-per-class # Produce a separate dex file per input class",
" --no-desugaring # Force disable desugaring.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
" --main-dex-list <file> # List of classes to place in the primary dex file.",
" --main-dex-list-output <file>",
" # Output resulting main dex list in <file>.",
@@ -245,6 +248,8 @@
builder.setIntermediate(true);
} else if (arg.equals("--no-desugaring")) {
builder.setDisableDesugaring(true);
+ } else if (arg.equals("--desugared-lib")) {
+ builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
} else {
if (arg.startsWith("--")) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 50ea501..a692e1d 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.L8Command.USAGE_MESSAGE;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.dex.ApplicationReader;
@@ -15,11 +16,13 @@
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
+import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.L8TreePruner;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SelfRetraceTest;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
@@ -144,4 +147,31 @@
}
}
}
+
+ private static void run(String[] args) throws CompilationFailedException {
+ L8Command command = L8Command.parse(args, CommandLineOrigin.INSTANCE).build();
+ if (command.isPrintHelp()) {
+ SelfRetraceTest.test();
+ System.out.println(USAGE_MESSAGE);
+ return;
+ }
+ if (command.isPrintVersion()) {
+ System.out.println("L8 " + Version.getVersionString());
+ return;
+ }
+ run(command);
+ }
+
+ /**
+ * Command-line entry to L8.
+ *
+ * <p>See {@link L8Command#USAGE_MESSAGE} or run {@code l8 --help} for usage information.
+ */
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ System.err.println(USAGE_MESSAGE);
+ System.exit(ExceptionUtils.STATUS_ERROR);
+ }
+ ExceptionUtils.withMainProgramHandler(() -> run(args));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 1346623..4a587aa 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -24,9 +24,11 @@
@Keep
public final class L8Command extends BaseCompilerCommand {
+ static final String USAGE_MESSAGE = R8CommandParser.USAGE_MESSAGE;
+
private final D8Command d8Command;
private final R8Command r8Command;
- private final com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration libraryConfiguration;
+ private final DesugaredLibraryConfiguration libraryConfiguration;
private final DexItemFactory factory;
boolean isShrinking() {
@@ -41,6 +43,33 @@
return r8Command;
}
+ /**
+ * Parse the L8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return L8 command builder with state set up according to parsed command line.
+ */
+ public static Builder parse(String[] args, Origin origin) {
+ return L8CommandParser.parse(args, origin);
+ }
+
+ /**
+ * Parse the L8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return L8 command builder with state set up according to parsed command line.
+ */
+ public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return L8CommandParser.parse(args, origin, handler);
+ }
+
private L8Command(
R8Command r8Command,
D8Command d8Command,
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
new file mode 100644
index 0000000..1dc83c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -0,0 +1,164 @@
+// Copyright (c) 2019, 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;
+
+import com.android.tools.r8.D8CommandParser.OrderedClassFileResourceProvider;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Set;
+
+public class L8CommandParser extends BaseCompilerCommandParser {
+
+ private static final Set<String> OPTIONS_WITH_PARAMETER =
+ ImmutableSet.of("--output", "--lib", "--min-api", "--desugared-lib");
+
+ public static void main(String[] args) throws CompilationFailedException {
+ L8Command command = parse(args, Origin.root()).build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ L8.run(command);
+ }
+
+ static final String USAGE_MESSAGE =
+ String.join(
+ "\n",
+ Arrays.asList(
+ "Usage: l8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # Compile with debugging information (default).",
+ " --release # Compile without debugging information.",
+ " --output <file> # Output result in <outfile>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
+ " --min-api <number> # Minimum Android API level compatibility, default: "
+ + AndroidApiLevel.getDefault().getLevel()
+ + ".",
+ " --pg-conf <file> # Proguard configuration <file>.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
+ " --version # Print the version of l8.",
+ " --help # Print this message."));
+
+ /**
+ * Parse the D8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return D8 command builder with state set up according to parsed command line.
+ */
+ public static L8Command.Builder parse(String[] args, Origin origin) {
+ return parse(args, origin, L8Command.builder());
+ }
+
+ /**
+ * Parse the D8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return D8 command builder with state set up according to parsed command line.
+ */
+ public static L8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return parse(args, origin, L8Command.builder(handler));
+ }
+
+ private static L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
+ CompilationMode compilationMode = null;
+ Path outputPath = null;
+ OutputMode outputMode = null;
+ boolean hasDefinedApiLevel = false;
+ OrderedClassFileResourceProvider.Builder classpathBuilder =
+ OrderedClassFileResourceProvider.builder();
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
+ String nextArg = null;
+ if (OPTIONS_WITH_PARAMETER.contains(arg)) {
+ if (++i < expandedArgs.length) {
+ nextArg = expandedArgs[i];
+ } else {
+ builder.error(
+ new StringDiagnostic("Missing parameter for " + expandedArgs[i - 1] + ".", origin));
+ break;
+ }
+ }
+ if (arg.length() == 0) {
+ continue;
+ } else if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (compilationMode == CompilationMode.RELEASE) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.DEBUG;
+ } else if (arg.equals("--release")) {
+ if (compilationMode == CompilationMode.DEBUG) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.RELEASE;
+ } else if (arg.equals("--output")) {
+ if (outputPath != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot output both to '" + outputPath.toString() + "' and '" + nextArg + "'",
+ origin));
+ continue;
+ }
+ outputPath = Paths.get(nextArg);
+ } else if (arg.equals("--min-api")) {
+ if (hasDefinedApiLevel) {
+ builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ } else {
+ parseMinApi(builder, nextArg, origin);
+ hasDefinedApiLevel = true;
+ }
+ } else if (arg.equals("--lib")) {
+ addLibraryArgument(builder, origin, nextArg);
+ } else if (arg.equals("--pg-conf")) {
+ builder.addProguardConfigurationFiles(Paths.get(nextArg));
+ } else if (arg.equals("--desugared-lib")) {
+ builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+ } else {
+ if (arg.startsWith("--")) {
+ builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
+ continue;
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ if (!classpathBuilder.isEmpty()) {
+ builder.addClasspathResourceProvider(classpathBuilder.build());
+ }
+ if (compilationMode != null) {
+ builder.setMode(compilationMode);
+ }
+ if (outputMode == null) {
+ outputMode = OutputMode.DexIndexed;
+ }
+ if (outputPath == null) {
+ outputPath = Paths.get(".");
+ }
+ return builder.setOutput(outputPath, outputMode);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index b0acdb6..dd9ab8e 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -25,7 +25,8 @@
"--main-dex-list",
"--main-dex-list-output",
"--pg-conf",
- "--pg-map-output");
+ "--pg-map-output",
+ "--desugared-lib");
public static void main(String[] args) throws CompilationFailedException {
R8Command command = parse(args, Origin.root()).build();
@@ -65,6 +66,8 @@
+ ".",
" --pg-conf <file> # Proguard configuration <file>.",
" --pg-map-output <file> # Output the resulting name and line mapping to <file>.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
" --no-tree-shaking # Force disable tree shaking of unreachable classes.",
" --no-minification # Force disable minification of names.",
" --no-data-resources # Ignore all data resources.",
@@ -206,6 +209,8 @@
builder.addProguardConfigurationFiles(Paths.get(nextArg));
} else if (arg.equals("--pg-map-output")) {
builder.setProguardMapOutputPath(Paths.get(nextArg));
+ } else if (arg.equals("--desugared-lib")) {
+ builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
} else if (arg.equals("--no-data-resources")) {
state.includeDataResources = false;
} else {
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index f54233b..d13081c 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -76,6 +76,9 @@
case "r8":
R8.main(shift(args));
break;
+ case "l8":
+ L8.main(shift(args));
+ break;
default:
runDefault(args);
break;
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index ae63272..db0baa3 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -525,6 +525,13 @@
"Missing parameter", handler -> parse(handler, "--output"));
}
+ @Test
+ public void desugaredLibrary() throws CompilationFailedException {
+ D8Command d8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+ assertFalse(
+ d8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 0a55b8a..558ba0d 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -4,10 +4,12 @@
package com.android.tools.r8;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.charset.StandardCharsets;
@@ -68,6 +70,27 @@
Marker marker = markers.iterator().next();
}
+ @Test
+ public void testMarkerCommandLine() throws Throwable {
+ Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
+ L8Command l8Command =
+ parse(
+ ToolHelper.getDesugarJDKLibs().toString(),
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--min-api",
+ "20",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(),
+ "--output",
+ output.toString());
+ L8.run(l8Command);
+ Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
+ // TODO(b/134732760): Shouldn't we remove the D8/R8 marker?
+ assertEquals(2, markers.size());
+ Marker marker = markers.iterator().next();
+ }
+
private L8Command.Builder prepareBuilder(DiagnosticsHandler handler) {
return L8Command.builder(handler)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
@@ -156,4 +179,15 @@
assertTrue(builder2.isShrinking());
assertNotNull(builder2.build().getR8Command());
}
+
+ @Test
+ public void desugaredLibrary() throws CompilationFailedException {
+ L8Command l8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+ assertFalse(
+ l8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
+ }
+
+ private L8Command parse(String... args) throws CompilationFailedException {
+ return L8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 57d278e..165b510 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -206,7 +206,8 @@
Path mainDexRules1 = temp.newFile("main-dex-1.rules").toPath();
Path mainDexRules2 = temp.newFile("main-dex-2.rules").toPath();
parse("--main-dex-rules", mainDexRules1.toString());
- parse("--main-dex-rules", mainDexRules1.toString(), "--main-dex-rules", mainDexRules2.toString());
+ parse(
+ "--main-dex-rules", mainDexRules1.toString(), "--main-dex-rules", mainDexRules2.toString());
}
@Test(expected = CompilationFailedException.class)
@@ -637,6 +638,13 @@
"Missing parameter", handler -> parse(handler, "--output"));
}
+ @Test
+ public void desugaredLibrary() throws CompilationFailedException {
+ R8Command r8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+ assertFalse(
+ r8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
+ }
+
private R8Command parse(String... args) throws CompilationFailedException {
return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}