Merge "Add option --main-dex-list to D8 command line"
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index ec4f527..5eb67c4 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -248,6 +248,10 @@
     }
 
     protected void validate() throws CompilationException {
+      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/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 6ac5f1e..b856556 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -71,6 +71,14 @@
       return this;
     }
 
+    protected void validate() throws CompilationException {
+      super.validate();
+      if (getAppBuilder().hasMainDexList() && intermediate) {
+        throw new CompilationException(
+            "Option --main-dex-list cannot be used with --intermediate");
+      }
+    }
+
     /**
      * Build the final D8Command.
      */
@@ -95,17 +103,19 @@
       "Usage: d8 [options] <input-files>",
       " where <input-files> are any combination of dex, class, zip, jar, or apk files",
       " and options are:",
-      "  --debug             # Compile with debugging information (default).",
-      "  --release           # Compile without debugging information.",
-      "  --output <file>     # Output result in <outfile>.",
-      "                      # <file> must be an existing directory or a zip file.",
-      "  --lib <file>        # Add <file> as a library resource.",
-      "  --classpath <file>  # Add <file> as a classpath resource.",
-      "  --min-api           # Minimum Android API level compatibility",
-      "  --intermediate      # Compile an intermediate result intended for later merging.",
-      "  --file-per-class    # Produce a separate dex file per class",
-      "  --version           # Print the version of d8.",
-      "  --help              # Print this message."));
+      "  --debug                 # Compile with debugging information (default).",
+      "  --release               # Compile without debugging information.",
+      "  --output <file>         # Output result in <outfile>.",
+      "                          # <file> must be an existing directory or a zip file.",
+      "  --lib <file>            # Add <file> as a library resource.",
+      "  --classpath <file>      # Add <file> as a classpath resource.",
+      "  --min-api               # Minimum Android API level compatibility",
+      "  --intermediate          # Compile an intermediate result intended for later",
+      "                          # merging.",
+      "  --file-per-class        # Produce a separate dex file per class",
+      "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+      "  --version               # Print the version of d8.",
+      "  --help                  # Print this message."));
 
   private boolean intermediate = false;
 
@@ -121,6 +131,7 @@
   public static Builder parse(String[] args) throws CompilationException, IOException {
     CompilationMode modeSet = null;
     Path outputPath = null;
+    String mainDexList = null;
     Builder builder = builder();
     try {
       for (int i = 0; i < args.length; i++) {
@@ -156,6 +167,12 @@
           builder.addLibraryFiles(Paths.get(args[++i]));
         } else if (arg.equals("--classpath")) {
           builder.addClasspathFiles(Paths.get(args[++i]));
+        } else if (arg.equals("--main-dex-list")) {
+          if (mainDexList != null) {
+            throw new CompilationException("Only one --main-dex-list supported");
+          }
+          mainDexList = args[++i];
+          builder.setMainDexListFile(Paths.get(mainDexList));
         } else if (arg.equals("--min-api")) {
           builder.setMinApiLevel(Integer.valueOf(args[++i]));
         } else if (arg.equals("--intermediate")) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 3b88ba8..c2a3245 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -129,6 +129,14 @@
       return self();
     }
 
+    protected void validate() throws CompilationException {
+      super.validate();
+      if (minimalMainDex && mainDexRules.isEmpty()) {
+        throw new CompilationException(
+            "Option --minimal-main-dex require --main-dex-rules");
+      }
+    }
+
     @Override
     public R8Command build() throws CompilationException, IOException {
       // If printing versions ignore everything else.
@@ -193,20 +201,23 @@
       "Usage: r8 [options] <input-files>",
       " where <input-files> are any combination of dex, class, zip, jar, or apk files",
       " and options are:",
-      "  --release               # Compile without debugging information (default).",
-      "  --debug                 # Compile with debugging information.",
-      "  --output <file>         # Output result in <file>.",
-      "                          # <file> must be an existing directory or a zip file.",
-      "  --lib <file>            # Add <file> as a library resource.",
-      "  --min-api               # Minimum Android API level compatibility.",
-      "  --pg-conf <file>        # Proguard configuration <file> (implies tree shaking/minification).",
-      "  --pg-map <file>         # Proguard map <file>.",
-      "  --no-tree-shaking       # Force disable tree shaking of unreachable classes.",
-      "  --no-minification       # Force disable minification of names.",
-      "  --multidex-rules <file> # Enable automatic classes partitioning for legacy multidex.",
-      "                          # <file> is a Proguard configuration file (with only keep rules).",
-      "  --version               # Print the version of r8.",
-      "  --help                  # Print this message."));
+      "  --release                # Compile without debugging information (default).",
+      "  --debug                  # Compile with debugging information.",
+      "  --output <file>          # Output result in <file>.",
+      "                           # <file> must be an existing directory or a zip file.",
+      "  --lib <file>             # Add <file> as a library resource.",
+      "  --min-api                # Minimum Android API level compatibility.",
+      "  --pg-conf <file>         # Proguard configuration <file> (implies tree",
+      "                           # shaking/minification).",
+      "  --pg-map <file>          # Proguard map <file>.",
+      "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
+      "  --no-minification        # Force disable minification of names.",
+      "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
+      "                           # primary dex file.",
+      "  --minimal-main-dex       # Only place classes specified by --main-dex-rules",
+      "                           # in the primary dex file.",
+      "  --version                # Print the version of r8.",
+      "  --help                   # Print this message."));
 
   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
   private final boolean minimalMainDex;
@@ -271,9 +282,9 @@
         builder.setTreeShaking(false);
       } else if (arg.equals("--no-minification")) {
         builder.setMinification(false);
-      } else if (arg.equals("--multidex-rules")) {
+      } else if (arg.equals("--main-dex-rules")) {
         builder.addMainDexRules(Paths.get(args[++i]));
-      } else if (arg.equals("--minimal-maindex")) {
+      } else if (arg.equals("--minimal-main-dex")) {
         builder.setMinimalMainDex(true);
       } else if (arg.equals("--pg-conf")) {
         builder.addProguardConfigurationFiles(Paths.get(args[++i]));
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index e767030..19d5153 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -579,6 +579,10 @@
       return this;
     }
 
+    public boolean hasMainDexList() {
+      return mainDexList != null;
+    }
+
     /**
      * Set the main-dex list data.
      */
diff --git a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
index 6f45137..ed146b72 100644
--- a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -156,6 +156,37 @@
   }
 
   @Test
+  public void mainDexList() throws Throwable {
+    Path mailDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+    D8Command command = parse("--main-dex-list", mailDexList.toString());
+    assertTrue(ToolHelper.getApp(command).hasMainDexList());
+  }
+
+  @Test
+  public void multipleMainDexList() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path mailDexList1 = temp.getRoot().toPath().resolve("main-dex-list-1.txt");
+    Path mailDexList2 = temp.getRoot().toPath().resolve("main-dex-list-2.txt");
+    parse("--main-dex-list", mailDexList1.toString(), "--main-dex-list", mailDexList2.toString());
+  }
+
+  @Test
+  public void mainDexListWithFilePerClass() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path mailDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+    D8Command command = parse("--main-dex-list", mailDexList.toString(), "--file-per-class");
+    assertTrue(ToolHelper.getApp(command).hasMainDexList());
+  }
+
+  @Test
+  public void mainDexListWithIntermediate() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path mailDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+    D8Command command = parse("--main-dex-list", mailDexList.toString(), "--intermediate");
+    assertTrue(ToolHelper.getApp(command).hasMainDexList());
+  }
+
+  @Test
   public void invalidOutputFileTypeParse() throws Throwable {
     thrown.expect(CompilationException.class);
     Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
diff --git a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
index 0c737b9..27f0367 100644
--- a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
@@ -89,6 +89,24 @@
   }
 
   @Test
+  public void mainDexRules() throws Throwable {
+    Path mailDexRules = temp.newFile("main-dex.rules").toPath();
+    parse("--main-dex-rules", mailDexRules.toString());
+  }
+
+  @Test
+  public void minimalMainDex() throws Throwable {
+    thrown.expect(CompilationException.class);
+    parse("--minimal-main-dex");
+  }
+
+  @Test
+  public void mainDexRulesWithMinimalMainDex() throws Throwable {
+    Path mailDexRules = temp.newFile("main-dex.rules").toPath();
+    parse("--main-dex-rules", mailDexRules.toString(), "--minimal-main-dex");
+  }
+
+  @Test
   public void existingOutputDirWithDexFiles() throws Throwable {
     Path existingDir = temp.newFolder().toPath();
     List<Path> classesFiles = ImmutableList.of(
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 1dabbd3..ee8981b 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -158,9 +158,9 @@
         app_provided_pg_conf = True
     if options.k:
       args.extend(['--pg-conf', options.k])
-    if 'multidexrules' in values:
-      for rules in values['multidexrules']:
-        args.extend(['--multidex-rules', rules])
+    if 'maindexrules' in values:
+      for rules in values['maindexrules']:
+        args.extend(['--main-dex-rules', rules])
 
   if not options.no_libraries and 'libraries' in values:
     for lib in values['libraries']:
diff --git a/tools/youtube_data.py b/tools/youtube_data.py
index a3d3791..114e6c7 100644
--- a/tools/youtube_data.py
+++ b/tools/youtube_data.py
@@ -77,7 +77,7 @@
       'pgconf': [
           '%s_proguard.config' % V12_22_PREFIX,
           '%s/proguardsettings/YouTubeRelease_proguard.config' % THIRD_PARTY],
-      'multidexrules' : [
+      'maindexrules' : [
           os.path.join(V12_22_BASE, 'mainDexClasses.rules'),
           os.path.join(V12_22_BASE, 'main-dex-classes-release.cfg'),
           os.path.join(V12_22_BASE, 'main_dex_YouTubeRelease_proguard.cfg')],