Extend list of backported methoods

When library desugaring is enabled additional methods are
backported. This extends the tool for retreiving the list of
backported methods to include these as well.

For that the desugar library configuration and the compilation library
has to be passed.

Bug: 140368601
Bug: 140367927
Change-Id: Ie711aea45564d8acf69a358f32ffb797f13036a7
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodList.java b/src/main/java/com/android/tools/r8/BackportedMethodList.java
index ef2bea3..befc767 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodList.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodList.java
@@ -6,10 +6,47 @@
 
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.concurrent.ExecutorService;
 
+/**
+ * Tool to extract the list of methods which is backported by the D8 and R8 compilers.
+ *
+ * <p>The D8 and R8 compilers will backport some simple Java APIs which are not present on all API
+ * levels. One example of this is the static method <code>int Integer.divideUnsigned(int a, int b)
+ * </code> which was added from API level 26.
+ *
+ * <p>As these backported methods is supported on all API levels, tools like linters and code
+ * checkers need this information to avoid false negatives when analyzing code.
+ *
+ * <p>This tool will generate a list of all the backported methods for the associated version of D8
+ * and R8.
+ *
+ * <p>This tool will <strong>not</strong> provide information about the APIs supported when using
+ * library desugaring. That information is provided in the dependencies used for library desugaring.
+ * However, in place of library desugaring backporting will be able to backport additional methods.
+ * If library desugaring is used, then passing information about that to this tool will provide the
+ * more precise list. See b/149078312.
+ *
+ * <p>The tool is invoked by calling {@link #run(BackportedMethodListCommand)
+ * BackportedMethodList.run} with an appropriate {@link BackportedMethodListCommand}.
+ *
+ * <p>For example:
+ *
+ * <pre>
+ *   BackportedMethodList.run(BackportedMethodListCommand.builder()
+ *       .setMinApiLevel(apiLevel)
+ *       .setOutputPath(Paths.get("methods-list.txt"))
+ *       .build());
+ * </pre>
+ *
+ * The above generates the list of backported methods for a compilation with a min API of <code>
+ * apiLevel</code> into the file <code>methods-list.txt</code>.
+ */
 @Keep
 public class BackportedMethodList {
 
@@ -18,7 +55,10 @@
           "Usage: BackportedMethodList [options]",
           " Options are:",
           "  --output <file>         # Output result in <file>.",
-          "  --min-api <number>      # Minimum Android API level",
+          "  --min-api <number>      # Minimum Android API level for the application",
+          "  --desugared-lib <file>  # Desugared library configuration (JSON from the",
+          "                          # configuration)",
+          "  --lib <file>            # The compilation SDK library (android.jar)",
           "  --version               # Print the version of BackportedMethodList.",
           "  --help                  # Print this message.");
 
@@ -29,7 +69,7 @@
         + method.proto.toDescriptorString();
   }
 
-  public static void run(BackportedMethodListCommand command) {
+  public static void run(BackportedMethodListCommand command) throws CompilationFailedException {
     if (command.isPrintHelp()) {
       System.out.println(USAGE_MESSAGE);
       return;
@@ -38,20 +78,34 @@
       System.out.println("BackportedMethodList " + Version.getVersionString());
       return;
     }
-    BackportedMethodRewriter.generateListOfBackportedMethods(
-            AndroidApiLevel.getAndroidApiLevel(command.getMinApiLevel()))
-        .stream()
-        .map(BackportedMethodList::formatMethod)
-        .sorted()
-        .forEach(
-            formattedMethod ->
-                command
-                    .getBackportedMethodListConsumer()
-                    .accept(formattedMethod, command.getReporter()));
-    command.getBackportedMethodListConsumer().finished(command.getReporter());
+    InternalOptions options = command.getInternalOptions();
+    ExecutorService executorService = ThreadUtils.getExecutorService(options);
+    try {
+      ExceptionUtils.withD8CompilationHandler(
+          command.getReporter(),
+          () -> {
+            BackportedMethodRewriter.generateListOfBackportedMethods(
+                    command.getInputApp(), options, executorService)
+                .stream()
+                .map(BackportedMethodList::formatMethod)
+                .sorted()
+                .forEach(
+                    formattedMethod ->
+                        command
+                            .getBackportedMethodListConsumer()
+                            .accept(formattedMethod, command.getReporter()));
+            command.getBackportedMethodListConsumer().finished(command.getReporter());
+          });
+    } finally {
+      executorService.shutdown();
+    }
+  }
+
+  public static void run(String[] args) throws CompilationFailedException {
+    run(BackportedMethodListCommand.parse(args).build());
   }
 
   public static void main(String[] args) {
-    run(BackportedMethodListCommand.parse(args).build());
+    ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index a8e7eb9..a4af022 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -3,13 +3,37 @@
 // 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.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+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;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 
+/**
+ * Immutable command structure for an invocation of the {@link BackportedMethodList} tool.
+ *
+ * <p>To build a BackportedMethodList command use the {@link BackportedMethodListCommand.Builder}
+ * class. For example:
+ *
+ * <pre>
+ *   BackportedMethodListCommand command = BackportedMethodListCommand.builder()
+ *     .setMinApiLevel(apiLevel)
+ *     .setOutputPath(Paths.get("methods-list.txt"))
+ *     .build();
+ * </pre>
+ */
 @Keep
 public class BackportedMethodListCommand {
 
@@ -17,7 +41,10 @@
   private final boolean printVersion;
   private final Reporter reporter;
   private final int minApiLevel;
+  private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+  private final AndroidApp app;
   private final StringConsumer backportedMethodListConsumer;
+  private final DexItemFactory factory;
 
   public boolean isPrintHelp() {
     return printHelp;
@@ -35,27 +62,51 @@
     return minApiLevel;
   }
 
+  public DesugaredLibraryConfiguration getDesugaredLibraryConfiguration() {
+    return desugaredLibraryConfiguration;
+  }
+
   public StringConsumer getBackportedMethodListConsumer() {
     return backportedMethodListConsumer;
   }
 
+  AndroidApp getInputApp() {
+    return app;
+  }
+
   private BackportedMethodListCommand(boolean printHelp, boolean printVersion) {
     this.printHelp = printHelp;
     this.printVersion = printVersion;
     this.reporter = new Reporter();
     this.minApiLevel = -1;
+    this.desugaredLibraryConfiguration = null;
+    this.app = null;
     this.backportedMethodListConsumer = null;
+    this.factory = null;
   }
 
   private BackportedMethodListCommand(
       Reporter reporter,
       int minApiLevel,
-      StringConsumer backportedMethodListConsumer) {
+      DesugaredLibraryConfiguration desugaredLibraryConfiguration,
+      AndroidApp app,
+      StringConsumer backportedMethodListConsumer,
+      DexItemFactory factory) {
     this.printHelp = false;
     this.printVersion = false;
     this.reporter = reporter;
     this.minApiLevel = minApiLevel;
+    this.desugaredLibraryConfiguration = desugaredLibraryConfiguration;
+    this.app = app;
     this.backportedMethodListConsumer = backportedMethodListConsumer;
+    this.factory = factory;
+  }
+
+  InternalOptions getInternalOptions() {
+    InternalOptions options = new InternalOptions(factory, getReporter());
+    options.minApiLevel = minApiLevel;
+    options.desugaredLibraryConfiguration = desugaredLibraryConfiguration;
+    return options;
   }
 
   public static Builder builder() {
@@ -67,14 +118,11 @@
   }
 
   public static Builder parse(String[] args) {
-    final Set<String> OPTIONS_WITH_PARAMETER = ImmutableSet.of("--output", "--min-api");
+    final Set<String> OPTIONS_WITH_PARAMETER =
+        ImmutableSet.of("--output", "--min-api", "--desugared-lib", "--lib");
 
     boolean hasDefinedApiLevel = false;
     Builder builder = builder();
-    if (args.length == 0) {
-      builder.setPrintHelp(true);
-      return builder;
-    }
     for (int i = 0; i < args.length; i++) {
       String arg = args[i].trim();
       String nextArg = null;
@@ -90,13 +138,17 @@
         builder.setPrintHelp(true);
       } else if (arg.equals("--version")) {
         builder.setPrintVersion(true);
-      } else if (arg.equals(" --min-api <number>")) {
+      } else if (arg.equals("--min-api")) {
         if (hasDefinedApiLevel) {
           builder.error(new StringDiagnostic("Cannot set multiple --min-api options"));
         } else {
           parseMinApi(builder, nextArg);
           hasDefinedApiLevel = true;
         }
+      } else if (arg.equals("--desugared-lib")) {
+        builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+      } else if (arg.equals("--lib")) {
+        builder.addLibraryFiles(Paths.get(nextArg));
       } else if (arg.equals("--output")) {
         builder.setOutputPath(Paths.get(nextArg));
       } else {
@@ -125,7 +177,9 @@
   public static class Builder {
 
     private final Reporter reporter;
-    private int minApiLevel;
+    private int minApiLevel = AndroidApiLevel.B.getLevel();
+    private List<StringResource> desugaredLibraryConfigurationResources = new ArrayList<>();
+    private final AndroidApp.Builder app;
     private StringConsumer backportedMethodListConsumer;
     private boolean printHelp = false;
     private boolean printVersion = false;
@@ -135,9 +189,17 @@
     }
 
     private Builder(DiagnosticsHandler diagnosticsHandler) {
+      this.app = AndroidApp.builder();
       this.reporter = new Reporter(diagnosticsHandler);
     }
 
+    /**
+     * Set the minimum API level for the application compiled.
+     *
+     * <p>The tool will only report backported methods which are not present at this API level.
+     *
+     * <p>The default is 1 if never set.
+     */
     public Builder setMinApiLevel(int minApiLevel) {
       if (minApiLevel <= 0) {
         reporter.error(new StringDiagnostic("Invalid minApiLevel: " + minApiLevel));
@@ -147,6 +209,57 @@
       return this;
     }
 
+    public int getMinApiLevel() {
+      return minApiLevel;
+    }
+
+    /** Desugared library configuration */
+    public Builder addDesugaredLibraryConfiguration(StringResource configuration) {
+      desugaredLibraryConfigurationResources.add(configuration);
+      return this;
+    }
+
+    /** Desugared library configuration */
+    public Builder addDesugaredLibraryConfiguration(String configuration) {
+      return addDesugaredLibraryConfiguration(
+          StringResource.fromString(configuration, Origin.unknown()));
+    }
+
+    /** The compilation SDK library (android.jar) */
+    public Builder addLibraryResourceProvider(ClassFileResourceProvider provider) {
+      app.addLibraryResourceProvider(provider);
+      return this;
+    }
+
+    /** The compilation SDK library (android.jar) */
+    public Builder addLibraryFiles(Path... files) {
+      addLibraryFiles(Arrays.asList(files));
+      return this;
+    }
+
+    /** The compilation SDK library (android.jar) */
+    public Builder addLibraryFiles(Collection<Path> files) {
+      for (Path path : files) {
+        app.addLibraryFile(path);
+      }
+      return this;
+    }
+
+    DesugaredLibraryConfiguration getDesugaredLibraryConfiguration(DexItemFactory factory) {
+      if (desugaredLibraryConfigurationResources.isEmpty()) {
+        return DesugaredLibraryConfiguration.empty();
+      }
+      if (desugaredLibraryConfigurationResources.size() > 1) {
+        reporter.fatalError("Only one desugared library configuration is supported.");
+      }
+      StringResource desugaredLibraryConfigurationResource =
+          desugaredLibraryConfigurationResources.get(0);
+      DesugaredLibraryConfigurationParser libraryParser =
+          new DesugaredLibraryConfigurationParser(factory, null, false, getMinApiLevel());
+      return libraryParser.parse(desugaredLibraryConfigurationResource);
+    }
+
+    /** Output file for the backported method list */
     public Builder setOutputPath(Path outputPath) {
       backportedMethodListConsumer =
           new StringConsumer.FileConsumer(outputPath) {
@@ -159,24 +272,29 @@
       return this;
     }
 
+    /** Consumer receiving the the backported method list */
     public Builder setConsumer(StringConsumer consumer) {
       this.backportedMethodListConsumer = consumer;
       return this;
     }
 
+    /** True if the print-help flag is enabled. */
     public boolean isPrintHelp() {
       return printHelp;
     }
 
+    /** Set the value of the print-help flag. */
     public Builder setPrintHelp(boolean printHelp) {
       this.printHelp = printHelp;
       return this;
     }
 
+    /** True if the print-version flag is enabled. */
     public boolean isPrintVersion() {
       return printVersion;
     }
 
+    /** Set the value of the print-version flag. */
     public Builder setPrintVersion(boolean printVersion) {
       this.printVersion = printVersion;
       return this;
@@ -187,9 +305,17 @@
     }
 
     public BackportedMethodListCommand build() {
+      AndroidApp library = app.build();
+      if (!desugaredLibraryConfigurationResources.isEmpty()
+          && library.getLibraryResourceProviders().isEmpty()) {
+        reporter.error(
+            new StringDiagnostic("With desugared library configuration a library is required"));
+      }
+
       if (isPrintHelp() || isPrintVersion()) {
         return new BackportedMethodListCommand(isPrintHelp(), isPrintVersion());
       }
+
       if (backportedMethodListConsumer == null) {
         backportedMethodListConsumer =
             new StringConsumer() {
@@ -202,8 +328,14 @@
               public void finished(DiagnosticsHandler handler) {}
             };
       }
+      DexItemFactory factory = new DexItemFactory();
       return new BackportedMethodListCommand(
-          reporter, minApiLevel, backportedMethodListConsumer);
+          reporter,
+          minApiLevel,
+          getDesugaredLibraryConfiguration(factory),
+          library,
+          backportedMethodListConsumer,
+          factory);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index d13081c..89aa64e 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -79,6 +79,9 @@
       case "l8":
         L8.main(shift(args));
         break;
+      case "backportedmethods":
+        BackportedMethodList.main(shift(args));
+        break;
       default:
         runDefault(args);
         break;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 52e363d..6ff25d9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -4,12 +4,18 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
+import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -45,11 +51,14 @@
 import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 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.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -94,15 +103,29 @@
             && appView.options().minApiLevel <= AndroidApiLevel.LATEST.getLevel();
   }
 
-  public static List<DexMethod> generateListOfBackportedMethods(AndroidApiLevel apiLevel) {
-    List<DexMethod> methods = new ArrayList<>();
-    InternalOptions options = new InternalOptions();
-    options.minApiLevel = apiLevel.getLevel();
-    AppView<?> appView = AppView.createForD8(null, options);
-    BackportedMethodRewriter.RewritableMethods rewritableMethods =
-        new BackportedMethodRewriter.RewritableMethods(options, appView);
-    rewritableMethods.visit(methods::add);
-    return methods;
+  public static List<DexMethod> generateListOfBackportedMethods(
+      AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
+    try {
+      List<DexMethod> methods = new ArrayList<>();
+      PrefixRewritingMapper rewritePrefix =
+          options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
+      AppInfo appInfo = null;
+      if (androidApp != null) {
+        DexApplication app =
+            new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
+        appInfo =
+            options.desugaredLibraryConfiguration.getRewritePrefix().isEmpty()
+                ? new AppInfo(app)
+                : new AppInfoWithClassHierarchy(app);
+      }
+      AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
+      BackportedMethodRewriter.RewritableMethods rewritableMethods =
+          new BackportedMethodRewriter.RewritableMethods(options, appView);
+      rewritableMethods.visit(methods::add);
+      return methods;
+    } catch (ExecutionException e) {
+      throw unwrapExecutionException(e);
+    }
   }
 
   public void desugar(IRCode code) {
diff --git a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
index 358e775..bb344d7 100644
--- a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
+++ b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
@@ -5,20 +5,42 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import org.junit.Assume;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+@RunWith(Parameterized.class)
 public class BackportedMethodListTest {
 
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
+  enum Mode {
+    NO_LIBRARY,
+    LIBRARY,
+    LIBRARY_DESUGAR
+  }
+
+  @Parameterized.Parameters(name = "Mode: {0}")
+  public static Object[] data() {
+    return Mode.values();
+  }
+
+  private final Mode mode;
+
+  public BackportedMethodListTest(Mode mode) {
+    this.mode = mode;
+  }
+
   private static class ListStringConsumer implements StringConsumer {
     List<String> strings = new ArrayList<>();
     boolean finished = false;
@@ -45,16 +67,16 @@
         apiLevel < AndroidApiLevel.O.getLevel(),
         backports.contains("java/lang/Short#toUnsignedLong(S)J"));
 
-    // Java 9, 10 and 11 Optional methods.
+    // Java 9, 10 and 11 Optional methods which require Android N or library desugaring.
     assertEquals(
-        apiLevel >= AndroidApiLevel.N.getLevel(),
+        mode == Mode.LIBRARY_DESUGAR || apiLevel >= AndroidApiLevel.N.getLevel(),
         backports.contains(
             "java/util/Optional#or(Ljava/util/function/Supplier;)Ljava/util/Optional;"));
     assertEquals(
-        apiLevel >= AndroidApiLevel.N.getLevel(),
+        mode == Mode.LIBRARY_DESUGAR || apiLevel >= AndroidApiLevel.N.getLevel(),
         backports.contains("java/util/OptionalInt#orElseThrow()I"));
     assertEquals(
-        apiLevel >= AndroidApiLevel.N.getLevel(),
+        mode == Mode.LIBRARY_DESUGAR || apiLevel >= AndroidApiLevel.N.getLevel(),
         backports.contains("java/util/OptionalLong#isEmpty()Z"));
 
     // Java 9, 10 and 11 methods.
@@ -63,18 +85,26 @@
     assertTrue(backports.contains("java/lang/Character#toString(I)Ljava/lang/String;"));
   }
 
+  private void addLibraryDesugaring(BackportedMethodListCommand.Builder builder) {
+    builder
+        .addDesugaredLibraryConfiguration(
+            StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()));
+  }
+
   @Test
   public void testConsumer() throws Exception {
     for (int apiLevel = 1; apiLevel < AndroidApiLevel.LATEST.getLevel(); apiLevel++) {
       ListStringConsumer consumer = new ListStringConsumer();
-      BackportedMethodList.run(
-          BackportedMethodListCommand.builder()
-              .setMinApiLevel(apiLevel)
-              .setConsumer(consumer)
-              .build());
-
+      BackportedMethodListCommand.Builder builder =
+          BackportedMethodListCommand.builder().setMinApiLevel(apiLevel).setConsumer(consumer);
+      if (mode == Mode.LIBRARY) {
+        builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()));
+      } else if (mode == Mode.LIBRARY_DESUGAR) {
+        addLibraryDesugaring(builder);
+      }
+      BackportedMethodList.run(builder.build());
       assertTrue(consumer.finished);
-
       checkContent(apiLevel, consumer.strings);
     }
   }
@@ -83,13 +113,42 @@
   public void testFile() throws Exception {
     for (int apiLevel = 1; apiLevel < AndroidApiLevel.LATEST.getLevel(); apiLevel++) {
       Path output = temp.newFile().toPath();
+      BackportedMethodListCommand.Builder builder =
+          BackportedMethodListCommand.builder().setMinApiLevel(apiLevel).setOutputPath(output);
+      if (mode == Mode.LIBRARY) {
+        builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()));
+      } else if (mode == Mode.LIBRARY_DESUGAR) {
+        addLibraryDesugaring(builder);
+      }
+      BackportedMethodList.run(builder.build());
+      checkContent(apiLevel, Files.readAllLines(output));
+    }
+  }
+
+  @Test
+  public void testFullList() throws Exception {
+    Assume.assumeTrue(mode == Mode.NO_LIBRARY);
+    ListStringConsumer consumer = new ListStringConsumer();
+    // Not setting neither min API level not library should produce the full list.
+    BackportedMethodList.run(BackportedMethodListCommand.builder().setConsumer(consumer).build());
+    assertTrue(consumer.finished);
+    checkContent(1, consumer.strings);
+  }
+
+  @Test
+  public void requireLibraryForDesugar() {
+    Assume.assumeTrue(mode == Mode.LIBRARY_DESUGAR);
+    // Require library when a desugar configuration is passed.
+    try {
       BackportedMethodList.run(
           BackportedMethodListCommand.builder()
-              .setMinApiLevel(apiLevel)
-              .setOutputPath(output)
+              .addDesugaredLibraryConfiguration(
+                  StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+              .setConsumer(new ListStringConsumer())
               .build());
-
-      checkContent(apiLevel, Files.readAllLines(output));
+      fail("Expected failure");
+    } catch (CompilationFailedException e) {
+      // Expected.
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index 2c7cbe5..83682b2 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -13,6 +13,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -34,8 +36,11 @@
       // Check that the backported methods for each API level are are not present in the
       // android.jar for that level.
       CodeInspector inspector = new CodeInspector(ToolHelper.getAndroidJar(apiLevel));
+      InternalOptions options = new InternalOptions();
+      options.minApiLevel = apiLevel.getLevel();
       List<DexMethod> backportedMethods =
-          BackportedMethodRewriter.generateListOfBackportedMethods(apiLevel);
+          BackportedMethodRewriter.generateListOfBackportedMethods(
+              null, options, ThreadUtils.getExecutorService(options));
       for (DexMethod method : backportedMethods) {
         // Two different DexItemFactories are in play, but as toSourceString is used for lookup
         // that is not an issue.