Fail when compiling to native multidex with main-dex options.

Change-Id: Ic4418769c4d292e3b858efdfca13d1cf3ed52a6c
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index f0217b9..1004532 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
@@ -140,6 +141,14 @@
       } else if (getMainDexListConsumer() != null) {
         reporter.error("Option --main-dex-list-output require --main-dex-list");
       }
+      if (getMinApiLevel() >= AndroidApiLevel.L.getLevel()) {
+        if (getMainDexListConsumer() != null || getAppBuilder().hasMainDexList()) {
+          reporter.error(
+              "D8 does not support main-dex inputs and outputs when compiling to API level "
+                  + AndroidApiLevel.L.getLevel()
+                  + " and above");
+        }
+      }
       super.validate();
     }
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7e4974b..ae166d3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -54,7 +54,6 @@
 import com.android.tools.r8.shaking.TreePruner;
 import com.android.tools.r8.shaking.VerticalClassMerger;
 import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -244,12 +243,6 @@
       System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
     }
     try {
-      AndroidApiLevel oLevel = AndroidApiLevel.O;
-      if (options.minApiLevel >= oLevel.getLevel()
-          && !options.mainDexKeepRules.isEmpty()) {
-        throw new CompilationError("Automatic main dex list is not supported when compiling for "
-            + oLevel.getName() + " and later (--min-api " + oLevel.getLevel() + ")");
-      }
       DexApplication application =
           new ApplicationReader(inputApp, options, timing).read(executorService).toDirect();
 
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index ff74d09..9f25fbc 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.shaking.ProguardConfigurationSourceBytes;
 import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
 import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
@@ -321,6 +322,17 @@
         reporter.error(
             "Option --main-dex-list-output require --main-dex-rules and/or --main-dex-list");
       }
+      if (!(getProgramConsumer() instanceof ClassFileConsumer)
+          && getMinApiLevel() >= AndroidApiLevel.L.getLevel()) {
+        if (getMainDexListConsumer() != null
+            || !mainDexRules.isEmpty()
+            || getAppBuilder().hasMainDexList()) {
+          reporter.error(
+              "R8 does not support main-dex inputs and outputs when compiling to API level "
+                  + AndroidApiLevel.L.getLevel()
+                  + " and above");
+        }
+      }
       for (Path file : programFiles) {
         if (FileUtils.isDexFile(file)) {
           reporter.error(new StringDiagnostic(
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index a478860..f6454f9 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -96,6 +96,7 @@
       ExecutorService executorService,
       ProgramClassConflictResolver resolver)
       throws IOException, ExecutionException {
+    assert verifyMainDexOptionsCompatible(inputApp, options);
     timing.begin("DexApplication.read");
     final LazyLoadedDexApplication.Builder builder =
         DexApplication.builder(itemFactory, timing, resolver);
@@ -123,7 +124,22 @@
     return builder.build();
   }
 
-  private int verifyOrComputeMinApiLevel(int computedMinApiLevel, DexReader dexReader) {
+  private static boolean verifyMainDexOptionsCompatible(
+      AndroidApp inputApp, InternalOptions options) {
+    if (!options.isGeneratingDex()) {
+      return true;
+    }
+    AndroidApiLevel nativeMultiDex = AndroidApiLevel.L;
+    if (options.minApiLevel < nativeMultiDex.getLevel()) {
+      return true;
+    }
+    assert options.mainDexKeepRules.isEmpty();
+    assert options.mainDexListConsumer == null;
+    assert !inputApp.hasMainDexList();
+    return true;
+  }
+
+  private int validateOrComputeMinApiLevel(int computedMinApiLevel, DexReader dexReader) {
     DexVersion version = DexVersion.getDexVersion(dexReader.getDexVersion());
     if (options.minApiLevel == AndroidApiLevel.getDefault().getLevel()) {
       computedMinApiLevel = Math
@@ -199,7 +215,7 @@
         int computedMinApiLevel = options.minApiLevel;
         for (ProgramResource input : dexSources) {
           DexReader dexReader = new DexReader(input);
-          computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+          computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
           dexParsers.add(new DexParser(dexReader, classKind, itemFactory, options.reporter));
         }
         options.minApiLevel = computedMinApiLevel;
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index aed1231..6b51909 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ZipUtils;
@@ -251,6 +252,19 @@
   }
 
   @Test(expected = CompilationFailedException.class)
+  public void mainDexListWithNonLegacyMinApi() throws Throwable {
+    Path mainDexList = temp.newFile("main-dex-list.txt").toPath();
+    DiagnosticsChecker.checkErrorsContains(
+        "does not support main-dex",
+        (handler) ->
+            D8Command.builder(handler)
+                .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+                .setMinApiLevel(AndroidApiLevel.L.getLevel())
+                .addMainDexListFiles(mainDexList)
+                .build());
+  }
+
+  @Test(expected = CompilationFailedException.class)
   public void invalidOutputFileTypeParse() throws Throwable {
     Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
     parse("--output", invalidType.toString());
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 4356327..518d26f 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
@@ -227,6 +228,32 @@
     parse("--main-dex-list-output", mainDexListOutput.toString());
   }
 
+  @Test(expected = CompilationFailedException.class)
+  public void mainDexRulesWithNonLegacyMinApi() throws Throwable {
+    Path mainDexRules = temp.newFile("main-dex.rules").toPath();
+    DiagnosticsChecker.checkErrorsContains(
+        "does not support main-dex",
+        (handler) ->
+            R8Command.builder(handler)
+                .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+                .setMinApiLevel(AndroidApiLevel.L.getLevel())
+                .addMainDexRulesFiles(mainDexRules)
+                .build());
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void mainDexListWithNonLegacyMinApi() throws Throwable {
+    Path mainDexList = temp.newFile("main-dex-list.txt").toPath();
+    DiagnosticsChecker.checkErrorsContains(
+        "does not support main-dex",
+        (handler) ->
+            R8Command.builder(handler)
+                .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+                .setMinApiLevel(AndroidApiLevel.L.getLevel())
+                .addMainDexListFiles(mainDexList)
+                .build());
+  }
+
   @Test
   public void existingOutputDirWithDexFiles() throws Throwable {
     Path existingDir = temp.newFolder().toPath();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
index 9d9f21d..48b23fe 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
@@ -114,6 +115,7 @@
     Path mainDexList = writeTextToTempFile(testClassMainDexName);
     TestMainDexListConsumer consumer = new TestMainDexListConsumer();
     testForD8()
+        .setMinApi(AndroidApiLevel.K)
         .addProgramClasses(ImmutableList.of(TestClass.class, MyConsumer.class))
         .addMainDexListFiles(ImmutableList.of(mainDexList))
         .setMainDexListConsumer(consumer)
@@ -128,6 +130,7 @@
     Path dexOutput = temp.getRoot().toPath().resolve("classes.zip");
     // Build intermediate dex code first.
     testForD8()
+        .setMinApi(AndroidApiLevel.K)
         .addProgramClasses(ImmutableList.of(TestClass.class, MyConsumer.class))
         .setIntermediate(true)
         .setProgramConsumer(new ArchiveConsumer(dexOutput))
@@ -135,6 +138,7 @@
     // Now test that when merging with a main dex list it is correctly updated.
     TestMainDexListConsumer consumer = new TestMainDexListConsumer();
     testForD8()
+        .setMinApi(AndroidApiLevel.K)
         .addProgramFiles(dexOutput)
         .addMainDexListFiles(ImmutableList.of(mainDexList))
         .setMainDexListConsumer(consumer)
diff --git a/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java b/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java
index 01265bb..18ccf33 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java
@@ -85,7 +85,7 @@
   public void runTestWithR8(GraphConsumer consumer, String rule) throws Exception {
     R8TestBuilder builder =
         testForR8(Backend.DEX)
-            .setMinApi(AndroidApiLevel.L)
+            .setMinApi(AndroidApiLevel.K)
             .addProgramClasses(CLASSES)
             .addMainDexRules(keepMainProguardConfiguration(HelloWorldMain.class))
             .setMainDexKeptGraphConsumer(consumer);