Merge "Revert "Add a separate tool for computing the main-dex list""
diff --git a/build.gradle b/build.gradle
index 82feb00..7362238 100644
--- a/build.gradle
+++ b/build.gradle
@@ -435,20 +435,6 @@
}
}
-task maindex(type: Jar) {
- from sourceSets.main.output
- baseName 'maindex'
- manifest {
- attributes 'Main-Class': 'com.android.tools.r8.GenerateMainDexList'
- }
- // In order to build without dependencies, pass the exclude_deps property using:
- // gradle -Pexclude_deps maindex
- if (!project.hasProperty('exclude_deps')) {
- // Also include dependencies
- from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
- }
-}
-
task ExtractMarker(type: Jar) {
from sourceSets.main.output
baseName 'extractmarker'
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index cbd8dd6..9ddbd9e 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -3,33 +3,51 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OutputMode;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
-/**
- * Base class for commands and command builders for applications/tools which take an Android
- * application (and a main-dex list) as input.
- */
abstract class BaseCommand {
private final boolean printHelp;
private final boolean printVersion;
private final AndroidApp app;
+ private final Path outputPath;
+ private final OutputMode outputMode;
+ private final CompilationMode mode;
+ private final int minApiLevel;
BaseCommand(boolean printHelp, boolean printVersion) {
this.printHelp = printHelp;
this.printVersion = printVersion;
// All other fields are initialized with stub/invalid values.
this.app = null;
+ this.outputPath = null;
+ this.outputMode = OutputMode.Indexed;
+ this.mode = null;
+ this.minApiLevel = 0;
}
- BaseCommand(AndroidApp app) {
+ BaseCommand(
+ AndroidApp app,
+ Path outputPath,
+ OutputMode outputMode,
+ CompilationMode mode,
+ int minApiLevel) {
assert app != null;
+ assert mode != null;
+ assert minApiLevel > 0;
this.app = app;
+ this.outputPath = outputPath;
+ this.outputMode = outputMode;
+ this.mode = mode;
+ this.minApiLevel = minApiLevel;
// Print options are not set.
printHelp = false;
printVersion = false;
@@ -51,27 +69,52 @@
// Internal access to the internal options.
abstract InternalOptions getInternalOptions();
- abstract public static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
+ public Path getOutputPath() {
+ return outputPath;
+ }
+
+ public CompilationMode getMode() {
+ return mode;
+ }
+
+ public int getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ public OutputMode getOutputMode() {
+ return outputMode;
+ }
+
+ abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
private boolean printHelp = false;
private boolean printVersion = false;
private final AndroidApp.Builder app;
+ private Path outputPath = null;
+ private OutputMode outputMode = OutputMode.Indexed;
+ private CompilationMode mode;
+ private int minApiLevel = Constants.DEFAULT_ANDROID_API;
- protected Builder() {
- this(AndroidApp.builder(), false);
+ // Internal flag used by CompatDx to ignore dex files in archives.
+ protected boolean ignoreDexInArchive = false;
+
+ protected Builder(CompilationMode mode) {
+ this(AndroidApp.builder(), mode, false);
}
- protected Builder(boolean ignoreDexInArchive) {
- this(AndroidApp.builder(), ignoreDexInArchive);
+ protected Builder(CompilationMode mode, boolean ignoreDexInArchive) {
+ this(AndroidApp.builder(), mode, ignoreDexInArchive);
}
// Internal constructor for testing.
Builder(AndroidApp app, CompilationMode mode) {
- this(AndroidApp.builder(app), false);
+ this(AndroidApp.builder(app), mode, false);
}
- protected Builder(AndroidApp.Builder builder, boolean ignoreDexInArchive) {
+ private Builder(AndroidApp.Builder builder, CompilationMode mode, boolean ignoreDexInArchive) {
+ assert mode != null;
this.app = builder;
+ this.mode = mode;
app.setIgnoreDexInArchive(ignoreDexInArchive);
}
@@ -138,6 +181,52 @@
return self();
}
+ /** Get current compilation mode. */
+ public CompilationMode getMode() {
+ return mode;
+ }
+
+ /** Set compilation mode. */
+ public B setMode(CompilationMode mode) {
+ assert mode != null;
+ this.mode = mode;
+ return self();
+ }
+
+ /** Get the output path. Null if not set. */
+ public Path getOutputPath() {
+ return outputPath;
+ }
+
+ /** Get the output mode. */
+ public OutputMode getOutputMode() {
+ return outputMode;
+ }
+
+ /** Set an output path. Must be an existing directory or a zip file. */
+ public B setOutputPath(Path outputPath) {
+ this.outputPath = outputPath;
+ return self();
+ }
+
+ /** Set an output mode. */
+ public B setOutputMode(OutputMode outputMode) {
+ this.outputMode = outputMode;
+ return self();
+ }
+
+ /** Get the minimum API level (aka SDK version). */
+ public int getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ /** Set the minimum required API level (aka SDK version). */
+ public B setMinApiLevel(int minApiLevel) {
+ assert minApiLevel > 0;
+ this.minApiLevel = minApiLevel;
+ return self();
+ }
+
/**
* Add main-dex list files.
*
@@ -211,7 +300,11 @@
}
protected void validate() throws CompilationException {
- // Currently does nothing.
+ if (app.hasMainDexList() && outputMode == OutputMode.FilePerClass) {
+ throw new CompilationException(
+ "Option --main-dex-list cannot be used with --file-per-class");
+ }
+ FileUtils.validateOutputFile(outputPath);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
deleted file mode 100644
index 5e8f975..0000000
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (c) 2017, 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.dex.Constants;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.OutputMode;
-import java.nio.file.Path;
-
-/**
- * Base class for commands and command builders for compiler applications/tools which besides an
- * Android application (and a main-dex list) also takes compilation output, compilation mode and
- * min API level as input.
- */
-abstract class BaseCompilerCommand extends BaseCommand {
-
- private final Path outputPath;
- private final OutputMode outputMode;
- private final CompilationMode mode;
- private final int minApiLevel;
-
- BaseCompilerCommand(boolean printHelp, boolean printVersion) {
- super(printHelp, printVersion);
-
- this.outputPath = null;
- this.outputMode = OutputMode.Indexed;
- this.mode = null;
- this.minApiLevel = 0;
- }
-
- BaseCompilerCommand(
- AndroidApp app,
- Path outputPath,
- OutputMode outputMode,
- CompilationMode mode,
- int minApiLevel) {
- super(app);
- assert mode != null;
- assert minApiLevel > 0;
- this.outputPath = outputPath;
- this.outputMode = outputMode;
- this.mode = mode;
- this.minApiLevel = minApiLevel;
- }
-
- public Path getOutputPath() {
- return outputPath;
- }
-
- public CompilationMode getMode() {
- return mode;
- }
-
- public int getMinApiLevel() {
- return minApiLevel;
- }
-
- public OutputMode getOutputMode() {
- return outputMode;
- }
-
- abstract public static class Builder<C extends BaseCompilerCommand, B extends Builder<C, B>>
- extends BaseCommand.Builder<C, B> {
-
- private Path outputPath = null;
- private OutputMode outputMode = OutputMode.Indexed;
- private CompilationMode mode;
- private int minApiLevel = Constants.DEFAULT_ANDROID_API;
-
- protected Builder(CompilationMode mode) {
- this(AndroidApp.builder(), mode, false);
- }
-
- protected Builder(CompilationMode mode, boolean ignoreDexInArchive) {
- this(AndroidApp.builder(), mode, ignoreDexInArchive);
- }
-
- // Internal constructor for testing.
- Builder(AndroidApp app, CompilationMode mode) {
- this(AndroidApp.builder(app), mode, false);
- }
-
- private Builder(AndroidApp.Builder builder, CompilationMode mode, boolean ignoreDexInArchive) {
- super(builder, ignoreDexInArchive);
- assert mode != null;
- this.mode = mode;
- }
-
- /** Get current compilation mode. */
- public CompilationMode getMode() {
- return mode;
- }
-
- /** Set compilation mode. */
- public B setMode(CompilationMode mode) {
- assert mode != null;
- this.mode = mode;
- return self();
- }
-
- /** Get the output path. Null if not set. */
- public Path getOutputPath() {
- return outputPath;
- }
-
- /** Get the output mode. */
- public OutputMode getOutputMode() {
- return outputMode;
- }
-
- /** Set an output path. Must be an existing directory or a zip file. */
- public B setOutputPath(Path outputPath) {
- this.outputPath = outputPath;
- return self();
- }
-
- /** Set an output mode. */
- public B setOutputMode(OutputMode outputMode) {
- this.outputMode = outputMode;
- return self();
- }
-
- /** Get the minimum API level (aka SDK version). */
- public int getMinApiLevel() {
- return minApiLevel;
- }
-
- /** Set the minimum required API level (aka SDK version). */
- public B setMinApiLevel(int minApiLevel) {
- assert minApiLevel > 0;
- this.minApiLevel = minApiLevel;
- return self();
- }
-
- protected void validate() throws CompilationException {
- super.validate();
- if (getAppBuilder().hasMainDexList() && outputMode == OutputMode.FilePerClass) {
- throw new CompilationException(
- "Option --main-dex-list cannot be used with --file-per-class");
- }
- FileUtils.validateOutputFile(outputPath);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 5b2e5cf..3618ab5 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -27,12 +27,12 @@
* .build();
* </pre>
*/
-public class D8Command extends BaseCompilerCommand {
+public class D8Command extends BaseCommand {
/**
* Builder for constructing a D8Command.
*/
- public static class Builder extends BaseCompilerCommand.Builder<D8Command, Builder> {
+ public static class Builder extends BaseCommand.Builder<D8Command, Builder> {
private boolean intermediate = false;
diff --git a/src/main/java/com/android/tools/r8/DexSegments.java b/src/main/java/com/android/tools/r8/DexSegments.java
index c27deb5..4e4a19b 100644
--- a/src/main/java/com/android/tools/r8/DexSegments.java
+++ b/src/main/java/com/android/tools/r8/DexSegments.java
@@ -7,9 +7,11 @@
import com.android.tools.r8.dex.Segment;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OutputMode;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Closer;
import java.io.IOException;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@@ -20,6 +22,10 @@
public static class Builder
extends BaseCommand.Builder<Command, Builder> {
+ private Builder() {
+ super(CompilationMode.RELEASE);
+ }
+
@Override
Command.Builder self() {
return this;
@@ -32,7 +38,8 @@
return new Command(isPrintHelp());
}
validate();
- return new Command(getAppBuilder().build());
+ return new Command(
+ getAppBuilder().build(), getOutputPath(), getOutputMode(), getMode(), getMinApiLevel());
}
}
@@ -70,8 +77,13 @@
}
}
- private Command(AndroidApp inputApp) {
- super(inputApp);
+ private Command(
+ AndroidApp inputApp,
+ Path outputPath,
+ OutputMode outputMode,
+ CompilationMode mode,
+ int minApiLevel) {
+ super(inputApp, outputPath, outputMode, mode, minApiLevel);
}
private Command(boolean printHelp) {
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
index bb72d5d..a7bfdc9 100644
--- a/src/main/java/com/android/tools/r8/Disassemble.java
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OutputMode;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
@@ -14,14 +15,14 @@
public class Disassemble {
public static class DisassembleCommand extends BaseCommand {
- private final Path outputPath;
-
public static class Builder
extends BaseCommand.Builder<DisassembleCommand, DisassembleCommand.Builder> {
-
- private Path outputPath = null;
private boolean useSmali = false;
+ private Builder() {
+ super(CompilationMode.RELEASE);
+ }
+
@Override
DisassembleCommand.Builder self() {
return this;
@@ -32,15 +33,6 @@
return this;
}
- public Path getOutputPath() {
- return outputPath;
- }
-
- public DisassembleCommand.Builder setOutputPath(Path outputPath) {
- this.outputPath = outputPath;
- return this;
- }
-
public DisassembleCommand.Builder setUseSmali(boolean useSmali) {
this.useSmali = useSmali;
return this;
@@ -54,7 +46,13 @@
}
validate();
- return new DisassembleCommand(getAppBuilder().build(), getOutputPath(), useSmali);
+ return new DisassembleCommand(
+ getAppBuilder().build(),
+ getOutputPath(),
+ getOutputMode(),
+ getMode(),
+ getMinApiLevel(),
+ useSmali);
}
}
@@ -95,9 +93,6 @@
builder.setUseSmali(true);
} else if (arg.equals("--pg-map")) {
builder.setProguardMapFile(Paths.get(args[++i]));
- } else if (arg.equals("--output")) {
- String outputPath = args[++i];
- builder.setOutputPath(Paths.get(outputPath));
} else {
if (arg.startsWith("--")) {
throw new CompilationException("Unknown option: " + arg);
@@ -107,22 +102,23 @@
}
}
- private DisassembleCommand(AndroidApp inputApp, Path outputPath, boolean useSmali) {
- super(inputApp);
- this.outputPath = outputPath;
+ private DisassembleCommand(
+ AndroidApp inputApp,
+ Path outputPath,
+ OutputMode outputMode,
+ CompilationMode mode,
+ int minApiLevel,
+ boolean useSmali) {
+ super(inputApp, outputPath, outputMode, mode, minApiLevel);
+ assert getOutputMode() == OutputMode.Indexed : "Only regular mode is supported in R8";
this.useSmali = useSmali;
}
private DisassembleCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
- this.outputPath = null;
this.useSmali = false;
}
- public Path getOutputPath() {
- return outputPath;
- }
-
public boolean useSmali() {
return useSmali;
}
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index d798781..0db183f 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -3,14 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.google.common.collect.ImmutableList;
+
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OutputMode;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
+
import java.io.IOException;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
@@ -20,6 +24,10 @@
public static class Builder
extends BaseCommand.Builder<ExtractMarker.Command, ExtractMarker.Command.Builder> {
+ private Builder() {
+ super(CompilationMode.RELEASE);
+ }
+
@Override
ExtractMarker.Command.Builder self() {
return this;
@@ -32,7 +40,8 @@
return new ExtractMarker.Command(isPrintHelp());
}
validate();
- return new ExtractMarker.Command(getAppBuilder().build());
+ return new ExtractMarker.Command(
+ getAppBuilder().build(), getOutputPath(), getOutputMode(), getMode(), getMinApiLevel());
}
}
@@ -70,8 +79,13 @@
}
}
- private Command(AndroidApp inputApp) {
- super(inputApp);
+ private Command(
+ AndroidApp inputApp,
+ Path outputPath,
+ OutputMode outputMode,
+ CompilationMode mode,
+ int minApiLevel) {
+ super(inputApp, outputPath, outputMode, mode, minApiLevel);
}
private Command(boolean printHelp) {
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
deleted file mode 100644
index 44d523e..0000000
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2017, 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.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.Enqueuer;
-import com.android.tools.r8.shaking.MainDexListBuilder;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.shaking.RootSetBuilder;
-import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.StandardOpenOption;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-
-public class GenerateMainDexList {
- private static final String VERSION = "v0.2.0";
- private final Timing timing = new Timing("maindex");
- private final InternalOptions options;
-
- private GenerateMainDexList(InternalOptions options) {
- this.options = options;
- }
-
- private List<String> run(AndroidApp app) throws IOException, ExecutionException {
- ExecutorService executor = ThreadUtils.getExecutorService(options);
- DexApplication application = new ApplicationReader(app, options, timing).read(executor);
- AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
- RootSet mainDexRootSet =
- new RootSetBuilder(application, appInfo, options.mainDexKeepRules).run(executor);
- Set<DexType> mainDexBaseClasses = new Enqueuer(appInfo).traceMainDex(mainDexRootSet, timing);
- Set<DexType> mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, application).run();
-
- List<String> result = mainDexClasses.stream()
- .map(c -> c.toSourceString().replace('.', '/') + ".class")
- .sorted()
- .collect(Collectors.toList());
-
- if (options.printMainDexListFile != null) {
- try (OutputStream mainDexOut = Files.newOutputStream(options.printMainDexListFile,
- StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
- PrintWriter writer = new PrintWriter(mainDexOut);
- result.forEach(writer::println);
- writer.flush();
- }
- }
-
- return result;
- }
-
- /**
- * Main API entry for computing the main-dex list.
- *
- * The main-dex list is represented as a list of strings, each string specifies one class to
- * keep in the primary dex file (<code>classes.dex</code>).
- *
- * A class is specified using the following format: "com/example/MyClass.class". That is
- * "/" as separator between package components, and a trailing ".class".
- *
- * @param command main dex-list generator command.
- * @return classes to keep in the primary dex file.
- */
- public static List<String> run(GenerateMainDexListCommand command)
- throws IOException, ExecutionException {
- ExecutorService executorService = ThreadUtils.getExecutorService(command.getInternalOptions());
- try {
- return run(command, executorService);
- } finally {
- executorService.shutdown();
- }
- }
-
- /**
- * Main API entry for computing the main-dex list.
- *
- * The main-dex list is represented as a list of strings, each string specifies one class to
- * keep in the primary dex file (<code>classes.dex</code>).
- *
- * A class is specified using the following format: "com/example/MyClass.class". That is
- * "/" as separator between package components, and a trailing ".class".
- *
- * @param command main dex-list generator command.
- * @param executor executor service from which to get threads for multi-threaded processing.
- * @return classes to keep in the primary dex file.
- */
- public static List<String> run(GenerateMainDexListCommand command, ExecutorService executor)
- throws IOException, ExecutionException {
- AndroidApp app = command.getInputApp();
- InternalOptions options = command.getInternalOptions();
- return new GenerateMainDexList(options).run(app);
- }
-
- public static void main(String[] args)
- throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
- GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.parse(args);
- GenerateMainDexListCommand command = builder.build();
- if (command.isPrintHelp()) {
- System.out.println(GenerateMainDexListCommand.USAGE_MESSAGE);
- return;
- }
- if (command.isPrintVersion()) {
- System.out.println("MainDexListGenerator " + VERSION);
- return;
- }
- List<String> result = run(command);
- if (command.getMainDexListOutputPath() == null) {
- result.forEach(System.out::println);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
deleted file mode 100644
index fb769f4..0000000
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) 2017, 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.graph.DexItemFactory;
-import com.android.tools.r8.shaking.ProguardConfigurationParser;
-import com.android.tools.r8.shaking.ProguardConfigurationRule;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class GenerateMainDexListCommand extends BaseCommand {
-
- private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
- private final Path mainDexListOutput;
- private final DexItemFactory factory;
-
- /**
- * Get the output path for the main-dex list. Null if not set.
- */
- public Path getMainDexListOutputPath() {
- return mainDexListOutput;
- }
-
- public static class Builder extends BaseCommand.Builder<GenerateMainDexListCommand, Builder> {
-
- private final DexItemFactory factory = new DexItemFactory();
- private final List<Path> mainDexRules = new ArrayList<>();
- private Path mainDexListOutput = null;
-
- @Override
- GenerateMainDexListCommand.Builder self() {
- return this;
- }
-
- /**
- * Add proguard configuration file resources for automatic main dex list calculation.
- */
- public GenerateMainDexListCommand.Builder addMainDexRules(Path... paths) {
- Collections.addAll(mainDexRules, paths);
- return self();
- }
-
- /**
- * Add proguard configuration file resources for automatic main dex list calculation.
- */
- public GenerateMainDexListCommand.Builder addMainDexRules(List<Path> paths) {
- mainDexRules.addAll(paths);
- return self();
- }
-
- /**
- * Get the output path for the main-dex list. Null if not set.
- */
- public Path getMainDexListOutputPath() {
- return mainDexListOutput;
- }
-
- /**
- * Set the output file for the main-dex list.
- *
- * If the file exists it will be overwritten.
- */
- public GenerateMainDexListCommand.Builder setMainDexListOutputPath(Path mainDexListOutputPath) {
- mainDexListOutput = mainDexListOutputPath;
- return self();
- }
-
-
- @Override
- public GenerateMainDexListCommand build() throws CompilationException, IOException {
- // If printing versions ignore everything else.
- if (isPrintHelp() || isPrintVersion()) {
- return new GenerateMainDexListCommand(isPrintHelp(), isPrintVersion());
- }
-
- validate();
- ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
- if (this.mainDexRules.isEmpty()) {
- mainDexKeepRules = ImmutableList.of();
- } else {
- ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
- try {
- parser.parse(mainDexRules);
- } catch (ProguardRuleParserException e) {
- throw new CompilationException(e.getMessage(), e.getCause());
- }
- mainDexKeepRules = parser.getConfig().getRules();
- }
-
- return new GenerateMainDexListCommand(
- factory,
- getAppBuilder().build(),
- mainDexKeepRules,
- mainDexListOutput);
- }
- }
-
- static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
- "Usage: maindex [options] <input-files>",
- " where <input-files> are JAR files",
- " and options are:",
- " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
- " # primary dex file.",
- " --main-dex-list <file> # List of classes to place in the primary dex file.",
- " --main-dex-list-output <file> # Output the full main-dex list in <file>.",
- " --version # Print the version.",
- " --help # Print this message."));
-
-
- public static GenerateMainDexListCommand.Builder builder() {
- return new GenerateMainDexListCommand.Builder();
- }
-
- public static GenerateMainDexListCommand.Builder parse(String[] args)
- throws CompilationException, IOException {
- GenerateMainDexListCommand.Builder builder = builder();
- parse(args, builder);
- return builder;
- }
-
- private static void parse(String[] args, GenerateMainDexListCommand.Builder builder)
- throws CompilationException, IOException {
- for (int i = 0; i < args.length; i++) {
- String arg = args[i].trim();
- 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("--main-dex-rules")) {
- builder.addMainDexRules(Paths.get(args[++i]));
- } else if (arg.equals("--main-dex-list")) {
- builder.addMainDexListFiles(Paths.get(args[++i]));
- } else if (arg.equals("--main-dex-list-output")) {
- builder.setMainDexListOutputPath(Paths.get(args[++i]));
- } else {
- if (arg.startsWith("--")) {
- throw new CompilationException("Unknown option: " + arg);
- }
- builder.addProgramFiles(Paths.get(arg));
- }
- }
- }
-
- private GenerateMainDexListCommand(
- DexItemFactory factory,
- AndroidApp inputApp,
- ImmutableList<ProguardConfigurationRule> mainDexKeepRules,
- Path mainDexListOutput) {
- super(inputApp);
- this.factory = factory;
- this.mainDexKeepRules = mainDexKeepRules;
- this.mainDexListOutput = mainDexListOutput;
- }
-
- private GenerateMainDexListCommand(boolean printHelp, boolean printVersion) {
- super(printHelp, printVersion);
- this.factory = new DexItemFactory();
- this.mainDexKeepRules = ImmutableList.of();
- this.mainDexListOutput = null;
- }
-
- @Override
- InternalOptions getInternalOptions() {
- InternalOptions internal = new InternalOptions(factory);
- internal.mainDexKeepRules = mainDexKeepRules;
- if (mainDexListOutput != null) {
- internal.printMainDexListFile = mainDexListOutput;
- }
- internal.minimalMainDex = internal.debug;
- internal.removeSwitchMaps = false;
- internal.inlineAccessors = false;
- return internal;
- }
-}
-
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 649b8c6..318b3e6 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -24,9 +24,9 @@
import java.util.Optional;
import java.util.function.Consumer;
-public class R8Command extends BaseCompilerCommand {
+public class R8Command extends BaseCommand {
- public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> {
+ public static class Builder extends BaseCommand.Builder<R8Command, Builder> {
private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
private Path mainDexListOutput = null;
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 4dd1880..5e7b42d 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -28,7 +28,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
@@ -573,29 +572,5 @@
return Collections
.unmodifiableMap(dependentNoShrinking.getOrDefault(item, Collections.emptyMap()));
}
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("RootSet");
-
- builder.append("\nnoShrinking: " + noShrinking.size());
- builder.append("\nnoOptimization: " + noOptimization.size());
- builder.append("\nnoObfuscation: " + noObfuscation.size());
- builder.append("\nreasonAsked: " + reasonAsked.size());
- builder.append("\nkeepPackageName: " + keepPackageName.size());
- builder.append("\ncheckDiscarded: " + checkDiscarded.size());
- builder.append("\nnoSideEffects: " + noSideEffects.size());
- builder.append("\nassumedValues: " + assumedValues.size());
- builder.append("\ndependentNoShrinking: " + dependentNoShrinking.size());
-
- builder.append("\n\nNo Shrinking:");
- noShrinking.keySet().stream()
- .sorted(Comparator.comparing(DexItem::toSourceString))
- .forEach(a -> builder
- .append("\n").append(a.toSourceString()).append(" ").append(noShrinking.get(a)));
- builder.append("\n");
- return builder.toString();
- }
}
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 3767286..93d2ee9 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -599,31 +599,13 @@
.toArray(new String[0]));
}
- public static ProcessResult forkGenerateMainDexList(Path dir, List<String> args1, String... args2)
- throws IOException, InterruptedException {
- List<String> args = new ArrayList<>();
- args.addAll(args1);
- args.addAll(Arrays.asList(args2));
- return forkJava(dir, GenerateMainDexList.class, args);
- }
-
- public static ProcessResult forkGenerateMainDexList(Path dir, String... args)
- throws IOException, InterruptedException {
- return forkJava(dir, GenerateMainDexList.class, args);
- }
-
private static ProcessResult forkJava(Path dir, Class clazz, String... args)
throws IOException, InterruptedException {
- return forkJava(dir, clazz, Arrays.asList(args));
- }
-
- private static ProcessResult forkJava(Path dir, Class clazz, List<String> args)
- throws IOException, InterruptedException {
List<String> command = new ImmutableList.Builder<String>()
.add(getJavaExecutable())
.add("-cp").add(System.getProperty("java.class.path"))
.add(clazz.getCanonicalName())
- .addAll(args)
+ .addAll(Arrays.asList(args))
.build();
return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 79c605c..c7e0c18 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -9,18 +9,16 @@
import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
import com.android.tools.r8.CompilationResult;
-import com.android.tools.r8.GenerateMainDexList;
-import com.android.tools.r8.GenerateMainDexListCommand;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -109,6 +107,7 @@
mainDexRules,
expectedMainDexList,
minSdk,
+ R8Command.builder(),
(options) -> {
options.inlineAccessors = false;
});
@@ -121,86 +120,48 @@
Path mainDexRules,
Path expectedMainDexList,
int minSdk,
+ R8Command.Builder builder,
Consumer<InternalOptions> optionsConsumer)
throws Throwable {
Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
Path inputJar = Paths.get(buildDir, packageName + JAR_EXTENSION);
+ builder.setMinApiLevel(minSdk);
try {
- // Build main-dex list using GenerateMainDexList.
- GenerateMainDexListCommand.Builder mdlCommandBuilder = GenerateMainDexListCommand.builder();
- GenerateMainDexListCommand command2 = mdlCommandBuilder
+ R8Command command = builder
.addProgramFiles(inputJar)
- .addProgramFiles(Paths.get(EXAMPLE_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION))
- .addMainDexRules(mainDexRules)
- .build();
- List<String> mainDexGeneratorMainDexList =
- GenerateMainDexList.run(command2).stream()
- .map(this::mainDexStringToDescriptor)
- .sorted()
- .collect(Collectors.toList());
-
- // Build main-dex list using R8.
- R8Command.Builder r8CommandBuilder = R8Command.builder();
- R8Command command = r8CommandBuilder
- .setMinApiLevel(minSdk)
- .addProgramFiles(inputJar)
- .addProgramFiles(Paths.get(EXAMPLE_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION))
- .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+ .addLibraryFiles(Paths.get(EXAMPLE_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION),
+ Paths.get(ToolHelper.getAndroidJar(minSdk)))
.setOutputPath(out)
.addMainDexRulesFiles(mainDexRules)
.build();
CompilationResult result = ToolHelper.runR8WithFullResult(command, optionsConsumer);
- List<String> r8MainDexList =
+ List<String> resultMainDexList =
result.dexApplication.mainDexList.stream()
.filter(dexType -> isApplicationClass(dexType, result))
.map(dexType -> dexType.descriptor.toString())
- .sorted()
.collect(Collectors.toList());
-
- // Check that both generated lists are the same as the reference list, except for lambda
- // classes which are only produced when running R8.
+ Collections.sort(resultMainDexList);
String[] refList = new String(Files.readAllBytes(
expectedMainDexList), StandardCharsets.UTF_8).split("\n");
- int nonLambdaOffset = 0;
for (int i = 0; i < refList.length; i++) {
String reference = refList[i].trim();
- checkSameMainDexEntry(reference, r8MainDexList.get(i));
- // The main dex list generator does not do any lambda desugaring.
- if (!isLambda(reference)) {
- checkSameMainDexEntry(reference, mainDexGeneratorMainDexList.get(i - nonLambdaOffset));
- } else {
- nonLambdaOffset++;
+ String computed = resultMainDexList.get(i);
+ if (reference.contains("-$$Lambda$")) {
+ // For lambda classes we check that there is a lambda class for the right containing
+ // class. However, we do not check the hash for the generated lambda class. The hash
+ // changes for different compiler versions because different compiler versions generate
+ // different lambda implementation method names.
+ reference = reference.substring(0, reference.lastIndexOf('$'));
+ computed = computed.substring(0, computed.lastIndexOf('$'));
}
+ Assert.assertEquals(reference, computed);
}
} catch (ExecutionException e) {
throw e.getCause();
}
}
- private boolean isLambda(String mainDexEntry) {
- return mainDexEntry.contains("-$$Lambda$");
- }
-
- private String mainDexStringToDescriptor(String mainDexString) {
- final String CLASS_EXTENSION = ".class";
- Assert.assertTrue(mainDexString.endsWith(CLASS_EXTENSION));
- return DescriptorUtils.getDescriptorFromClassBinaryName(
- mainDexString.substring(0, mainDexString.length() - CLASS_EXTENSION.length()));
- }
-
- private void checkSameMainDexEntry(String reference, String computed) {
- if (isLambda(reference)) {
- // For lambda classes we check that there is a lambda class for the right containing
- // class. However, we do not check the hash for the generated lambda class. The hash
- // changes for different compiler versions because different compiler versions generate
- // different lambda implementation method names.
- reference = reference.substring(0, reference.lastIndexOf('$'));
- computed = computed.substring(0, computed.lastIndexOf('$'));
- }
- Assert.assertEquals(reference, computed);
- }
-
private boolean isApplicationClass(DexType dexType, CompilationResult result) {
DexClass clazz = result.appInfo.definitionFor(dexType);
return clazz != null && clazz.isProgramClass();
diff --git a/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java b/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
deleted file mode 100644
index 64e7a5b..0000000
--- a/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) 2017, 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.utils;
-
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.GenerateMainDexListCommand;
-import com.android.tools.r8.ToolHelper;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
-import java.util.ArrayList;
-import java.util.List;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-
-public class GenerateMainDexListCommandTest {
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @Rule
- public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
- @Test
- public void emptyCommand() throws Throwable {
- verifyEmptyCommand(GenerateMainDexListCommand.builder().build());
- verifyEmptyCommand(parse());
- verifyEmptyCommand(parse(""));
- verifyEmptyCommand(parse("", ""));
- verifyEmptyCommand(parse(" "));
- verifyEmptyCommand(parse(" ", " "));
- verifyEmptyCommand(parse("\t"));
- verifyEmptyCommand(parse("\t", "\t"));
- }
-
- private void verifyEmptyCommand(GenerateMainDexListCommand command) throws IOException {
- assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
- assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
- assertFalse(ToolHelper.getApp(command).hasMainDexListResources());
- }
-
- // Add the jars used in the com.android.tools.r8.maindexlist.MainDexTracingTest test.
- private void addInputJarsToCommandLine(List<String> args) {
- args.add(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "multidex001" + JAR_EXTENSION)
- .toAbsolutePath().toString());
- args.add(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION)
- .toAbsolutePath().toString());
- }
-
- // Add main-dex rules used in the com.android.tools.r8.maindexlist.MainDexTracingTest test.
- private void addMainDexRuleToCommandLine(List<String> args) {
- args.add("--main-dex-rules");
- args.add(Paths.get(ToolHelper.EXAMPLES_DIR, "multidex", "main-dex-rules.txt")
- .toAbsolutePath().toString());
- }
-
- @Test
- public void defaultOutIsCwd() throws Throwable {
- Path working = temp.getRoot().toPath();
- String mainDexListOutput = "main-dex-list.txt";
- Path output = working.resolve(mainDexListOutput);
- assertFalse(Files.exists(output));
- List<String> args = new ArrayList<>();
- addInputJarsToCommandLine(args);
- addMainDexRuleToCommandLine(args);
- assertEquals(0, ToolHelper.forkGenerateMainDexList(
- working, args, "--main-dex-list-output", mainDexListOutput).exitCode);
- assertTrue(Files.exists(output));
- assertTrue(Files.size(output) > 0);
- }
-
- @Test
- public void validOutputPath() throws Throwable {
- Path existingFile = temp.getRoot().toPath().resolve("existing_output");
- try (OutputStream existingFileOut = Files.newOutputStream(existingFile,
- StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
- PrintWriter writer = new PrintWriter(existingFileOut);
- writer.println("Hello, world!");
- writer.flush();
- }
- Path nonExistingFile = temp.getRoot().toPath().resolve("non_existing_output");
- assertEquals(
- existingFile,
- GenerateMainDexListCommand.builder().setMainDexListOutputPath(existingFile).build()
- .getMainDexListOutputPath());
- assertEquals(
- nonExistingFile,
- GenerateMainDexListCommand.builder().setMainDexListOutputPath(nonExistingFile).build()
- .getMainDexListOutputPath());
- assertEquals(
- existingFile,
- parse("--main-dex-list-output", existingFile.toString()).getMainDexListOutputPath());
- assertEquals(
- nonExistingFile,
- parse("--main-dex-list-output", nonExistingFile.toString()).getMainDexListOutputPath());
- }
-
- @Test
- public void nonExistingOutputFileInNonExistingDir() throws Throwable {
- Path nonExistingFileInNonExistingDir =
- temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
- assertEquals(
- nonExistingFileInNonExistingDir,
- GenerateMainDexListCommand.builder()
- .setMainDexListOutputPath(nonExistingFileInNonExistingDir).build()
- .getMainDexListOutputPath());
- assertEquals(
- nonExistingFileInNonExistingDir,
- parse("--main-dex-list-output",
- nonExistingFileInNonExistingDir.toString()).getMainDexListOutputPath());
- }
-
- @Test
- public void mainDexRules() throws Throwable {
- 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());
- }
-
- @Test
- public void mainDexList() throws Throwable {
- Path mainDexList1 = temp.newFile("main-dex-list-1.txt").toPath();
- Path mainDexList2 = temp.newFile("main-dex-list-2.txt").toPath();
- parse("--main-dex-list", mainDexList1.toString());
- parse("--main-dex-list", mainDexList1.toString(), "--main-dex-list", mainDexList2.toString());
- }
-
- private GenerateMainDexListCommand parse(String... args) throws Throwable {
- return GenerateMainDexListCommand.parse(args).build();
- }
-}