Merge commit '6d188632' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8Logger.java b/src/main/java/com/android/tools/r8/D8Logger.java
deleted file mode 100644
index 6c3fca0..0000000
--- a/src/main/java/com/android/tools/r8/D8Logger.java
+++ /dev/null
@@ -1,52 +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.compatdx.CompatDx;
-import com.google.common.collect.ImmutableList;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.Arrays;
-
-public final class D8Logger {
-
- private static final int STATUS_ERROR = 1;
-
- private static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
- "Usage: java -jar d8logger.jar <compiler-options>",
- " where <compiler-options> will be",
- "",
- " 1. forwarded to the 'd8' or 'compatdx' tool (depending on the presence of the '--dex'",
- " option), and also",
- " 2. appended to the file in the environment variable 'D8LOGGER_OUTPUT'",
- "",
- " The options will be appended as a new line with TAB characters between the arguments."));
-
- public static void main(String[] args) throws IOException {
- if (args.length == 0) {
- System.err.println(USAGE_MESSAGE);
- System.exit(STATUS_ERROR);
- }
- String output = System.getenv("D8LOGGER_OUTPUT");
- if (output == null) {
- throw new IOException("D8Logger: D8LOGGER_OUTPUT environment variable must be set.");
- }
-
- if (output.length() > 0) {
- String[] absArgs = Arrays.stream(args)
- .map(s -> s.startsWith("-") ? s : Paths.get(s).toAbsolutePath().toString())
- .toArray(String[]::new);
- try (FileWriter fw = new FileWriter(output, true)) {
- fw.write(String.join("\t", absArgs) + System.lineSeparator());
- }
- }
-
- if (Arrays.stream(args).anyMatch(s -> s.equals("--dex"))) {
- CompatDx.main(args);
- } else {
- D8.main(args);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index 6bf5af9..f079277 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -4,9 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.bisect.Bisect;
-import com.android.tools.r8.compatdx.CompatDx;
import com.android.tools.r8.compatproguard.CompatProguard;
-import com.android.tools.r8.dexfilemerger.DexFileMerger;
import com.android.tools.r8.dexsplitter.DexSplitter;
import com.android.tools.r8.relocator.RelocatorCommandLine;
import java.util.Arrays;
@@ -32,21 +30,12 @@
case "bisect":
Bisect.main(shift(args));
break;
- case "compatdx":
- CompatDx.main(shift(args));
- break;
case "compatproguard":
CompatProguard.main(shift(args));
break;
case "d8":
D8.main(shift(args));
break;
- case "d8logger":
- D8Logger.main(shift(args));
- break;
- case "dexfilemerger":
- DexFileMerger.main(shift(args));
- break;
case "dexsegments":
DexSegments.main(shift(args));
break;
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
deleted file mode 100644
index 69cbddb..0000000
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ /dev/null
@@ -1,205 +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.compatdexbuilder;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.CompatDxHelper;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.origin.ArchiveEntryOrigin;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.io.ByteStreams;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.zip.CRC32;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
-
-public class CompatDexBuilder {
-
- private static class DexConsumer extends DexIndexedConsumer.ForwardingConsumer {
-
- byte[] bytes;
-
- public DexConsumer() {
- super(null);
- }
-
- @Override
- public synchronized void accept(
- int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
- super.accept(fileIndex, data, descriptors, handler);
- assert bytes == null;
- bytes = data.copyByteData();
- }
-
- byte[] getBytes() {
- return bytes;
- }
- }
-
- private String input = null;
- private String output = null;
- private int numberOfThreads = 8;
- private boolean noLocals = false;
- private boolean backportStatics = false;
-
- public static void main(String[] args)
- throws IOException, InterruptedException, ExecutionException {
- new CompatDexBuilder().run(args);
- }
-
- private void run(String[] args) throws IOException, InterruptedException, ExecutionException {
- List<String> flags = new ArrayList<>();
-
- for (String arg : args) {
- if (arg.startsWith("@")) {
- flags.addAll(Files.readAllLines(Paths.get(arg.substring(1))));
- } else {
- flags.add(arg);
- }
- }
-
- for (int i = 0; i < flags.size(); i++) {
- String flag = flags.get(i);
- if (flag.startsWith("--positions=")) {
- String positionsValue = flag.substring("--positions=".length());
- if (positionsValue.startsWith("throwing") || positionsValue.startsWith("important")) {
- noLocals = true;
- }
- continue;
- }
- if (flag.startsWith("--num-threads=")) {
- numberOfThreads = Integer.parseInt(flag.substring("--num-threads=".length()));
- continue;
- }
- switch (flag) {
- case "--input_jar":
- input = flags.get(++i);
- break;
- case "--output_zip":
- output = flags.get(++i);
- break;
- case "--verify-dex-file":
- case "--no-verify-dex-file":
- case "--show_flags":
- case "--no-optimize":
- case "--nooptimize":
- case "--help":
- // Ignore
- break;
- case "--nolocals":
- noLocals = true;
- break;
- case "--desugar-backport-statics":
- backportStatics = true;
- break;
- default:
- System.err.println("Unsupported option: " + flag);
- System.exit(1);
- }
- }
-
- if (input == null) {
- System.err.println("No input jar specified");
- System.exit(1);
- }
-
- if (output == null) {
- System.err.println("No output jar specified");
- System.exit(1);
- }
-
- ExecutorService executor = ThreadUtils.getExecutorService(numberOfThreads);
- try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(Paths.get(output)))) {
-
- List<ZipEntry> toDex = new ArrayList<>();
-
- try (ZipFile zipFile = new ZipFile(input, StandardCharsets.UTF_8)) {
- final Enumeration<? extends ZipEntry> entries = zipFile.entries();
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- if (!entry.getName().endsWith(".class")) {
- try (InputStream stream = zipFile.getInputStream(entry)) {
- addEntry(entry.getName(), stream, out);
- }
- } else {
- toDex.add(entry);
- }
- }
-
- List<Future<DexConsumer>> futures = new ArrayList<>(toDex.size());
- for (int i = 0; i < toDex.size(); i++) {
- ZipEntry classEntry = toDex.get(i);
- futures.add(executor.submit(() -> dexEntry(zipFile, classEntry, executor)));
- }
- for (int i = 0; i < futures.size(); i++) {
- ZipEntry entry = toDex.get(i);
- DexConsumer consumer = futures.get(i).get();
- addEntry(entry.getName() + ".dex", consumer.getBytes(), out);
- }
- }
- } finally {
- executor.shutdown();
- }
- }
-
- private DexConsumer dexEntry(ZipFile zipFile, ZipEntry classEntry, ExecutorService executor)
- throws IOException, CompilationFailedException {
- DexConsumer consumer = new DexConsumer();
- D8Command.Builder builder = D8Command.builder();
- CompatDxHelper.ignoreDexInArchive(builder);
- builder
- .setProgramConsumer(consumer)
- .setMode(noLocals ? CompilationMode.RELEASE : CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.H_MR2.getLevel())
- .setDisableDesugaring(true);
- if (backportStatics) {
- CompatDxHelper.enableDesugarBackportStatics(builder);
- }
- try (InputStream stream = zipFile.getInputStream(classEntry)) {
- builder.addClassProgramData(
- ByteStreams.toByteArray(stream),
- new ArchiveEntryOrigin(
- classEntry.getName(), new PathOrigin(Paths.get(zipFile.getName()))));
- }
- D8.run(builder.build(), executor);
- return consumer;
- }
-
- private static void addEntry(String name, InputStream stream, ZipOutputStream out)
- throws IOException {
- addEntry(name, ByteStreams.toByteArray(stream), out);
- }
-
- private static void addEntry(String name, byte[] bytes, ZipOutputStream out) throws IOException {
- ZipEntry zipEntry = new ZipEntry(name);
- CRC32 crc32 = new CRC32();
- crc32.update(bytes);
- zipEntry.setSize(bytes.length);
- zipEntry.setMethod(ZipEntry.STORED);
- zipEntry.setCrc(crc32.getValue());
- zipEntry.setTime(0);
- out.putNextEntry(zipEntry);
- out.write(bytes);
- out.closeEntry();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
deleted file mode 100644
index e98d5c0..0000000
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ /dev/null
@@ -1,626 +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.compatdx;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.CompatDxHelper;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.Version;
-import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.DxUsageMessage;
-import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.PositionInfo;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.ZipUtils;
-import com.google.common.collect.ImmutableList;
-import com.google.common.io.ByteStreams;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.nio.charset.StandardCharsets;
-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.Enumeration;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import joptsimple.OptionParser;
-import joptsimple.OptionSet;
-import joptsimple.OptionSpec;
-
-/**
- * Dx compatibility interface for d8.
- *
- * This should become a mostly drop-in replacement for uses of the DX dexer (eg, dx --dex ...).
- */
-public class CompatDx {
-
- private static final String USAGE_HEADER = "Usage: compatdx [options] <input files>";
-
- /**
- * Compatibility options parsing for the DX --dex sub-command.
- */
- public static class DxCompatOptions {
- // Final values after parsing.
- // Note: These are ordered by their occurrence in "dx --help"
- public final boolean help;
- public final boolean version;
- public final boolean debug;
- public final boolean verbose;
- public final PositionInfo positions;
- public final boolean noLocals;
- public final boolean noOptimize;
- public final boolean statistics;
- public final String optimizeList;
- public final String noOptimizeList;
- public final boolean noStrict;
- public final boolean keepClasses;
- public final String output;
- public final String dumpTo;
- public final int dumpWidth;
- public final String dumpMethod;
- public final boolean verboseDump;
- public final boolean dump;
- public final boolean noFiles;
- public final boolean coreLibrary;
- public final int numThreads;
- public final boolean incremental;
- public final boolean forceJumbo;
- public final boolean noWarning;
- public final boolean multiDex;
- public final String mainDexList;
- public final boolean minimalMainDex;
- public final int minApiLevel;
- public final boolean backportStatics;
- public final String inputList;
- public final ImmutableList<String> inputs;
- // Undocumented option
- public final int maxIndexNumber;
-
- private static final String FILE_ARG = "file";
- private static final String NUM_ARG = "number";
- private static final String METHOD_ARG = "method";
-
- public enum PositionInfo {
- NONE, IMPORTANT, LINES, THROWING
- }
-
- // Exception thrown on invalid dx compat usage.
- public static class DxUsageMessage extends Exception {
- public final String message;
-
- DxUsageMessage(String message) {
- this.message = message;
- }
-
- void printHelpOn(PrintStream sink) throws IOException {
- sink.println(message);
- }
- }
-
- // Parsing specification.
- private static class Spec {
- final OptionParser parser;
-
- // Note: These are ordered by their occurrence in "dx --help"
- final OptionSpec<Void> debug;
- final OptionSpec<Void> verbose;
- final OptionSpec<String> positions;
- final OptionSpec<Void> noLocals;
- final OptionSpec<Void> noOptimize;
- final OptionSpec<Void> statistics;
- final OptionSpec<String> optimizeList;
- final OptionSpec<String> noOptimizeList;
- final OptionSpec<Void> noStrict;
- final OptionSpec<Void> keepClasses;
- final OptionSpec<String> output;
- final OptionSpec<String> dumpTo;
- final OptionSpec<Integer> dumpWidth;
- final OptionSpec<String> dumpMethod;
- final OptionSpec<Void> dump;
- final OptionSpec<Void> verboseDump;
- final OptionSpec<Void> noFiles;
- final OptionSpec<Void> coreLibrary;
- final OptionSpec<Integer> numThreads;
- final OptionSpec<Void> incremental;
- final OptionSpec<Void> forceJumbo;
- final OptionSpec<Void> noWarning;
- final OptionSpec<Void> multiDex;
- final OptionSpec<String> mainDexList;
- final OptionSpec<Void> minimalMainDex;
- final OptionSpec<Integer> minApiLevel;
- final OptionSpec<Void> backportStatics;
- final OptionSpec<String> inputList;
- final OptionSpec<String> inputs;
- final OptionSpec<Void> version;
- final OptionSpec<Void> help;
- final OptionSpec<Integer> maxIndexNumber;
-
- Spec() {
- parser = new OptionParser();
- parser.accepts("dex");
- debug = parser.accepts("debug", "Print debug information");
- verbose = parser.accepts("verbose", "Print verbose information");
- positions = parser
- .accepts("positions",
- "What source-position information to keep. One of: none, lines, important")
- .withOptionalArg()
- .describedAs("keep")
- .defaultsTo("lines");
- noLocals = parser.accepts("no-locals", "Don't keep local variable information");
- statistics = parser.accepts("statistics", "Print statistics information");
- noOptimize = parser.accepts("no-optimize", "Don't optimize");
- optimizeList = parser
- .accepts("optimize-list", "File listing methods to optimize")
- .withRequiredArg()
- .describedAs(FILE_ARG);
- noOptimizeList = parser
- .accepts("no-optimize-list", "File listing methods not to optimize")
- .withRequiredArg()
- .describedAs(FILE_ARG);
- noStrict = parser.accepts("no-strict", "Disable strict file/class name checks");
- keepClasses = parser.accepts("keep-classes", "Keep input class files in in output jar");
- output = parser
- .accepts("output", "Output file or directory")
- .withRequiredArg()
- .describedAs(FILE_ARG);
- dumpTo = parser
- .accepts("dump-to", "File to dump information to")
- .withRequiredArg()
- .describedAs(FILE_ARG);
- dumpWidth = parser
- .accepts("dump-width", "Max width for columns in dump output")
- .withRequiredArg()
- .ofType(Integer.class)
- .defaultsTo(0)
- .describedAs(NUM_ARG);
- dumpMethod = parser
- .accepts("dump-method", "Method to dump information for")
- .withRequiredArg()
- .describedAs(METHOD_ARG);
- dump = parser.accepts("dump", "Dump information");
- verboseDump = parser.accepts("verbose-dump", "Dump verbose information");
- noFiles = parser.accepts("no-files", "Don't fail if given no files");
- coreLibrary = parser.accepts("core-library", "Construct a core library");
- numThreads = parser
- .accepts("num-threads", "Number of threads to run with")
- .withRequiredArg()
- .ofType(Integer.class)
- .defaultsTo(1)
- .describedAs(NUM_ARG);
- incremental = parser.accepts("incremental", "Merge result with the output if it exists");
- forceJumbo = parser.accepts("force-jumbo", "Force use of string-jumbo instructions");
- noWarning = parser.accepts("no-warning", "Suppress warnings");
- maxIndexNumber = parser.accepts("set-max-idx-number",
- "Undocumented: Set maximal index number to use in a dex file.")
- .withRequiredArg()
- .ofType(Integer.class)
- .defaultsTo(0)
- .describedAs("Maximum index");
- minimalMainDex = parser.accepts("minimal-main-dex", "Produce smallest possible main dex");
- mainDexList = parser
- .accepts("main-dex-list", "File listing classes that must be in the main dex file")
- .withRequiredArg()
- .describedAs(FILE_ARG);
- multiDex =
- parser
- .accepts("multi-dex", "Allow generation of multi-dex")
- .requiredIf(minimalMainDex, mainDexList, maxIndexNumber);
- minApiLevel = parser
- .accepts("min-sdk-version", "Minimum Android API level compatibility.")
- .withRequiredArg().ofType(Integer.class);
- backportStatics =
- parser.accepts("desugar-backport-statics", "Backport additional Java 8 APIs");
- inputList = parser
- .accepts("input-list", "File listing input files")
- .withRequiredArg()
- .describedAs(FILE_ARG);
- inputs = parser.nonOptions("Input files");
- version = parser.accepts("version", "Print the version of this tool").forHelp();
- help = parser.accepts("help", "Print this message").forHelp();
- }
- }
-
- private DxCompatOptions(OptionSet options, Spec spec) {
- help = options.has(spec.help);
- version = options.has(spec.version);
- debug = options.has(spec.debug);
- verbose = options.has(spec.verbose);
- if (options.has(spec.positions)) {
- switch (options.valueOf(spec.positions)) {
- case "none":
- positions = PositionInfo.NONE;
- break;
- case "important":
- positions = PositionInfo.IMPORTANT;
- break;
- case "lines":
- positions = PositionInfo.LINES;
- break;
- case "throwing":
- positions = PositionInfo.THROWING;
- break;
- default:
- positions = PositionInfo.IMPORTANT;
- break;
- }
- } else {
- positions = PositionInfo.LINES;
- }
- noLocals = options.has(spec.noLocals);
- noOptimize = options.has(spec.noOptimize);
- statistics = options.has(spec.statistics);
- optimizeList = options.valueOf(spec.optimizeList);
- noOptimizeList = options.valueOf(spec.noOptimizeList);
- noStrict = options.has(spec.noStrict);
- keepClasses = options.has(spec.keepClasses);
- output = options.valueOf(spec.output);
- dumpTo = options.valueOf(spec.dumpTo);
- dumpWidth = options.valueOf(spec.dumpWidth);
- dumpMethod = options.valueOf(spec.dumpMethod);
- dump = options.has(spec.dump);
- verboseDump = options.has(spec.verboseDump);
- noFiles = options.has(spec.noFiles);
- coreLibrary = options.has(spec.coreLibrary);
- numThreads = lastIntOf(options.valuesOf(spec.numThreads));
- incremental = options.has(spec.incremental);
- forceJumbo = options.has(spec.forceJumbo);
- noWarning = options.has(spec.noWarning);
- multiDex = options.has(spec.multiDex);
- mainDexList = options.valueOf(spec.mainDexList);
- minimalMainDex = options.has(spec.minimalMainDex);
- if (options.has(spec.minApiLevel)) {
- List<Integer> allMinApiLevels = options.valuesOf(spec.minApiLevel);
- minApiLevel = allMinApiLevels.get(allMinApiLevels.size() - 1);
- } else {
- minApiLevel = AndroidApiLevel.getDefault().getLevel();
- }
- backportStatics = options.has(spec.backportStatics);
- inputList = options.valueOf(spec.inputList);
- inputs = ImmutableList.copyOf(options.valuesOf(spec.inputs));
- maxIndexNumber = options.valueOf(spec.maxIndexNumber);
- }
-
- public static DxCompatOptions parse(String[] args) {
- Spec spec = new Spec();
- return new DxCompatOptions(spec.parser.parse(args), spec);
- }
-
- private static int lastIntOf(List<Integer> values) {
- assert !values.isEmpty();
- return values.get(values.size() - 1);
- }
- }
-
- public static void main(String[] args) throws IOException {
- try {
- run(args);
- } catch (DxUsageMessage e) {
- System.err.println(USAGE_HEADER);
- e.printHelpOn(System.err);
- System.exit(1);
- } catch (CompilationFailedException e) {
- System.exit(1);
- }
- }
-
- private static void run(String[] args)
- throws DxUsageMessage, IOException, CompilationFailedException {
- DxCompatOptions dexArgs = DxCompatOptions.parse(args);
- if (dexArgs.help) {
- printHelpOn(System.out);
- return;
- }
- if (dexArgs.version) {
- System.out.println("CompatDx " + Version.getVersionString());
- return;
- }
- CompilationMode mode = CompilationMode.RELEASE;
- Path output = null;
- List<Path> inputs = new ArrayList<>();
- boolean singleDexFile = !dexArgs.multiDex;
- Path mainDexList = null;
- int numberOfThreads = 1;
-
- for (String path : dexArgs.inputs) {
- processPath(new File(path), inputs);
- }
- if (inputs.isEmpty()) {
- if (dexArgs.noFiles) {
- return;
- }
- throw new DxUsageMessage("No input files specified");
- }
-
- if (!Log.ENABLED && dexArgs.debug) {
- System.out.println("Warning: logging is not enabled for this build.");
- }
-
- if (dexArgs.dump && dexArgs.verbose) {
- System.out.println("Warning: dump is not supported");
- }
-
- if (dexArgs.verboseDump) {
- throw new Unimplemented("verbose dump file not yet supported");
- }
-
- if (dexArgs.dumpMethod != null) {
- throw new Unimplemented("method-dump not yet supported");
- }
-
- if (dexArgs.output != null) {
- output = Paths.get(dexArgs.output);
- if (FileUtils.isDexFile(output)) {
- if (!singleDexFile) {
- throw new DxUsageMessage("Cannot output to a single dex-file when running with multidex");
- }
- } else if (!FileUtils.isArchive(output)
- && (!output.toFile().exists() || !output.toFile().isDirectory())) {
- throw new DxUsageMessage("Unsupported output file or output directory does not exist. "
- + "Output must be a directory or a file of type dex, apk, jar or zip.");
- }
- }
-
- if (dexArgs.dumpTo != null && dexArgs.verbose) {
- System.out.println("dump-to file not yet supported");
- }
-
- if (dexArgs.positions == PositionInfo.NONE && dexArgs.verbose) {
- System.out.println("Warning: no support for positions none.");
- }
-
- if (dexArgs.positions == PositionInfo.LINES && !dexArgs.noLocals) {
- mode = CompilationMode.DEBUG;
- }
-
- if (dexArgs.incremental) {
- throw new Unimplemented("incremental merge not supported yet");
- }
-
- if (dexArgs.forceJumbo && dexArgs.verbose) {
- System.out.println(
- "Warning: no support for forcing jumbo-strings.\n"
- + "Strings will only use jumbo-string indexing if necessary.\n"
- + "Make sure that any dex merger subsequently used "
- + "supports correct handling of jumbo-strings (eg, D8/R8 does).");
- }
-
- if (dexArgs.noOptimize && dexArgs.verbose) {
- System.out.println("Warning: no support for not optimizing");
- }
-
- if (dexArgs.optimizeList != null) {
- throw new Unimplemented("no support for optimize-method list");
- }
-
- if (dexArgs.noOptimizeList != null) {
- throw new Unimplemented("no support for dont-optimize-method list");
- }
-
- if (dexArgs.statistics && dexArgs.verbose) {
- System.out.println("Warning: no support for printing statistics");
- }
-
- if (dexArgs.numThreads > 1) {
- numberOfThreads = dexArgs.numThreads;
- }
-
- if (dexArgs.mainDexList != null) {
- mainDexList = Paths.get(dexArgs.mainDexList);
- }
-
- if (dexArgs.noStrict) {
- if (dexArgs.verbose) {
- System.out.println("Warning: conservative main-dex list not yet supported");
- }
- } else {
- if (dexArgs.verbose) {
- System.out.println("Warning: strict name checking not yet supported");
- }
- }
-
- if (dexArgs.minimalMainDex && dexArgs.verbose) {
- System.out.println("Warning: minimal main-dex support is not yet supported");
- }
-
- if (dexArgs.maxIndexNumber != 0 && dexArgs.verbose) {
- System.out.println("Warning: internal maximum-index setting is not supported");
- }
-
- if (numberOfThreads < 1) {
- throw new DxUsageMessage("Invalid numThreads value of " + numberOfThreads);
- }
- ExecutorService executor = ThreadUtils.getExecutorService(numberOfThreads);
-
- try {
- D8Command.Builder builder = D8Command.builder();
- CompatDxHelper.ignoreDexInArchive(builder);
- builder
- .addProgramFiles(inputs)
- .setProgramConsumer(createConsumer(inputs, output, singleDexFile, dexArgs.keepClasses))
- .setMode(mode)
- .setDisableDesugaring(true) // DX does not desugar.
- .setMinApiLevel(dexArgs.minApiLevel);
- if (mainDexList != null) {
- builder.addMainDexListFiles(mainDexList);
- }
- if (dexArgs.backportStatics) {
- CompatDxHelper.enableDesugarBackportStatics(builder);
- }
- CompatDxHelper.run(builder.build(), dexArgs.minimalMainDex);
- } finally {
- executor.shutdown();
- }
- }
-
- private static ProgramConsumer createConsumer(
- List<Path> inputs, Path output, boolean singleDexFile, boolean keepClasses)
- throws DxUsageMessage {
- if (output == null) {
- return DexIndexedConsumer.emptyConsumer();
- }
- if (singleDexFile) {
- return new SingleDexFileConsumer(
- FileUtils.isDexFile(output)
- ? new NamedDexFileConsumer(output)
- : createDexConsumer(output, inputs, keepClasses));
- }
- return createDexConsumer(output, inputs, keepClasses);
- }
-
- private static DexIndexedConsumer createDexConsumer(
- Path output, List<Path> inputs, boolean keepClasses)
- throws DxUsageMessage {
- if (keepClasses) {
- if (!FileUtils.isArchive(output)) {
- throw new DxCompatOptions.DxUsageMessage(
- "Output must be an archive when --keep-classes is set.");
- }
- return new DexKeepClassesConsumer(output, inputs);
- }
- return FileUtils.isArchive(output)
- ? new DexIndexedConsumer.ArchiveConsumer(output)
- : new DexIndexedConsumer.DirectoryConsumer(output);
- }
-
- private static class SingleDexFileConsumer extends DexIndexedConsumer.ForwardingConsumer {
-
- private byte[] bytes = null;
-
- public SingleDexFileConsumer(DexIndexedConsumer consumer) {
- super(consumer);
- }
-
- @Override
- public void accept(
- int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
- if (fileIndex > 0) {
- throw new CompilationError(
- "Compilation result could not fit into a single dex file. "
- + "Reduce the input-program size or run with --multi-dex enabled");
- }
- assert bytes == null;
- // Store a copy of the bytes as we may not assume the backing is valid after accept returns.
- bytes = data.copyByteData();
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {
- if (bytes != null) {
- super.accept(0, ByteDataView.of(bytes), null, handler);
- }
- super.finished(handler);
- }
- }
-
- private static class NamedDexFileConsumer extends DexIndexedConsumer.ForwardingConsumer {
- private final Path output;
-
- public NamedDexFileConsumer(Path output) {
- super(null);
- this.output = output;
- }
-
- @Override
- public void accept(
- int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
- StandardOpenOption[] options = {
- StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING
- };
- try (OutputStream stream = new BufferedOutputStream(Files.newOutputStream(output, options))) {
- stream.write(data.getBuffer(), data.getOffset(), data.getLength());
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, new PathOrigin(output)));
- }
- }
- }
-
- private static class DexKeepClassesConsumer extends DexIndexedConsumer.ArchiveConsumer {
-
- private final List<Path> inputs;
-
- public DexKeepClassesConsumer(Path archive, List<Path> inputs) {
- super(archive);
- this.inputs = inputs;
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {
- try {
- writeZipWithClasses(handler);
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, getOrigin()));
- }
- super.finished(handler);
- }
-
- private void writeZipWithClasses(DiagnosticsHandler handler) throws IOException {
- // For each input archive file, add all class files within.
- for (Path input : inputs) {
- if (FileUtils.isArchive(input)) {
- try (ZipFile zipFile = FileUtils.createZipFile(input.toFile(), StandardCharsets.UTF_8)) {
- final Enumeration<? extends ZipEntry> entries = zipFile.entries();
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- if (ZipUtils.isClassFile(entry.getName())) {
- try (InputStream entryStream = zipFile.getInputStream(entry)) {
- byte[] bytes = ByteStreams.toByteArray(entryStream);
- outputBuilder.addFile(entry.getName(), ByteDataView.of(bytes), handler);
- }
- }
- }
- }
- }
- }
- }
- }
-
- static void printHelpOn(PrintStream sink) throws IOException {
- sink.println(USAGE_HEADER);
- new DxCompatOptions.Spec().parser.printHelpOn(sink);
- }
-
- private static void processPath(File file, List<Path> files) {
- if (!file.exists()) {
- throw new CompilationError("File does not exist: " + file);
- }
- if (file.isDirectory()) {
- processDirectory(file, files);
- return;
- }
- Path path = file.toPath();
- if (FileUtils.isZipFile(path) || FileUtils.isJarFile(path) || FileUtils.isClassFile(path)) {
- files.add(path);
- return;
- }
- if (FileUtils.isApkFile(path)) {
- throw new Unimplemented("apk files not yet supported");
- }
- }
-
- private static void processDirectory(File directory, List<Path> files) {
- assert directory.exists();
- for (File file : directory.listFiles()) {
- processPath(file, files);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
deleted file mode 100644
index 7cbf816..0000000
--- a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
+++ /dev/null
@@ -1,403 +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.dexfilemerger;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.DexFileMergerHelper;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.OptionsParsing;
-import com.android.tools.r8.utils.OptionsParsing.ParseContext;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.ZipUtils;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-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.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-public class DexFileMerger {
- /** File name prefix of a {@code .dex} file automatically loaded in an archive. */
- private static final String DEX_PREFIX = "classes";
-
- private static final String DEFAULT_OUTPUT_ARCHIVE_FILENAME = "classes.dex.jar";
-
- private static final boolean PRINT_ARGS = false;
-
- /** Strategies for outputting multiple {@code .dex} files supported by {@link DexFileMerger}. */
- private enum MultidexStrategy {
- /** Create exactly one .dex file. The operation will fail if .dex limits are exceeded. */
- OFF,
- /** Create exactly one <prefixN>.dex file with N taken from the (single) input archive. */
- GIVEN_SHARD,
- /**
- * Assemble .dex files similar to {@link com.android.dx.command.dexer.Main dx}, with all but one
- * file as large as possible.
- */
- MINIMAL,
- /**
- * Allow some leeway and sometimes use additional .dex files to speed up processing. This option
- * exists to give flexibility but it often (or always) may be identical to {@link #MINIMAL}.
- */
- BEST_EFFORT;
-
- public boolean isMultidexAllowed() {
- switch (this) {
- case OFF:
- case GIVEN_SHARD:
- return false;
- case MINIMAL:
- case BEST_EFFORT:
- return true;
- }
- throw new AssertionError("Unknown: " + this);
- }
-
- public static MultidexStrategy parse(String value) {
- switch (value) {
- case "off":
- return OFF;
- case "given_shard":
- return GIVEN_SHARD;
- case "minimal":
- return MINIMAL;
- case "best_effort":
- return BEST_EFFORT;
- default:
- throw new RuntimeException(
- "Multidex argument must be either 'off', 'given_shard', 'minimal' or 'best_effort'.");
- }
- }
- }
-
- private static class Options {
- List<String> inputArchives = new ArrayList<>();
- String outputArchive = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
- MultidexStrategy multidexMode = MultidexStrategy.OFF;
- String mainDexListFile = null;
- boolean minimalMainDex = false;
- boolean verbose = false;
- String dexPrefix = DEX_PREFIX;
- }
-
-
- private static Options parseArguments(String[] args) throws IOException {
- // We may have a single argument which is a parameter file path, prefixed with '@'.
- if (args.length == 1 && args[0].startsWith("@")) {
- // TODO(tamaskenez) Implement more sophisticated processing
- // which is aligned with Blaze's
- // com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor
- Path paramsFile = Paths.get(args[0].substring(1));
- List<String> argsList = new ArrayList<>();
- for (String s : Files.readAllLines(paramsFile)) {
- s = s.trim();
- if (s.isEmpty()) {
- continue;
- }
- // Trim optional enclosing single quotes. Unescaping omitted for now.
- if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
- s = s.substring(1, s.length() - 1);
- }
- argsList.add(s);
- }
- args = argsList.toArray(new String[argsList.size()]);
- }
-
- Options options = new Options();
- ParseContext context = new ParseContext(args);
- List<String> strings;
- String string;
- Boolean b;
- while (context.head() != null) {
- if (context.head().startsWith("@")) {
- throw new RuntimeException("A params file must be the only argument: " + context.head());
- }
- strings = OptionsParsing.tryParseMulti(context, "--input");
- if (strings != null) {
- options.inputArchives.addAll(strings);
- continue;
- }
- string = OptionsParsing.tryParseSingle(context, "--output", "-o");
- if (string != null) {
- options.outputArchive = string;
- continue;
- }
- string = OptionsParsing.tryParseSingle(context, "--multidex", null);
- if (string != null) {
- options.multidexMode = MultidexStrategy.parse(string);
- continue;
- }
- string = OptionsParsing.tryParseSingle(context, "--main-dex-list", null);
- if (string != null) {
- options.mainDexListFile = string;
- continue;
- }
- b = OptionsParsing.tryParseBoolean(context, "--minimal-main-dex");
- if (b != null) {
- options.minimalMainDex = b;
- continue;
- }
- b = OptionsParsing.tryParseBoolean(context, "--verbose");
- if (b != null) {
- options.verbose = b;
- continue;
- }
- string = OptionsParsing.tryParseSingle(context, "--max-bytes-wasted-per-file", null);
- if (string != null) {
- System.err.println("Warning: '--max-bytes-wasted-per-file' is ignored.");
- continue;
- }
- string = OptionsParsing.tryParseSingle(context, "--set-max-idx-number", null);
- if (string != null) {
- System.err.println("Warning: The '--set-max-idx-number' option is ignored.");
- continue;
- }
- b = OptionsParsing.tryParseBoolean(context, "--forceJumbo");
- if (b != null) {
- System.err.println(
- "Warning: '--forceJumbo' can be safely omitted. Strings will only use "
- + "jumbo-string indexing if necessary.");
- continue;
- }
- string = OptionsParsing.tryParseSingle(context, "--dex_prefix", null);
- if (string != null) {
- options.dexPrefix = string;
- continue;
- }
- throw new RuntimeException(String.format("Unknown options: '%s'.", context.head()));
- }
- return options;
- }
-
- /**
- * Implements a DexIndexedConsumer writing into a ZipStream with support for custom dex file name
- * prefix, reindexing a single dex output file to a nonzero index and reporting if any data has
- * been written.
- */
- private static class ArchiveConsumer implements DexIndexedConsumer {
- private final Path path;
- private final String prefix;
- private final Integer singleFixedFileIndex;
- private final Origin origin;
- private ZipOutputStream stream = null;
-
- private int highestIndexWritten = -1;
- private final Map<Integer, Runnable> writers = new TreeMap<>();
- private boolean hasWrittenSomething = false;
-
- /** If singleFixedFileIndex is not null then we expect only one output dex file */
- private ArchiveConsumer(Path path, String prefix, Integer singleFixedFileIndex) {
- this.path = path;
- this.prefix = prefix;
- this.singleFixedFileIndex = singleFixedFileIndex;
- this.origin = new PathOrigin(path);
- }
-
- private boolean hasWrittenSomething() {
- return hasWrittenSomething;
- }
-
- private String getDexFileName(int fileIndex) {
- if (singleFixedFileIndex != null) {
- fileIndex = singleFixedFileIndex;
- }
- return prefix + (fileIndex == 0 ? "" : (fileIndex + 1)) + FileUtils.DEX_EXTENSION;
- }
-
- @Override
- public synchronized void accept(
- int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
- if (singleFixedFileIndex != null && fileIndex != 0) {
- handler.error(new StringDiagnostic("Result does not fit into a single dex file."));
- return;
- }
- // Make a copy of the actual bytes as they will possibly be accessed later by the runner.
- final byte[] bytes = data.copyByteData();
- writers.put(fileIndex, () -> writeEntry(fileIndex, bytes, descriptors, handler));
-
- while (writers.containsKey(highestIndexWritten + 1)) {
- ++highestIndexWritten;
- writers.get(highestIndexWritten).run();
- writers.remove(highestIndexWritten);
- }
- }
-
- /** Get or open the zip output stream. */
- private synchronized ZipOutputStream getStream(DiagnosticsHandler handler) {
- if (stream == null) {
- try {
- stream =
- new ZipOutputStream(
- Files.newOutputStream(
- path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, origin));
- }
- }
- return stream;
- }
-
- private void writeEntry(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
- try {
- ZipUtils.writeToZipStream(
- getStream(handler),
- getDexFileName(fileIndex),
- ByteDataView.of(data),
- ZipEntry.DEFLATED);
- hasWrittenSomething = true;
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, origin));
- }
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {
- if (!writers.isEmpty()) {
- handler.error(
- new StringDiagnostic(
- "Failed to write zip, for a multidex output some of the classes.dex files were"
- + " not produced."));
- }
- try {
- if (stream != null) {
- stream.close();
- stream = null;
- }
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, origin));
- }
- }
- }
-
- private static int parseFileIndexFromShardFilename(String inputArchive) {
- Pattern namingPattern = Pattern.compile("([0-9]+)\\..*");
- String name = new File(inputArchive).getName();
- Matcher matcher = namingPattern.matcher(name);
- if (!matcher.matches()) {
- throw new RuntimeException(
- String.format(
- "Expect input named <N>.xxx.zip for --multidex=given_shard but got %s.", name));
- }
- int shard = Integer.parseInt(matcher.group(1));
- if (shard <= 0) {
- throw new RuntimeException(
- String.format("Expect positive N in input named <N>.xxx.zip but got %d.", shard));
- }
- return shard;
- }
-
- public static void run(String[] args) throws CompilationFailedException, IOException {
- Options options = parseArguments(args);
-
- if (options.inputArchives.isEmpty()) {
- throw new RuntimeException("Need at least one --input");
- }
-
- if (options.mainDexListFile != null && options.inputArchives.size() != 1) {
- throw new RuntimeException(
- "--main-dex-list only supported with exactly one --input, use DexFileSplitter for more");
- }
-
- if (!options.multidexMode.isMultidexAllowed()) {
- if (options.mainDexListFile != null) {
- throw new RuntimeException(
- "--main-dex-list is only supported with multidex enabled, but mode is: "
- + options.multidexMode.toString());
- }
- if (options.minimalMainDex) {
- throw new RuntimeException(
- "--minimal-main-dex is only supported with multidex enabled, but mode is: "
- + options.multidexMode.toString());
- }
- }
-
- D8Command.Builder builder = D8Command.builder();
-
- Map<String, Integer> inputOrdering = new HashMap<>(options.inputArchives.size());
- int sequenceNumber = 0;
- for (String s : options.inputArchives) {
- builder.addProgramFiles(Paths.get(s));
- inputOrdering.put(s, sequenceNumber++);
- }
-
- // Determine enabling multidexing and file indexing.
- Integer singleFixedFileIndex = null;
- switch (options.multidexMode) {
- case OFF:
- singleFixedFileIndex = 0;
- break;
- case GIVEN_SHARD:
- if (options.inputArchives.size() != 1) {
- throw new RuntimeException("'--multidex=given_shard' requires exactly one --input.");
- }
- singleFixedFileIndex = parseFileIndexFromShardFilename(options.inputArchives.get(0)) - 1;
- break;
- case MINIMAL:
- case BEST_EFFORT:
- // Nothing to do.
- break;
- default:
- throw new Unreachable("Unexpected enum: " + options.multidexMode);
- }
-
- if (options.mainDexListFile != null) {
- builder.addMainDexListFiles(Paths.get(options.mainDexListFile));
- }
-
- ArchiveConsumer consumer =
- new ArchiveConsumer(
- Paths.get(options.outputArchive), options.dexPrefix, singleFixedFileIndex);
- builder.setProgramConsumer(consumer);
-
- DexFileMergerHelper.run(builder.build(), options.minimalMainDex, inputOrdering);
-
- // If input was empty we still need to write out an empty zip.
- if (!consumer.hasWrittenSomething()) {
- File f = new File(options.outputArchive);
- ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f));
- out.close();
- }
- }
-
- public static void main(String[] args) {
- try {
- if (PRINT_ARGS) {
- printArgs(args);
- }
- run(args);
- } catch (CompilationFailedException | IOException e) {
- System.err.println("Merge failed: " + e.getMessage());
- System.exit(1);
- }
- }
-
- private static void printArgs(String[] args) {
- System.err.print("r8.DexFileMerger");
- for (String s : args) {
- System.err.printf(" %s", s);
- }
- System.err.println("");
- }
-}
diff --git a/src/main/keep.txt b/src/main/keep.txt
index 2450f6b..7791d4e 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -8,8 +8,6 @@
-keep public class com.android.tools.r8.D8 { public static void main(java.lang.String[]); }
-keep public class com.android.tools.r8.R8 { public static void main(java.lang.String[]); }
-keep public class com.android.tools.r8.ExtractMarker { public static void main(java.lang.String[]); }
--keep public class com.android.tools.r8.compatdexbuilder.CompatDexBuilder { public static void main(java.lang.String[]); }
--keep public class com.android.tools.r8.dexfilemerger.DexFileMerger { public static void main(java.lang.String[]); }
-keep public class com.android.tools.r8.dexsplitter.DexSplitter { public static void main(java.lang.String[]); }
-keep public class com.android.tools.r8.Version { public static java.lang.String getVersionString(); }
@@ -25,6 +23,3 @@
# Compatibility command line program used by the Android Platform build.
-keep public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
-
-# Compatibility command line program used by in google3.
--keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderTests.java b/src/test/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderTests.java
deleted file mode 100644
index 341fb67..0000000
--- a/src/test/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderTests.java
+++ /dev/null
@@ -1,104 +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.compatdexbuilder;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-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.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Path;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class CompatDexBuilderTests {
-
- @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
- @Test
- public void compileManyClasses() throws IOException, InterruptedException, ExecutionException {
- final String SUBDIR = "naming001";
- final String INPUT_JAR = ToolHelper.TESTS_BUILD_DIR + "examples/" + SUBDIR + ".jar";
- final List<String> CLASS_NAMES =
- ImmutableList.of(
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "Reflect2$A",
- "Reflect2$B",
- "Reflect2",
- "Reflect");
-
- // Run CompatDexBuilder on naming001.jar
- Path outputZip = temp.getRoot().toPath().resolve("out.zip");
- CompatDexBuilder.main(
- new String[] {"--input_jar", INPUT_JAR, "--output_zip", outputZip.toString()});
- assertTrue(outputZip.toFile().exists());
-
- // Verify if all the classes have their corresponding ".class.dex" files in the zip.
- Set<String> expectedNames = new HashSet<>();
- for (String className : CLASS_NAMES) {
- expectedNames.add(SUBDIR + "/" + className + ".class.dex");
- }
- try (ZipFile zipFile = new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8)) {
- for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
- ZipEntry ze = e.nextElement();
- expectedNames.remove(ze.getName());
- }
- }
- assertTrue(expectedNames.isEmpty());
- }
-
- @Test
- public void compileTwoClassesAndRun()
- throws IOException, InterruptedException, ExecutionException, CompilationFailedException {
- // Run CompatDexBuilder on dexMergeSample.jar
- final String INPUT_JAR = ToolHelper.EXAMPLES_BUILD_DIR + "dexmergesample.jar";
- Path outputZip = temp.getRoot().toPath().resolve("out.zip");
- CompatDexBuilder.main(
- new String[] {"--input_jar", INPUT_JAR, "--output_zip", outputZip.toString()});
-
- // Merge zip content into a single dex file.
- Path d8OutDir = temp.newFolder().toPath();
- D8.run(
- D8Command.builder()
- .setOutput(d8OutDir, OutputMode.DexIndexed)
- .addProgramFiles(outputZip)
- .build());
-
- // Validate by running methods of Class1 and Class2
- for (String className : new String[] {"Class1", "Class2"}) {
- ArtCommandBuilder artCommandBuilder = new ArtCommandBuilder();
- artCommandBuilder.appendClasspath(d8OutDir.resolve("classes.dex").toString());
- artCommandBuilder.setMainClass("dexmergesample." + className);
- String out = ToolHelper.runArt(artCommandBuilder);
- assertEquals(out, className + "\n");
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
deleted file mode 100644
index 4ec0131..0000000
--- a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
+++ /dev/null
@@ -1,216 +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.compatdx;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.StringUtils.BraceType;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import java.io.IOException;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.StandardOpenOption;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class CompatDxTests {
- private static final int MAX_METHOD_COUNT = Constants.U16BIT_MAX;
-
- private static final String EXAMPLE_JAR_FILE1 = ToolHelper.EXAMPLES_BUILD_DIR + "arithmetic.jar";
- private static final String EXAMPLE_JAR_FILE2 = ToolHelper.EXAMPLES_BUILD_DIR + "barray.jar";
-
- private static final String NO_LOCALS = "--no-locals";
- private static final String NO_POSITIONS = "--positions=none";
- private static final String MULTIDEX = "--multi-dex";
- private static final String NUM_THREADS_5 = "--num-threads=5";
-
- @Rule
- public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
- @Test
- public void noFilesTest() throws IOException {
- runDexer("--no-files");
- }
-
- @Test
- public void noOutputTest() throws IOException {
- runDexerWithoutOutput(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void singleJarInputFile() throws IOException {
- runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void multipleJarInputFiles() throws IOException {
- runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1, EXAMPLE_JAR_FILE2);
- }
-
- @Test
- public void outputZipFile() throws IOException {
- runDexerWithOutput("foo.dex.zip", NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void useMultipleThreads() throws IOException {
- runDexer(NUM_THREADS_5, NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void withPositions() throws IOException {
- runDexer(NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void withLocals() throws IOException {
- runDexer(NO_POSITIONS, MULTIDEX, EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void withoutMultidex() throws IOException {
- runDexer(NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void writeToNamedDexFile() throws IOException {
- runDexerWithOutput("named-output.dex", EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void keepClassesSingleDexTest() throws IOException {
- runDexerWithOutput("out.zip", "--keep-classes", EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void keepClassesMultiDexTest() throws IOException {
- runDexerWithOutput("out.zip", "--keep-classes", "--multi-dex", EXAMPLE_JAR_FILE1);
- }
-
- @Test
- public void ignoreDexInArchiveTest() throws IOException {
- // Create a JAR with both a .class and a .dex file (the .dex file is just empty).
- Path jarWithClassesAndDex = temp.newFile("test.jar").toPath();
- Files.copy(Paths.get(EXAMPLE_JAR_FILE1), jarWithClassesAndDex,
- StandardCopyOption.REPLACE_EXISTING);
- URI uri = URI.create("jar:" + jarWithClassesAndDex.toUri());
- FileSystem fileSystem = FileSystems.newFileSystem(uri, ImmutableMap.of("create", "true"));
- Path dexFile = fileSystem.getPath("classes.dex");
- Files.newOutputStream(dexFile, StandardOpenOption.CREATE).close();
- fileSystem.close();
-
- // Only test this with CompatDx, as dx does not like the empty .dex file.
- List<String> d8Args =ImmutableList.of(
- "--output=" + temp.newFolder("out").toString(), jarWithClassesAndDex.toString());
- CompatDx.main(d8Args.toArray(StringUtils.EMPTY_ARRAY));
- }
-
- private void runDexer(String... args) throws IOException {
- runDexerWithOutput("", args);
- }
-
- private void runDexerWithoutOutput(String... args) throws IOException {
- runDexerWithOutput(null, args);
- }
-
- private Path getOutputD8() {
- return temp.getRoot().toPath().resolve("d8-out");
- }
-
- private Path getOutputDX() {
- return temp.getRoot().toPath().resolve("dx-out");
- }
-
- private void runDexerWithOutput(String out, String... args) throws IOException {
- Path d8Out = null;
- Path dxOut = null;
- if (out != null) {
- Path baseD8 = getOutputD8();
- Path baseDX = getOutputDX();
- Files.createDirectory(baseD8);
- Files.createDirectory(baseDX);
- d8Out = baseD8.resolve(out);
- dxOut = baseDX.resolve(out);
- assertNotEquals(d8Out, dxOut);
- }
-
- List<String> d8Args = new ArrayList<>(args.length + 2);
- d8Args.add("--dex");
- if (d8Out != null) {
- d8Args.add("--output=" + d8Out);
- }
- Collections.addAll(d8Args, args);
- System.out.println("running: d8 " + StringUtils.join(d8Args, " "));
- CompatDx.main(d8Args.toArray(StringUtils.EMPTY_ARRAY));
-
- List<String> dxArgs = new ArrayList<>(args.length + 2);
- if (dxOut != null) {
- dxArgs.add("--output=" + dxOut);
- }
- Collections.addAll(dxArgs, args);
- System.out.println("running: dx " + StringUtils.join(dxArgs, " "));
- ProcessResult result = ToolHelper.runDX(dxArgs.toArray(StringUtils.EMPTY_ARRAY));
- assertEquals(result.stderr, 0, result.exitCode);
-
- if (out == null) {
- // Can't check output if explicitly not writing any.
- return;
- }
-
- List<Path> d8Files = Files.list(Files.isDirectory(d8Out) ? d8Out : d8Out.getParent())
- .sorted().collect(Collectors.toList());
- List<Path> dxFiles = Files.list(Files.isDirectory(dxOut) ? dxOut : dxOut.getParent())
- .sorted().collect(Collectors.toList());
- assertEquals("Out file names differ",
- StringUtils.join(dxFiles, "\n", BraceType.NONE, (file) ->
- file.getFileName().toString()),
- StringUtils.join(d8Files, "\n", BraceType.NONE, (file) ->
- file.getFileName().toString()));
-
- for (int i = 0; i < d8Files.size(); i++) {
- if (FileUtils.isArchive(d8Files.get(i))) {
- compareArchiveFiles(d8Files.get(i), dxFiles.get(i));
- }
- }
- }
-
- private void compareArchiveFiles(Path d8File, Path dxFile) throws IOException {
- ZipFile d8Zip = new ZipFile(d8File.toFile(), StandardCharsets.UTF_8);
- ZipFile dxZip = new ZipFile(dxFile.toFile(), StandardCharsets.UTF_8);
- // TODO(zerny): This should test resource containment too once supported.
- Set<String> d8Content = d8Zip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
- Set<String> dxContent = dxZip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
- for (String entry : d8Content) {
- assertTrue("Expected dx output to contain " + entry, dxContent.contains(entry));
- }
- for (String entry : dxContent) {
- Path path = Paths.get(entry);
- if (FileUtils.isDexFile(path) || FileUtils.isClassFile(path)) {
- assertTrue("Expected d8 output to contain " + entry, d8Content.contains(entry));
- }
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
deleted file mode 100644
index 91364d4..0000000
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
+++ /dev/null
@@ -1,149 +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.dexfilemerger;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.DexFileMergerHelper;
-import com.android.tools.r8.ExtractMarker;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.dex.Marker;
-import com.android.tools.r8.maindexlist.MainDexListTests;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collection;
-import java.util.concurrent.ExecutionException;
-import org.junit.Test;
-
-public class DexFileMergerTests extends TestBase {
-
- private static final String CLASS_DIR = ToolHelper.EXAMPLES_BUILD_DIR + "classes/dexmergesample";
- private static final String CLASS1_CLASS = CLASS_DIR + "/Class1.class";
- private static final String CLASS2_CLASS = CLASS_DIR + "/Class2.class";
- private static final int MAX_METHOD_COUNT = Constants.U16BIT_MAX;
-
- private Path createMergerInputWithTwoClasses(OutputMode outputMode, boolean addMarker)
- throws CompilationFailedException, IOException {
- // Compile Class1 and Class2
- Path mergerInputZip = temp.newFolder().toPath().resolve("merger-input.zip");
- D8Command command =
- D8Command.builder()
- .setOutput(mergerInputZip, outputMode)
- .addProgramFiles(Paths.get(CLASS1_CLASS))
- .addProgramFiles(Paths.get(CLASS2_CLASS))
- .build();
-
- DexFileMergerHelper.runD8ForTesting(command, !addMarker);
-
- return mergerInputZip;
- }
-
- private void testMarker(boolean addMarkerToInput)
- throws CompilationFailedException, IOException, ResourceException, ExecutionException {
- Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexIndexed, addMarkerToInput);
- int expectedNumberOfMarkers = addMarkerToInput ? 1 : 0;
-
- Collection<Marker> inputMarkers = ExtractMarker.extractMarkerFromDexFile(mergerInputZip);
- assertEquals(expectedNumberOfMarkers, inputMarkers.size());
-
- // Test that the DexFileMerger preserves markers.
- Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
- DexFileMerger.main(
- new String[] {
- "--input", mergerInputZip.toString(), "--output", mergerOutputZip.toString()
- });
- Collection<Marker> outputMarkers = ExtractMarker.extractMarkerFromDexFile(mergerOutputZip);
- assertEquals(expectedNumberOfMarkers, outputMarkers.size());
-
- // Test that D8 when used for merging preserves markers.
- D8.main(new String[] { mergerInputZip.toString(), "--output", mergerOutputZip.toString() });
- Collection<Marker> d8OutputMarkers = ExtractMarker.extractMarkerFromDexFile((mergerOutputZip));
- assertEquals(expectedNumberOfMarkers, d8OutputMarkers.size());
- }
-
- @Test
- public void testMarkerPreserved()
- throws CompilationFailedException, IOException, ResourceException, ExecutionException {
- testMarker(true);
- }
-
- @Test
- public void testMarkerNotAdded()
- throws CompilationFailedException, IOException, ResourceException, ExecutionException {
- testMarker(false);
- }
-
- @Test
- public void mergeTwoFiles() throws CompilationFailedException, IOException {
- Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexFilePerClassFile, false);
-
- Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
- DexFileMerger.main(
- new String[] {
- "--input", mergerInputZip.toString(), "--output", mergerOutputZip.toString()
- });
-
- // Test by running methods of Class1 and Class2
- for (String className : new String[] {"Class1", "Class2"}) {
- ArtCommandBuilder builder = new ArtCommandBuilder();
- builder.appendClasspath(mergerOutputZip.toString());
- builder.setMainClass("dexmergesample." + className);
- String out = ToolHelper.runArt(builder);
- assertEquals(out, className + "\n");
- }
- }
-
- private void generateClassesAndTest(int extraMethodCount, int programResourcesSize)
- throws IOException, ExecutionException, CompilationFailedException {
- AndroidApp generatedApp =
- MainDexListTests.generateApplication(
- ImmutableList.of("A", "B"),
- AndroidApiLevel.N.getLevel(),
- MAX_METHOD_COUNT / 2 + 1 + extraMethodCount);
- Path appDir = temp.newFolder().toPath().resolve("merger-input.zip");
- assertEquals(programResourcesSize, generatedApp.getDexProgramResourcesForTesting().size());
- generatedApp.write(appDir, OutputMode.DexIndexed);
-
- Path outZip = temp.getRoot().toPath().resolve("out.zip");
- DexFileMerger.run(
- new String[] {
- "--input", appDir.toString(), "--output", outZip.toString(), "--multidex=off"
- });
- }
-
- @Test
- public void failIfTooBig() throws IOException, ExecutionException {
- // Generates an application with two classes, each with the number of methods just enough not to
- // fit into a single dex file.
- try {
- generateClassesAndTest(1, 2);
- fail("Expect to fail");
- } catch (CompilationFailedException e) {
- assertThat(e.getCause().getMessage(), containsString("does not fit into a single dex file"));
- }
- }
-
- @Test
- public void failIfTooBigControl()
- throws IOException, ExecutionException, CompilationFailedException {
- // Control test for failIfTooBig to make sure we don't fail with less methods.
- generateClassesAndTest(0, 1);
- }
-}