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