L8 Cf to dex

L8 compilation now generates two
commands and run them one after
the other.

Bug: 134732760
Change-Id: Iffb9e1345b97d1fa2626c1a19b8ff7274274d06a
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 2ea9573..d2b5fb3 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
@@ -33,23 +34,30 @@
    * @param command L8 command.
    */
   public static void run(L8Command command) throws CompilationFailedException {
-    AndroidApp app = command.getInputApp();
-    InternalOptions options = command.getInternalOptions();
-    ExecutorService executor = ThreadUtils.getExecutorService(options);
-    ExceptionUtils.withD8CompilationHandler(
-        command.getReporter(),
-        () -> {
-          try {
-            run(app, options, executor);
-          } finally {
-            executor.shutdown();
-          }
-        });
+    ExecutorService executor =
+        ThreadUtils.getExecutorService(command.getInternalOptions().numberOfThreads);
+    try {
+      ExceptionUtils.withD8CompilationHandler(
+          command.getReporter(),
+          () -> {
+            desugar(command.getInputApp(), command.getInternalOptions(), executor);
+          });
+      if (command.isShrinking()) {
+        command
+            .getReporter()
+            .warning(new StringDiagnostic("Shrinking of desugared library is work in progress."));
+        R8.run(command.getR8Command(), executor);
+      } else {
+        D8.run(command.getD8Command(), executor);
+      }
+    } finally {
+      executor.shutdown();
+    }
   }
 
-  private static void run(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
-      throws IOException {
-    Timing timing = new Timing("L8");
+  private static void desugar(
+      AndroidApp inputApp, InternalOptions options, ExecutorService executor) throws IOException {
+    Timing timing = new Timing("L8 desugaring");
     try {
       // Disable global optimizations.
       options.disableGlobalOptimizations();
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 944c2bd..b760470 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.origin.Origin;
@@ -10,108 +11,33 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
-/** Immutable command structure for an invocation of the {@link L8} libray compiler. */
+/** Immutable command structure for an invocation of the {@link L8} library compiler. */
 @Keep
 public final class L8Command extends BaseCompilerCommand {
 
-  private static class DefaultL8DiagnosticsHandler implements DiagnosticsHandler {
+  private final D8Command d8Command;
+  private final R8Command r8Command;
 
-    @Override
-    public void error(Diagnostic error) {
-      if (error instanceof DexFileOverflowDiagnostic) {
-        DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
-        DiagnosticsHandler.super.error(
-            new StringDiagnostic(
-                overflowDiagnostic.getDiagnosticMessage()
-                    + ". Library too large. L8 can only produce a single .dex file"));
-        return;
-      }
-      DiagnosticsHandler.super.error(error);
-    }
+  boolean isShrinking() {
+    return r8Command != null;
   }
 
-  /**
-   * Builder for constructing a L8Command.
-   *
-   * <p>A builder is obtained by calling {@link L8Command#builder}.
-   */
-  @Keep
-  public static class Builder extends BaseCompilerCommand.Builder<L8Command, Builder> {
-
-    private Builder() {
-      this(new DefaultL8DiagnosticsHandler());
-    }
-
-    private Builder(DiagnosticsHandler diagnosticsHandler) {
-      super(diagnosticsHandler);
-    }
-
-    /** Add dex program-data. */
-    @Override
-    public Builder addDexProgramData(byte[] data, Origin origin) {
-      guard(() -> getAppBuilder().addDexProgramData(data, origin));
-      return self();
-    }
-
-    @Override
-    Builder self() {
-      return this;
-    }
-
-    @Override
-    CompilationMode defaultCompilationMode() {
-      return CompilationMode.DEBUG;
-    }
-
-    @Override
-    void validate() {
-      Reporter reporter = getReporter();
-      if (getSpecialLibraryConfiguration() == null) {
-        reporter.error("L8 requires a special library configuration");
-      } else if (!getSpecialLibraryConfiguration().equals("default")) {
-        reporter.error("L8 currently requires the special library configuration to be \"default\"");
-      }
-      if (getProgramConsumer() instanceof DexIndexedConsumer) {
-        reporter.error("L8 does not support compiling to dex");
-      }
-      if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) {
-        reporter.error("L8 does not support compiling to dex per class");
-      }
-      if (getAppBuilder().hasMainDexList()) {
-        reporter.error("L8 does not support a main dex list");
-      } else if (getMainDexListConsumer() != null) {
-        reporter.error("L8 does not support generating a main dex list");
-      }
-      super.validate();
-    }
-
-    @Override
-    L8Command makeCommand() {
-      if (isPrintHelp() || isPrintVersion()) {
-        return new L8Command(isPrintHelp(), isPrintVersion());
-      }
-
-      return new L8Command(
-          getAppBuilder().build(),
-          getMode(),
-          getProgramConsumer(),
-          getMainDexListConsumer(),
-          getMinApiLevel(),
-          getReporter(),
-          getSpecialLibraryConfiguration());
-    }
+  D8Command getD8Command() {
+    return d8Command;
   }
 
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
-    return new Builder(diagnosticsHandler);
+  R8Command getR8Command() {
+    return r8Command;
   }
 
   private L8Command(
+      R8Command r8Command,
+      D8Command d8Command,
       AndroidApp inputApp,
       CompilationMode mode,
       ProgramConsumer programConsumer,
@@ -131,27 +57,56 @@
         specialLibraryConfiguration,
         false,
         (name, checksum) -> true);
+    this.d8Command = d8Command;
+    this.r8Command = r8Command;
   }
 
   private L8Command(boolean printHelp, boolean printVersion) {
     super(printHelp, printVersion);
+    r8Command = null;
+    d8Command = null;
   }
 
   private void configureLibraryDesugaring(InternalOptions options) {
     SpecialLibraryConfiguration.configureLibraryDesugaringForLibraryCompilation(options);
   }
 
+  protected static class DefaultL8DiagnosticsHandler implements DiagnosticsHandler {
+
+    @Override
+    public void error(Diagnostic error) {
+      if (error instanceof DexFileOverflowDiagnostic) {
+        DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
+        DiagnosticsHandler.super.error(
+            new StringDiagnostic(
+                overflowDiagnostic.getDiagnosticMessage()
+                    + ". Library too large. L8 can only produce a single .dex file"));
+        return;
+      }
+      DiagnosticsHandler.super.error(error);
+    }
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
+    return new Builder(diagnosticsHandler);
+  }
+
   @Override
   InternalOptions getInternalOptions() {
     InternalOptions internal = new InternalOptions(new DexItemFactory(), getReporter());
     assert !internal.debug;
     internal.debug = getMode() == CompilationMode.DEBUG;
-    internal.programConsumer = getProgramConsumer();
     assert internal.mainDexListConsumer == null;
     assert !internal.minimalMainDex;
     internal.minApiLevel = getMinApiLevel();
     assert !internal.intermediate;
     assert internal.readCompileTimeAnnotations;
+    internal.programConsumer = getProgramConsumer();
+    assert internal.programConsumer instanceof ClassFileConsumer;
 
     // Assert and fixup defaults.
     assert !internal.isShrinking();
@@ -188,4 +143,134 @@
 
     return internal;
   }
+
+  /**
+   * Builder for constructing a L8Command.
+   *
+   * <p>A builder is obtained by calling {@link L8Command#builder}.
+   */
+  @Keep
+  public static class Builder extends BaseCompilerCommand.Builder<L8Command, Builder> {
+
+    private Builder() {
+      this(new DefaultL8DiagnosticsHandler());
+    }
+
+    private Builder(DiagnosticsHandler diagnosticsHandler) {
+      super(diagnosticsHandler);
+    }
+
+    public boolean isShrinking() {
+      // TODO(b/134732760): Answers true if keep rules, even empty, are provided.
+      return false;
+    }
+
+    @Override
+    Builder self() {
+      return this;
+    }
+
+    @Override
+    CompilationMode defaultCompilationMode() {
+      return CompilationMode.DEBUG;
+    }
+
+    @Override
+    void validate() {
+      Reporter reporter = getReporter();
+      if (getSpecialLibraryConfiguration() == null) {
+        reporter.error("L8 requires a special library configuration");
+      } else if (!getSpecialLibraryConfiguration().equals("default")) {
+        reporter.error("L8 currently requires the special library configuration to be \"default\"");
+      }
+      if (getProgramConsumer() instanceof ClassFileConsumer) {
+        reporter.error("L8 does not support compiling to class files");
+      }
+      if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) {
+        reporter.error("L8 does not support compiling to dex per class");
+      }
+      if (getAppBuilder().hasMainDexList()) {
+        reporter.error("L8 does not support a main dex list");
+      } else if (getMainDexListConsumer() != null) {
+        reporter.error("L8 does not support generating a main dex list");
+      }
+      super.validate();
+    }
+
+    @Override
+    L8Command makeCommand() {
+      if (isPrintHelp() || isPrintVersion()) {
+        return new L8Command(isPrintHelp(), isPrintVersion());
+      }
+
+      if (getMode() == null) {
+        setMode(defaultCompilationMode());
+      }
+
+      R8Command r8Command = null;
+      D8Command d8Command = null;
+
+      AndroidApp inputs = getAppBuilder().build();
+      DesugaredLibrary desugaredLibrary = new DesugaredLibrary();
+
+      if (isShrinking()) {
+        // TODO(b/134732760): Support R8 is incomplete.
+        R8Command.Builder r8Builder =
+            R8Command.builder()
+                .addProgramResourceProvider(desugaredLibrary)
+                .setMinApiLevel(getMinApiLevel())
+                .setMode(getMode())
+                .setProgramConsumer(getProgramConsumer());
+        for (ClassFileResourceProvider libraryResourceProvider :
+            inputs.getLibraryResourceProviders()) {
+          r8Builder.addLibraryResourceProvider(libraryResourceProvider);
+        }
+        r8Command = r8Builder.makeCommand();
+      } else {
+        D8Command.Builder d8Builder =
+            D8Command.builder()
+                .addProgramResourceProvider(desugaredLibrary)
+                .setMinApiLevel(getMinApiLevel())
+                .setMode(getMode())
+                .setProgramConsumer(getProgramConsumer());
+        for (ClassFileResourceProvider libraryResourceProvider :
+            inputs.getLibraryResourceProviders()) {
+          d8Builder.addLibraryResourceProvider(libraryResourceProvider);
+        }
+        d8Command = d8Builder.makeCommand();
+      }
+      return new L8Command(
+          r8Command,
+          d8Command,
+          inputs,
+          getMode(),
+          desugaredLibrary,
+          getMainDexListConsumer(),
+          getMinApiLevel(),
+          getReporter(),
+          getSpecialLibraryConfiguration());
+    }
+  }
+
+  static class DesugaredLibrary implements ClassFileConsumer, ProgramResourceProvider {
+
+    private final List<ProgramResource> resources = new ArrayList<>();
+
+    @Override
+    public synchronized void accept(
+        ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+      // TODO(b/139273544): Map Origin information.
+      resources.add(
+          ProgramResource.fromBytes(
+              Origin.unknown(), Kind.CF, data.copyByteData(), Collections.singleton(descriptor)));
+    }
+
+    @Override
+    public Collection<ProgramResource> getProgramResources() throws ResourceException {
+      return resources;
+    }
+
+    @Override
+    public void finished(DiagnosticsHandler handler) {}
+  }
 }
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 4b0e3fc..1c3af7a 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -534,6 +534,9 @@
      * Add library resource provider.
      */
     public Builder addLibraryResourceProvider(ClassFileResourceProvider provider) {
+      if (provider instanceof InternalArchiveClassFileProvider) {
+        archiveProvidersToClose.add((InternalArchiveClassFileProvider) provider);
+      }
       libraryResourceProviders.add(provider);
       return this;
     }
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 065d3f2..ca080de 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -4,16 +4,13 @@
 package com.android.tools.r8;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import com.android.sdklib.AndroidVersion;
 import com.android.tools.r8.dex.Marker;
-import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
 import java.nio.file.Path;
 import java.util.Collection;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -32,23 +29,20 @@
   public void emptyCommand() throws Throwable {
     verifyEmptyCommand(
         L8Command.builder()
-            .setProgramConsumer(ClassFileConsumer.emptyConsumer())
+            .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
             .addSpecialLibraryConfiguration("default")
             .build());
   }
 
-  private void verifyEmptyCommand(L8Command command) throws Throwable {
-    assertEquals(CompilationMode.DEBUG, command.getMode());
-    assertEquals(AndroidVersion.DEFAULT.getApiLevel(), command.getMinApiLevel());
+  private void verifyEmptyCommand(L8Command command) {
+    BaseCompilerCommand compilationCommand =
+        command.getD8Command() == null ? command.getR8Command() : command.getD8Command();
+    assertNotNull(compilationCommand);
     assertTrue(command.getProgramConsumer() instanceof ClassFileConsumer);
-    AndroidApp app = ToolHelper.getApp(command);
-    assertEquals(0, app.getDexProgramResourcesForTesting().size());
-    assertEquals(0, app.getClassProgramResourcesForTesting().size());
+    assertTrue(compilationCommand.getProgramConsumer() instanceof DexIndexedConsumer);
   }
 
-  // We ignore this test since L8 is currently Cf to Cf.
   @Test
-  @Ignore
   public void testMarker() throws Throwable {
     Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
     L8.run(
@@ -57,13 +51,12 @@
             .addProgramFiles(ToolHelper.getDesugarJDKLibs())
             .setMinApiLevel(20)
             .addSpecialLibraryConfiguration("default")
-            .setOutput(output, OutputMode.ClassFile)
+            .setOutput(output, OutputMode.DexIndexed)
             .build());
     Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
-    assertEquals(1, markers.size());
+    // TODO(b/134732760): Shouldn't we remove the D8/R8 marker?
+    assertEquals(2, markers.size());
     Marker marker = markers.iterator().next();
-    assertEquals(20, marker.getMinApi().intValue());
-    assertEquals(Tool.L8, marker.getTool());
   }
 
   private L8Command.Builder prepareBuilder(DiagnosticsHandler handler) {
@@ -96,11 +89,11 @@
   }
 
   @Test(expected = CompilationFailedException.class)
-  public void dexFileOutputNotSupported() throws Throwable {
+  public void classFileOutputNotSupported() throws Throwable {
     DiagnosticsChecker.checkErrorsContains(
-        "L8 does not support compiling to dex",
+        "L8 does not support compiling to class files",
         (handler) ->
-            prepareBuilder(handler).setProgramConsumer(DexIndexedConsumer.emptyConsumer()).build());
+            prepareBuilder(handler).setProgramConsumer(ClassFileConsumer.emptyConsumer()).build());
   }
 
   @Test(expected = CompilationFailedException.class)
@@ -112,7 +105,7 @@
   }
 
   @Test(expected = CompilationFailedException.class)
-  public void specialLibraryConfgurationMustBeDefault() throws Throwable {
+  public void specialLibraryConfigurationMustBeDefault() throws Throwable {
     DiagnosticsChecker.checkErrorsContains(
         "L8 currently requires the special library configuration to be \"default\"",
         (handler) ->
@@ -128,7 +121,7 @@
         "Special library configuration is still work in progress",
         handler ->
             prepareBuilder(handler)
-                .setProgramConsumer(ClassFileConsumer.emptyConsumer())
+                .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
                 .addSpecialLibraryConfiguration("default")
                 .build());
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
index 9f708ce..cd510a5 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
@@ -10,22 +10,15 @@
 import com.android.tools.r8.L8Command;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
 
 public class CoreLibDesugarTestBase extends TestBase {
 
-  private static Map<CacheEntry, TestCompileResult> computedLibraryCache =
-      new ConcurrentHashMap<>();
-
   @Deprecated
   protected boolean requiresCoreLibDesugaring(TestParameters parameters) {
     // TODO(b/134732760): Use the two other APIS instead.
@@ -62,28 +55,10 @@
   protected Path buildDesugaredLibrary(
       AndroidApiLevel apiLevel, String keepRules, boolean shrink, List<Path> additionalProgramFiles)
       throws RuntimeException {
+    // TODO(b/134732760): Support Shrinking.
     // We wrap exceptions in a RuntimeException to call this from a lambda.
     try {
-      Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
-      CacheEntry cacheEntry = new CacheEntry(apiLevel, keepRules, shrink, additionalProgramFiles);
-      TestCompileResult testCompileResult =
-          computedLibraryCache.computeIfAbsent(
-              cacheEntry,
-              key -> compileDesugaredLibrary(apiLevel, keepRules, shrink, additionalProgramFiles));
-      testCompileResult.writeToZip(output);
-      return output;
-    } catch (Exception e) {
-      throw new RuntimeException(e);
-    }
-  }
-
-  private TestCompileResult compileDesugaredLibrary(
-      AndroidApiLevel apiLevel, String keepRules, boolean shrink, List<Path> additionalProgramFiles)
-      throws RuntimeException {
-    // We wrap exceptions in a RuntimeException to call this from a lambda.
-    try {
-      // TODO(b/138922694): Known performance issue here.
-      Path cfDesugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_cf.zip");
+      Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
       L8.run(
           L8Command.builder()
               .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
@@ -91,66 +66,14 @@
               .addProgramFiles(additionalProgramFiles)
               .addSpecialLibraryConfiguration("default")
               .setMinApiLevel(apiLevel.getLevel())
-              .setOutput(cfDesugaredLib, OutputMode.ClassFile)
+              .setOutput(desugaredLib, OutputMode.DexIndexed)
               .build());
-      if (shrink) {
-        return testForR8(Backend.DEX)
-            .addProgramFiles(cfDesugaredLib)
-            .noMinification()
-            .addKeepRules(keepRules)
-            // We still need P+ library files to resolve classes.
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-            .setMinApi(apiLevel)
-            .compile();
-      }
-      return testForD8()
-          .addProgramFiles(cfDesugaredLib)
-          .setMinApi(apiLevel)
-          // We still need P+ library files to resolve classes.
-          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-          .compile();
+      return desugaredLib;
     } catch (Exception e) {
       throw new RuntimeException(e);
     }
   }
 
-  private static class CacheEntry {
-
-    private int apiLevel;
-    private String keepRules;
-    private boolean shrink;
-    private List<Path> additionalProgramFiles;
-
-    private CacheEntry(
-        AndroidApiLevel apiLevel,
-        String keepRules,
-        boolean shrink,
-        List<Path> additionalProgramFiles) {
-      this.apiLevel = apiLevel.getLevel();
-      this.keepRules = keepRules;
-      this.shrink = shrink;
-      this.additionalProgramFiles = additionalProgramFiles;
-    }
-
-    @Override
-    public int hashCode() {
-      // In practice there are only 2 sets of additionalProgramFiles with different sizes.
-      return Objects.hash(apiLevel, keepRules, shrink, additionalProgramFiles.size());
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (!(o instanceof CacheEntry)) {
-        return false;
-      }
-      CacheEntry other = (CacheEntry) o;
-      return apiLevel == other.apiLevel
-          && keepRules.equals(other.keepRules)
-          && shrink == other.shrink
-          && additionalProgramFiles.equals(other.additionalProgramFiles);
-    }
-  }
-
   protected void assertLines2By2Correct(String stdOut) {
     String[] lines = stdOut.split("\n");
     assert lines.length % 2 == 0;
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/MergingJ$Test.java b/src/test/java/com/android/tools/r8/desugar/corelib/MergingJ$Test.java
index f4f5807..bdce532 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/MergingJ$Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/MergingJ$Test.java
@@ -27,7 +27,11 @@
     CodeInspector codeInspectorOutput = null;
     try {
       codeInspectorOutput =
-          testForD8().addProgramFiles(mergerInputPart1, mergerInputPart2).compile().inspector();
+          testForD8()
+              .addProgramFiles(mergerInputPart1, mergerInputPart2)
+              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+              .compile()
+              .inspector();
     } catch (Exception e) {
       if (e.getCause().getMessage().contains("Merging dex file containing classes with prefix")) {
         // TODO(b/138278440): Forbid to merge j$ classes in a Google3 compliant way.
@@ -47,7 +51,6 @@
   }
 
   private Path buildSplitDesugaredLibraryPart1() throws Exception {
-    Path outputCf = temp.newFolder().toPath().resolve("merger-input-cf.zip");
     Path outputDex = temp.newFolder().toPath().resolve("merger-input-dex.zip");
     L8.run(
         L8Command.builder()
@@ -55,13 +58,8 @@
             .addProgramFiles(ToolHelper.getDesugarJDKLibs())
             .addSpecialLibraryConfiguration("default")
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
-            .setOutput(outputCf, OutputMode.ClassFile)
+            .setOutput(outputDex, OutputMode.DexIndexed)
             .build());
-    testForD8()
-        .addProgramFiles(outputCf)
-        .setMinApi(AndroidApiLevel.B)
-        .compile()
-        .writeToZip(outputDex);
     return outputDex;
   }
 
@@ -75,7 +73,7 @@
             .addClasspathFiles(ToolHelper.getDesugarJDKLibs())
             .addSpecialLibraryConfiguration("default")
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
-            .setOutput(outputCf, OutputMode.ClassFile)
+            .setOutput(outputCf, OutputMode.DexIndexed)
             .build());
     testForD8()
         .addProgramFiles(outputCf)