Use Library Configuration JSON

- allow to use a JSON file instead of the
  SpecialLibraryConfiguration hardcoded hack.
- Add a parser for library configuration.
- Add test to check SpecialLibraryConfiguration
  against JSON parsed configuration.
- Move LibraryConfiguration to its own structure.

Bug:134732760
Change-Id: Iaa9979055b6a5d1cf05a5b9c4b796e267d1e1d0b
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 05c4147..9918583 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -3,7 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
+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.FileUtils;
@@ -33,7 +38,6 @@
   private final boolean enableDesugaring;
   private final boolean includeClassesChecksum;
   private final boolean optimizeMultidexForLinearAlloc;
-  private String specialLibraryConfiguration;
   private final BiPredicate<String, Long> dexClassChecksumFilter;
 
   BaseCompilerCommand(boolean printHelp, boolean printVersion) {
@@ -58,7 +62,6 @@
       Reporter reporter,
       boolean enableDesugaring,
       boolean optimizeMultidexForLinearAlloc,
-      String specialLibraryConfiguration,
       boolean includeClassesChecksum,
       BiPredicate<String, Long> dexClassChecksumFilter) {
     super(app);
@@ -71,7 +74,6 @@
     this.reporter = reporter;
     this.enableDesugaring = enableDesugaring;
     this.optimizeMultidexForLinearAlloc = optimizeMultidexForLinearAlloc;
-    this.specialLibraryConfiguration = specialLibraryConfiguration;
     this.includeClassesChecksum = includeClassesChecksum;
     this.dexClassChecksumFilter = dexClassChecksumFilter;
   }
@@ -132,10 +134,6 @@
     return reporter;
   }
 
-  public String getSpecialLibraryConfiguration() {
-    return specialLibraryConfiguration;
-  }
-
   /**
    * Base builder for compilation commands.
    *
@@ -156,7 +154,7 @@
     private CompilationMode mode;
     private int minApiLevel = 0;
     private boolean disableDesugaring = false;
-    private String specialLibraryConfiguration;
+    private List<StringResource> desugaredLibraryConfigurationResources = new ArrayList<>();
     private boolean includeClassesChecksum = false;
     private boolean lookupLibraryBeforeProgram = true;
     private boolean optimizeMultidexForLinearAlloc = false;
@@ -225,8 +223,8 @@
      * Get the program consumer.
      *
      * @return The currently set program consumer, null if no program consumer or output
-     *     path-and-mode is set, e.g., neither {@link #setProgramConsumer} nor
-     *     {@link #setOutput} have been called.
+     *     path-and-mode is set, e.g., neither {@link #setProgramConsumer} nor {@link #setOutput}
+     *     have been called.
      */
     public ProgramConsumer getProgramConsumer() {
       return programConsumer;
@@ -429,14 +427,54 @@
       return disableDesugaring;
     }
 
-    /** Special library configuration */
-    public B addSpecialLibraryConfiguration(String configuration) {
-      this.specialLibraryConfiguration = configuration;
+    /** Desugared library configuration */
+    // Configuration "default" is for testing only and support will be dropped.
+    public B addDesugaredLibraryConfiguration(String configuration) {
+      this.desugaredLibraryConfigurationResources.add(
+          StringResource.fromString(configuration, Origin.unknown()));
       return self();
     }
 
-    public String getSpecialLibraryConfiguration() {
-      return specialLibraryConfiguration;
+    /** Desugared library configuration */
+    public B addDesugaredLibraryConfiguration(StringResource configuration) {
+      this.desugaredLibraryConfigurationResources.add(configuration);
+      return self();
+    }
+
+    DesugaredLibraryConfiguration getDesugaredLibraryConfiguration(
+        DexItemFactory factory, boolean libraryCompilation, int minAPILevel) {
+      if (desugaredLibraryConfigurationResources.isEmpty()) {
+        return DesugaredLibraryConfiguration.empty();
+      }
+      if (desugaredLibraryConfigurationResources.size() > 1) {
+        throw new CompilationError("Only one desugared library configuration is supported.");
+      }
+      StringResource desugaredLibraryConfigurationResource =
+          desugaredLibraryConfigurationResources.get(0);
+
+      // TODO(b/134732760): Remove the following once the default testing hack is not supported
+      // anymore.
+      try {
+        if (desugaredLibraryConfigurationResource.getString().equals("default")) {
+          if (libraryCompilation) {
+            return DesugaredLibraryConfigurationForTesting
+                .configureLibraryDesugaringForLibraryCompilation(minAPILevel);
+          }
+          return DesugaredLibraryConfigurationForTesting
+              .configureLibraryDesugaringForProgramCompilation(minAPILevel);
+        }
+      } catch (ResourceException e) {
+        throw new RuntimeException(e);
+      }
+
+      DesugaredLibraryConfigurationParser libraryParser =
+          new DesugaredLibraryConfigurationParser(
+              factory, getReporter(), libraryCompilation, getMinApiLevel());
+      return libraryParser.parse(desugaredLibraryConfigurationResource);
+    }
+
+    boolean hasDesugaredLibraryConfiguration(){
+      return desugaredLibraryConfigurationResources.size() == 1;
     }
 
     /** Encodes checksum for each class when generating dex files. */
@@ -482,12 +520,11 @@
         }
         reporter.error(builder.toString());
       }
-      if (specialLibraryConfiguration != null) {
+      if (!desugaredLibraryConfigurationResources.isEmpty()) {
         reporter.warning(
-            new StringDiagnostic("Special library configuration is still work in progress"));
+            new StringDiagnostic("Desugared library configuration is still work in progress"));
       }
       super.validate();
     }
   }
-
 }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 4d23aef..4eca620 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -79,26 +80,34 @@
       super(app);
     }
 
-    /** Add dex program-data. */
+    /**
+     * Add dex program-data.
+     */
     @Override
     public Builder addDexProgramData(byte[] data, Origin origin) {
       guard(() -> getAppBuilder().addDexProgramData(data, origin));
       return self();
     }
 
-    /** Add classpath file resources. These have @Override to ensure binary compatibility. */
+    /**
+     * Add classpath file resources. These have @Override to ensure binary compatibility.
+     */
     @Override
     public Builder addClasspathFiles(Path... files) {
       return super.addClasspathFiles(files);
     }
 
-    /** Add classpath file resources. */
+    /**
+     * Add classpath file resources.
+     */
     @Override
     public Builder addClasspathFiles(Collection<Path> files) {
       return super.addClasspathFiles(files);
     }
 
-    /** Add classfile resources provider for class-path resources. */
+    /**
+     * Add classfile resources provider for class-path resources.
+     */
     @Override
     public Builder addClasspathResourceProvider(ClassFileResourceProvider provider) {
       return super.addClasspathResourceProvider(provider);
@@ -125,7 +134,9 @@
       return self();
     }
 
-    /** Get the consumer that will receive dependency information for desugaring. */
+    /**
+     * Get the consumer that will receive dependency information for desugaring.
+     */
     public DesugarGraphConsumer getDesugarGraphConsumer() {
       return desugarGraphConsumer;
     }
@@ -174,14 +185,8 @@
                   + " and above");
         }
       }
-      if (getSpecialLibraryConfiguration() != null) {
-        if (getDisableDesugaring()) {
-          reporter.error("Using special library configuration requires desugaring to be enabled");
-        }
-        if (!getSpecialLibraryConfiguration().equals("default")) {
-          reporter
-              .error("D8 currently requires the special library configuration to be \"default\"");
-        }
+      if (hasDesugaredLibraryConfiguration() && getDisableDesugaring()) {
+        reporter.error("Using desugared library configuration requires desugaring to be enabled");
       }
       super.validate();
     }
@@ -194,6 +199,10 @@
 
       intermediate |= getProgramConsumer() instanceof DexFilePerClassFileConsumer;
 
+      DexItemFactory factory = new DexItemFactory();
+      DesugaredLibraryConfiguration libraryConfiguration =
+          getDesugaredLibraryConfiguration(factory, false, getMinApiLevel());
+
       return new D8Command(
           getAppBuilder().build(),
           getMode(),
@@ -204,11 +213,12 @@
           !getDisableDesugaring(),
           intermediate,
           isOptimizeMultidexForLinearAlloc(),
-          getSpecialLibraryConfiguration(),
           getIncludeClassesChecksum(),
           getDexClassChecksumFilter(),
           getDesugarGraphConsumer(),
-          desugaredLibraryKeepRuleConsumer);
+          desugaredLibraryKeepRuleConsumer,
+          libraryConfiguration,
+          factory);
     }
   }
 
@@ -217,6 +227,8 @@
   private final boolean intermediate;
   private final DesugarGraphConsumer desugarGraphConsumer;
   private final StringConsumer desugaredLibraryKeepRuleConsumer;
+  private final DesugaredLibraryConfiguration libraryConfiguration;
+  private final DexItemFactory factory;
 
   public static Builder builder() {
     return new Builder();
@@ -268,11 +280,12 @@
       boolean enableDesugaring,
       boolean intermediate,
       boolean optimizeMultidexForLinearAlloc,
-      String specialLibraryConfiguration,
       boolean encodeChecksum,
       BiPredicate<String, Long> dexClassChecksumFilter,
       DesugarGraphConsumer desugarGraphConsumer,
-      StringConsumer desugaredLibraryKeepRuleConsumer) {
+      StringConsumer desugaredLibraryKeepRuleConsumer,
+      DesugaredLibraryConfiguration libraryConfiguration,
+      DexItemFactory factory) {
     super(
         inputApp,
         mode,
@@ -282,12 +295,13 @@
         diagnosticsHandler,
         enableDesugaring,
         optimizeMultidexForLinearAlloc,
-        specialLibraryConfiguration,
         encodeChecksum,
         dexClassChecksumFilter);
     this.intermediate = intermediate;
     this.desugarGraphConsumer = desugarGraphConsumer;
     this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+    this.libraryConfiguration = libraryConfiguration;
+    this.factory = factory;
   }
 
   private D8Command(boolean printHelp, boolean printVersion) {
@@ -295,15 +309,13 @@
     intermediate = false;
     desugarGraphConsumer = null;
     desugaredLibraryKeepRuleConsumer = null;
-  }
-
-  private void configureLibraryDesugaring(InternalOptions options) {
-    SpecialLibraryConfiguration.configureLibraryDesugaringForProgramCompilation(options);
+    libraryConfiguration = null;
+    factory = null;
   }
 
   @Override
   InternalOptions getInternalOptions() {
-    InternalOptions internal = new InternalOptions(new DexItemFactory(), getReporter());
+    InternalOptions internal = new InternalOptions(factory, getReporter());
     assert !internal.debug;
     internal.debug = getMode() == CompilationMode.DEBUG;
     internal.programConsumer = getProgramConsumer();
@@ -342,15 +354,7 @@
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
 
     // TODO(134732760): This is still work in progress.
-    assert internal.rewritePrefix.isEmpty();
-    assert internal.emulateLibraryInterface.isEmpty();
-    assert internal.retargetCoreLibMember.isEmpty();
-    assert internal.backportCoreLibraryMembers.isEmpty();
-    assert internal.dontRewriteInvocations.isEmpty();
-    if (getSpecialLibraryConfiguration() != null) {
-      configureLibraryDesugaring(internal);
-    }
-
+    internal.libraryConfiguration = libraryConfiguration;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
     return internal;
diff --git a/src/main/java/com/android/tools/r8/SpecialLibraryConfiguration.java b/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
similarity index 76%
rename from src/main/java/com/android/tools/r8/SpecialLibraryConfiguration.java
rename to src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
index 8f9bb7c..90d232b 100644
--- a/src/main/java/com/android/tools/r8/SpecialLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
@@ -4,19 +4,18 @@
 
 package com.android.tools.r8;
 
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.util.List;
 import java.util.Map;
 
 // TODO(134732760): This is still work in progress.
-// The SpecialLibraryConfiguration is a set of flags for experimentation
+// The DesugaredLibraryConfigurationForTesting is a set of flags for experimentation
 // with the desugared JDK libraries which should be replaced by a D8/R8
 // API level flag --coreLibraryDescriptor
-public class SpecialLibraryConfiguration {
+public class DesugaredLibraryConfigurationForTesting {
 
   private static Map<String, String> buildPrefixRewritingForProgramCompilationAllAndroid() {
     return ImmutableMap.<String, String>builder()
@@ -184,51 +183,67 @@
         .build();
   }
 
-  public static void configureLibraryDesugaringForProgramCompilation(InternalOptions options) {
+  public static DesugaredLibraryConfiguration configureLibraryDesugaringForProgramCompilation(
+      int minApiLevel) {
     // TODO(b/134732760): Make assertions in D8/R8 commands.
-    if (options.minApiLevel >= AndroidApiLevel.O.getLevel()) {
-      options.reporter.warning(
-          new StringDiagnostic(
-              "Desugaring core libraries for Android O and over is possible but not required."));
+    if (minApiLevel >= AndroidApiLevel.O.getLevel()) {
+      return DesugaredLibraryConfiguration.empty();
     }
-    options.coreLibraryCompilation = false;
-    if (options.minApiLevel < AndroidApiLevel.N.getLevel()) {
-      if (options.minApiLevel < AndroidApiLevel.M.getLevel()) {
-        options.rewritePrefix = buildPrefixRewritingForProgramCompilationAllAndroid();
+    Map<String, String> rewritePrefix;
+    Map<String, String> retargetCoreLibMember;
+    Map<String, String> emulateLibraryInterface = ImmutableMap.of();
+    List<String> dontRewriteInvocations = ImmutableList.of();
+    if (minApiLevel < AndroidApiLevel.N.getLevel()) {
+      if (minApiLevel < AndroidApiLevel.M.getLevel()) {
+        rewritePrefix = buildPrefixRewritingForProgramCompilationAllAndroid();
       } else {
-        options.rewritePrefix = buildPrefixRewritingForProgramCompilationAndroidOPlus();
+        rewritePrefix = buildPrefixRewritingForProgramCompilationAndroidOPlus();
       }
-      options.retargetCoreLibMember =
-          buildRetargetCoreLibraryMemberForProgramCompilationAllAndroid();
-      options.emulateLibraryInterface = buildEmulateLibraryInterface();
-      options.dontRewriteInvocations = buildDontRewriteInvocations();
+      retargetCoreLibMember = buildRetargetCoreLibraryMemberForProgramCompilationAllAndroid();
+      emulateLibraryInterface = buildEmulateLibraryInterface();
+      dontRewriteInvocations = buildDontRewriteInvocations();
     } else {
-      options.rewritePrefix = buildPrefixRewritingForProgramCompilationAndroidNPlus();
-      options.retargetCoreLibMember =
-          buildRetargetCoreLibraryMemberForProgramCompilationAndroidNPlus();
+      rewritePrefix = buildPrefixRewritingForProgramCompilationAndroidNPlus();
+      retargetCoreLibMember = buildRetargetCoreLibraryMemberForProgramCompilationAndroidNPlus();
     }
+    return new DesugaredLibraryConfiguration(
+        false,
+        rewritePrefix,
+        emulateLibraryInterface,
+        retargetCoreLibMember,
+        ImmutableMap.of(),
+        dontRewriteInvocations);
   }
 
-  public static void configureLibraryDesugaringForLibraryCompilation(InternalOptions options) {
+  public static DesugaredLibraryConfiguration configureLibraryDesugaringForLibraryCompilation(
+      int minApiLevel) {
     // TODO(b/134732760): Make assertions in L8 commands.
-    if (options.minApiLevel >= AndroidApiLevel.O.getLevel()) {
-      options.reporter.warning(
-          new StringDiagnostic(
-              "Desugaring core libraries for Android O and over is possible but not required."));
+    if (minApiLevel >= AndroidApiLevel.O.getLevel()) {
+      return DesugaredLibraryConfiguration.empty();
     }
-    options.coreLibraryCompilation = true;
-    options.backportCoreLibraryMembers = buildBackportCoreLibraryMembers();
-    if (options.minApiLevel < AndroidApiLevel.N.getLevel()) {
-      options.retargetCoreLibMember = buildRetargetCoreLibraryMemberForCoreLibCompilation();
-      options.dontRewriteInvocations = buildDontRewriteInvocations();
-      options.emulateLibraryInterface = buildEmulateLibraryInterface();
-      if (options.minApiLevel < AndroidApiLevel.M.getLevel()) {
-        options.rewritePrefix = buildPrefixRewritingForCoreLibCompilationAllAndroid();
+    Map<String, String> rewritePrefix;
+    Map<String, String> retargetCoreLibMember = ImmutableMap.of();
+    Map<String, String> emulateLibraryInterface = ImmutableMap.of();
+    List<String> dontRewriteInvocations = ImmutableList.of();
+    Map<String, String> backportCoreLibraryMembers = buildBackportCoreLibraryMembers();
+    if (minApiLevel < AndroidApiLevel.N.getLevel()) {
+      retargetCoreLibMember = buildRetargetCoreLibraryMemberForCoreLibCompilation();
+      dontRewriteInvocations = buildDontRewriteInvocations();
+      emulateLibraryInterface = buildEmulateLibraryInterface();
+      if (minApiLevel < AndroidApiLevel.M.getLevel()) {
+        rewritePrefix = buildPrefixRewritingForCoreLibCompilationAllAndroid();
       } else {
-        options.rewritePrefix = buildPrefixRewritingForCoreLibCompilationAndroidOPlus();
+        rewritePrefix = buildPrefixRewritingForCoreLibCompilationAndroidOPlus();
       }
     } else {
-      options.rewritePrefix = buildPrefixRewritingForCoreLibCompilationAndroidNPlus();
+      rewritePrefix = buildPrefixRewritingForCoreLibCompilationAndroidNPlus();
     }
+    return new DesugaredLibraryConfiguration(
+        true,
+        rewritePrefix,
+        emulateLibraryInterface,
+        retargetCoreLibMember,
+        backportCoreLibraryMembers,
+        dontRewriteInvocations);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index cc8f925..1c72b43 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -26,7 +26,9 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
-/** The L8 compiler. */
+/**
+ * The L8 compiler.
+ */
 @Keep
 public class L8 {
 
@@ -107,6 +109,10 @@
 
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
       app = new L8TreePruner(options).prune(app);
+      if (app.classes().size() == 0) {
+        // TODO(b/134732760): report error instead of fatalError.
+        throw options.reporter.fatalError("Empty desugared library.");
+      }
       AppInfo appInfo = new AppInfo(app);
 
       AppView<?> appView = AppView.createForL8(appInfo, options);
@@ -119,15 +125,15 @@
       assert appView.appInfo() == appInfo;
 
       new CfApplicationWriter(
-              app,
-              appView,
-              options,
-              options.getMarker(Tool.L8),
-              null,
-              GraphLense.getIdentityLense(),
-              PrefixRewritingNamingLens.createPrefixRewritingNamingLens(
-                  options, converter.getAdditionalRewritePrefix()),
-              null)
+          app,
+          appView,
+          options,
+          options.getMarker(Tool.L8),
+          null,
+          GraphLense.getIdentityLense(),
+          PrefixRewritingNamingLens.createPrefixRewritingNamingLens(
+              options, converter.getAdditionalRewritePrefix()),
+          null)
           .write(options.getClassFileConsumer(), executor);
       options.printWarnings();
     } catch (ExecutionException e) {
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 4933163..a22cd4d 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -6,6 +6,7 @@
 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.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
@@ -23,6 +24,8 @@
 
   private final D8Command d8Command;
   private final R8Command r8Command;
+  private final com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration libraryConfiguration;
+  private final DexItemFactory factory;
 
   boolean isShrinking() {
     return r8Command != null;
@@ -45,7 +48,8 @@
       StringConsumer mainDexListConsumer,
       int minApiLevel,
       Reporter diagnosticsHandler,
-      String specialLibraryConfiguration) {
+      DesugaredLibraryConfiguration libraryConfiguration,
+      DexItemFactory factory) {
     super(
         inputApp,
         mode,
@@ -55,21 +59,20 @@
         diagnosticsHandler,
         true,
         false,
-        specialLibraryConfiguration,
         false,
         (name, checksum) -> true);
     this.d8Command = d8Command;
     this.r8Command = r8Command;
+    this.libraryConfiguration = libraryConfiguration;
+    this.factory = factory;
   }
 
   private L8Command(boolean printHelp, boolean printVersion) {
     super(printHelp, printVersion);
     r8Command = null;
     d8Command = null;
-  }
-
-  private void configureLibraryDesugaring(InternalOptions options) {
-    SpecialLibraryConfiguration.configureLibraryDesugaringForLibraryCompilation(options);
+    libraryConfiguration = null;
+    factory = null;
   }
 
   protected static class DefaultL8DiagnosticsHandler implements DiagnosticsHandler {
@@ -98,7 +101,7 @@
 
   @Override
   InternalOptions getInternalOptions() {
-    InternalOptions internal = new InternalOptions(new DexItemFactory(), getReporter());
+    InternalOptions internal = new InternalOptions(factory, getReporter());
     assert !internal.debug;
     internal.debug = getMode() == CompilationMode.DEBUG;
     assert internal.mainDexListConsumer == null;
@@ -134,13 +137,7 @@
     internal.enableInheritanceClassInDexDistributor = false;
 
     // TODO(134732760): This is still work in progress.
-    assert internal.rewritePrefix.isEmpty();
-    assert internal.emulateLibraryInterface.isEmpty();
-    assert internal.retargetCoreLibMember.isEmpty();
-    assert internal.backportCoreLibraryMembers.isEmpty();
-    assert internal.dontRewriteInvocations.isEmpty();
-    assert getSpecialLibraryConfiguration().equals("default");
-    configureLibraryDesugaring(internal);
+    internal.libraryConfiguration = libraryConfiguration;
 
     return internal;
   }
@@ -187,10 +184,8 @@
     @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 (!hasDesugaredLibraryConfiguration()){
+        reporter.error("L8 requires a desugared library configuration");
       }
       if (getProgramConsumer() instanceof ClassFileConsumer) {
         reporter.error("L8 does not support compiling to class files");
@@ -216,6 +211,10 @@
         setMode(defaultCompilationMode());
       }
 
+      DexItemFactory factory = new DexItemFactory();
+      DesugaredLibraryConfiguration libraryConfiguration =
+          getDesugaredLibraryConfiguration(factory, true, getMinApiLevel());
+
       R8Command r8Command = null;
       D8Command d8Command = null;
 
@@ -265,7 +264,8 @@
           getMainDexListConsumer(),
           getMinApiLevel(),
           getReporter(),
-          getSpecialLibraryConfiguration());
+          libraryConfiguration,
+          factory);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index b4d0411..7050e40 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.origin.StandardOutOrigin;
@@ -283,7 +284,6 @@
      * Set a consumer for receiving the proguard configuration information.
      *
      * <p>Note that any subsequent calls to this method will replace the previous setting.
-     *
      * @param proguardConfigurationConsumer
      */
     public Builder setProguardConfigurationConsumer(StringConsumer proguardConfigurationConsumer) {
@@ -293,8 +293,6 @@
 
     /**
      * Set a consumer for receiving kept-graph events.
-     *
-     * @param graphConsumer
      */
     public Builder setKeptGraphConsumer(GraphConsumer graphConsumer) {
       this.keptGraphConsumer = graphConsumer;
@@ -303,8 +301,6 @@
 
     /**
      * Set a consumer for receiving kept-graph events for the content of the main-dex output.
-     *
-     * @param graphConsumer
      */
     public Builder setMainDexKeptGraphConsumer(GraphConsumer graphConsumer) {
       this.mainDexKeptGraphConsumer = graphConsumer;
@@ -398,14 +394,8 @@
       if (getProgramConsumer() instanceof ClassFileConsumer && isMinApiLevelSet()) {
         reporter.error("R8 does not support --min-api when compiling to class files");
       }
-      if (getSpecialLibraryConfiguration() != null) {
-        if (getDisableDesugaring()) {
-          reporter.error("Using special library configuration requires desugaring to be enabled");
-        }
-        if (!getSpecialLibraryConfiguration().equals("default")) {
-          reporter
-              .error("R8 currently requires the special library configuration to be \"default\"");
-        }
+      if (hasDesugaredLibraryConfiguration() && getDisableDesugaring()) {
+        reporter.error("Using desugared library configuration requires desugaring to be enabled");
       }
       super.validate();
     }
@@ -432,6 +422,9 @@
         mainDexKeepRules = parser.getConfig().getRules();
       }
 
+      DesugaredLibraryConfiguration libraryConfiguration =
+          getDesugaredLibraryConfiguration(factory, false, getMinApiLevel());
+
       ProguardConfigurationParser parser =
           new ProguardConfigurationParser(factory, reporter, allowTestProguardOptions);
       if (!proguardConfigs.isEmpty()) {
@@ -472,17 +465,17 @@
             }
           };
 
-      getAppBuilder().getProgramResourceProviders()
-          .stream()
+      getAppBuilder().getProgramResourceProviders().stream()
           .map(ProgramResourceProvider::getDataResourceProvider)
           .filter(Objects::nonNull)
-          .forEach(dataResourceProvider -> {
-              try {
-                dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
-              } catch (ResourceException e) {
-                reporter.error(new ExceptionDiagnostic(e));
-              }
-          });
+          .forEach(
+              dataResourceProvider -> {
+                try {
+                  dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
+                } catch (ResourceException e) {
+                  reporter.error(new ExceptionDiagnostic(e));
+                }
+              });
 
       if (disableTreeShaking) {
         configurationBuilder.disableShrinking();
@@ -526,10 +519,10 @@
               mainDexKeptGraphConsumer,
               syntheticProguardRulesConsumer,
               isOptimizeMultidexForLinearAlloc(),
-              getSpecialLibraryConfiguration(),
               getIncludeClassesChecksum(),
               getDexClassChecksumFilter(),
-              desugaredLibraryKeepRuleConsumer);
+              desugaredLibraryKeepRuleConsumer,
+              libraryConfiguration);
 
       return command;
     }
@@ -610,6 +603,7 @@
   private final GraphConsumer mainDexKeptGraphConsumer;
   private final Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer;
   private final StringConsumer desugaredLibraryKeepRuleConsumer;
+  private final DesugaredLibraryConfiguration libraryConfiguration;
 
   /** Get a new {@link R8Command.Builder}. */
   public static Builder builder() {
@@ -681,10 +675,10 @@
       GraphConsumer mainDexKeptGraphConsumer,
       Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
       boolean optimizeMultidexForLinearAlloc,
-      String specialLibraryConfiguration,
       boolean encodeChecksum,
       BiPredicate<String, Long> dexClassChecksumFilter,
-      StringConsumer desugaredLibraryKeepRuleConsumer) {
+      StringConsumer desugaredLibraryKeepRuleConsumer,
+      com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration libraryConfiguration) {
     super(
         inputApp,
         mode,
@@ -694,7 +688,6 @@
         reporter,
         enableDesugaring,
         optimizeMultidexForLinearAlloc,
-        specialLibraryConfiguration,
         encodeChecksum,
         dexClassChecksumFilter);
     assert proguardConfiguration != null;
@@ -714,6 +707,7 @@
     this.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer;
     this.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
     this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+    this.libraryConfiguration = libraryConfiguration;
   }
 
   private R8Command(boolean printHelp, boolean printVersion) {
@@ -733,6 +727,7 @@
     mainDexKeptGraphConsumer = null;
     syntheticProguardRulesConsumer = null;
     desugaredLibraryKeepRuleConsumer = null;
+    libraryConfiguration = null;
   }
 
   /** Get the enable-tree-shaking state. */
@@ -761,12 +756,13 @@
     // shaking for removing the lambda classes which should be revised later.
     internal.enableLambdaMerging = getEnableTreeShaking();
     assert !internal.ignoreMissingClasses;
-    internal.ignoreMissingClasses = proguardConfiguration.isIgnoreWarnings()
-        // TODO(70706667): We probably only want this in Proguard compatibility mode.
-        || (forceProguardCompatibility
-            && !proguardConfiguration.isOptimizing()
-            && !internal.isShrinking()
-            && !internal.isMinifying());
+    internal.ignoreMissingClasses =
+        proguardConfiguration.isIgnoreWarnings()
+            // TODO(70706667): We probably only want this in Proguard compatibility mode.
+            || (forceProguardCompatibility
+                && !proguardConfiguration.isOptimizing()
+                && !internal.isShrinking()
+                && !internal.isMinifying());
 
     assert !internal.verbose;
     internal.mainDexKeepRules = mainDexKeepRules;
@@ -847,24 +843,12 @@
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
 
     // TODO(134732760): This is still work in progress.
-    assert internal.rewritePrefix.isEmpty();
-    assert internal.emulateLibraryInterface.isEmpty();
-    assert internal.retargetCoreLibMember.isEmpty();
-    assert internal.backportCoreLibraryMembers.isEmpty();
-    assert internal.dontRewriteInvocations.isEmpty();
-    if (getSpecialLibraryConfiguration() != null) {
-      configureLibraryDesugaring(internal);
-    }
-
+    internal.libraryConfiguration = libraryConfiguration;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
     return internal;
   }
 
-  private void configureLibraryDesugaring(InternalOptions options) {
-    SpecialLibraryConfiguration.configureLibraryDesugaringForProgramCompilation(options);
-  }
-
   private static StringConsumer wrapStringConsumer(
       StringConsumer optionConsumer, boolean optionsFlag, Path optionFile) {
     if (optionsFlag) {
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 6f3ff04..37c9e12 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -21,8 +21,9 @@
 public abstract class CodeToKeep {
 
   static CodeToKeep createCodeToKeep(InternalOptions options, NamingLens namingLens) {
-    if ((!namingLens.hasPrefixRewritingLogic() && options.emulateLibraryInterface.isEmpty())
-        || options.coreLibraryCompilation) {
+    if ((!namingLens.hasPrefixRewritingLogic()
+            && options.libraryConfiguration.getEmulateLibraryInterface().isEmpty())
+        || options.isDesugaredLibraryCompilation()) {
       return new NopCodeToKeep();
     }
     return new DesugaredLibraryCodeToKeep(namingLens, options);
@@ -49,7 +50,8 @@
       // Any class implementing one interface should implement the other one.
       // Interface method desugaring should have created the types if emulatedLibraryInterfaces
       // are set.
-      for (String rewrittenName : options.emulateLibraryInterface.values()) {
+      for (String rewrittenName :
+          options.libraryConfiguration.getEmulateLibraryInterface().values()) {
         DexString descriptor =
             options.itemFactory.lookupString(DescriptorUtils.javaTypeToDescriptor(rewrittenName));
         assert descriptor != null;
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 0dac6a9..6ab56d3 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -222,7 +222,7 @@
 
     // A consumer can manage the generated keep rules.
     if (options.desugaredLibraryKeepRuleConsumer != null && !desugaredLibraryCodeToKeep.isNop()) {
-      assert !options.coreLibraryCompilation;
+      assert !options.isDesugaredLibraryCompilation();
       desugaredLibraryCodeToKeep.generateKeepRules(options);
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 7ef077a..4cc9ee8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -690,7 +690,7 @@
           || (arity == dexCode.getDebugInfo().parameters.length);
     } else {
       // TODO(b/134732760): Patch Cf debug info.
-      assert appView.options().coreLibraryCompilation;
+      assert appView.options().isDesugaredLibraryCompilation();
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 0cbef69..2b446b3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -201,7 +201,7 @@
             .map(prefix -> "L" + DescriptorUtils.getPackageBinaryNameFromJavaType(prefix))
             .map(options.itemFactory::createString)
             .collect(Collectors.toList());
-    if (options.coreLibraryCompilation) {
+    if (options.isDesugaredLibraryCompilation()) {
       // Specific L8 Settings.
       // BackportedMethodRewriter is needed for retarget core library members and backports.
       // InterfaceMethodRewriter is needed for emulated interfaces.
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 f4905df..b581486 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
@@ -79,13 +79,14 @@
     this.converter = converter;
     this.factory = appView.dexItemFactory();
     this.rewritableMethods = new RewritableMethods(appView);
-    for (String coreLibMember : appView.options().backportCoreLibraryMembers.keySet()) {
+    Map<String, String> backportCoreLibraryMembers =
+        appView.options().libraryConfiguration.getBackportCoreLibraryMember();
+    for (String coreLibMember : backportCoreLibraryMembers.keySet()) {
       DexType extraCoreLibMemberType =
           factory.createType(DescriptorUtils.javaTypeToDescriptor(coreLibMember));
       DexType coreLibMemberType =
           factory.createType(
-              DescriptorUtils.javaTypeToDescriptor(
-                  appView.options().backportCoreLibraryMembers.get(coreLibMember)));
+              DescriptorUtils.javaTypeToDescriptor(backportCoreLibraryMembers.get(coreLibMember)));
       this.backportCoreLibraryMembers.put(extraCoreLibMemberType, coreLibMemberType);
     }
   }
@@ -251,7 +252,7 @@
         initializeAndroidOMethodProviders(factory);
       }
 
-      if (options.rewritePrefix.containsKey("java.util.Optional")
+      if (options.libraryConfiguration.getRewritePrefix().containsKey("java.util.Optional")
           || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
         // These are currently not implemented at any API level in Android.
         // They however require the Optional class to be present, either through
@@ -264,7 +265,7 @@
       initializeJava9MethodProviders(factory);
       initializeJava11MethodProviders(factory);
 
-      if (!options.retargetCoreLibMember.isEmpty()) {
+      if (!options.libraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
         initializeRetargetCoreLibraryMembers(appView);
       }
     }
@@ -1075,7 +1076,8 @@
       String unqualifiedName =
           DescriptorUtils.getUnqualifiedClassNameFromDescriptor(clazz.toString());
       // Avoid duplicate class names between core lib dex file and program dex files.
-      String coreLibUtilitySuffix = appView.options().coreLibraryCompilation ? "$corelib" : "";
+      String coreLibUtilitySuffix =
+          appView.options().isDesugaredLibraryCompilation() ? "$corelib" : "";
       String descriptor =
           UTILITY_CLASS_DESCRIPTOR_PREFIX
               + '$'
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 6833067..e23772f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -80,7 +80,7 @@
     boolean desugaredLibraryLookup =
         superClass != null
             && superClass.isLibraryClass()
-            && appView.options().emulateLibraryInterface.size() > 0;
+            && appView.options().libraryConfiguration.getEmulateLibraryInterface().size() > 0;
 
     if (clazz.interfaces.isEmpty() && !desugaredLibraryLookup) {
       // Since superclass has already been processed and it has all missing methods
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
new file mode 100644
index 0000000..c98378a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2019, 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.ir.desugar;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.Reporter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DesugaredLibraryConfiguration {
+
+  // TODO(b/134732760): should use DexString, DexType, DexMethod or so on when possible.
+  private final boolean libraryCompilation;
+  private final Map<String, String> rewritePrefix;
+  private final Map<String, String> emulateLibraryInterface;
+  private final Map<String, String> retargetCoreLibMember;
+  private final Map<String, String> backportCoreLibraryMember;
+  private final List<String> dontRewriteInvocation;
+
+  public static Builder builder(DexItemFactory dexItemFactory, Reporter reporter) {
+    return new Builder(dexItemFactory, reporter);
+  }
+
+  public static DesugaredLibraryConfiguration empty() {
+    return new DesugaredLibraryConfiguration(
+        false,
+        ImmutableMap.of(),
+        ImmutableMap.of(),
+        ImmutableMap.of(),
+        ImmutableMap.of(),
+        ImmutableList.of());
+  }
+
+  public DesugaredLibraryConfiguration(
+      boolean libraryCompilation,
+      Map<String, String> rewritePrefix,
+      Map<String, String> emulateLibraryInterface,
+      Map<String, String> retargetCoreLibMember,
+      Map<String, String> backportCoreLibraryMember,
+      List<String> dontRewriteInvocation) {
+    this.libraryCompilation = libraryCompilation;
+    this.rewritePrefix = rewritePrefix;
+    this.emulateLibraryInterface = emulateLibraryInterface;
+    this.retargetCoreLibMember = retargetCoreLibMember;
+    this.backportCoreLibraryMember = backportCoreLibraryMember;
+    this.dontRewriteInvocation = dontRewriteInvocation;
+  }
+
+  public boolean isLibraryCompilation() {
+    return libraryCompilation;
+  }
+
+  public Map<String, String> getRewritePrefix() {
+    return rewritePrefix;
+  }
+
+  public Map<String, String> getEmulateLibraryInterface() {
+    return emulateLibraryInterface;
+  }
+
+  public Map<String, String> getRetargetCoreLibMember() {
+    return retargetCoreLibMember;
+  }
+
+  public Map<String, String> getBackportCoreLibraryMember() {
+    return backportCoreLibraryMember;
+  }
+
+  public List<String> getDontRewriteInvocation() {
+    return dontRewriteInvocation;
+  }
+
+  public static class Builder {
+
+    private final DexItemFactory factory;
+    private final Reporter reporter;
+
+    private boolean libraryCompilation = false;
+    private Map<String, String> rewritePrefix = new HashMap<>();
+    private Map<String, String> emulateLibraryInterface = new HashMap<>();
+    private Map<String, String> retargetCoreLibMember = new HashMap<>();
+    private Map<String, String> backportCoreLibraryMember = new HashMap<>();
+    private List<String> dontRewriteInvocation = new ArrayList<>();
+
+    public Builder(DexItemFactory dexItemFactory, Reporter reporter) {
+      this.factory = dexItemFactory;
+      this.reporter = reporter;
+    }
+
+    public Builder setProgramCompilation() {
+      libraryCompilation = false;
+      return this;
+    }
+
+    public Builder setLibraryCompilation() {
+      libraryCompilation = true;
+      return this;
+    }
+
+    public Builder putRewritePrefix(String prefix, String rewrittenPrefix) {
+      rewritePrefix.put(prefix, rewrittenPrefix);
+      return this;
+    }
+
+    public Builder putEmulateLibraryInterface(
+        String emulateLibraryItf, String rewrittenEmulateLibraryItf) {
+      emulateLibraryInterface.put(emulateLibraryItf, rewrittenEmulateLibraryItf);
+      return this;
+    }
+
+    public Builder putRetargetCoreLibMember(String retarget, String rewrittenRetarget) {
+      retargetCoreLibMember.put(retarget, rewrittenRetarget);
+      return this;
+    }
+
+    public Builder putBackportCoreLibraryMember(String backport, String rewrittenBackport) {
+      backportCoreLibraryMember.put(backport, rewrittenBackport);
+      return this;
+    }
+
+    public Builder addDontRewriteInvocation(String dontRewriteInvocation) {
+      this.dontRewriteInvocation.add(dontRewriteInvocation);
+      return this;
+    }
+
+    public DesugaredLibraryConfiguration build() {
+      return new DesugaredLibraryConfiguration(
+          libraryCompilation,
+          ImmutableMap.copyOf(rewritePrefix),
+          ImmutableMap.copyOf(emulateLibraryInterface),
+          ImmutableMap.copyOf(retargetCoreLibMember),
+          ImmutableMap.copyOf(backportCoreLibraryMember),
+          ImmutableList.copyOf(dontRewriteInvocation));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
new file mode 100644
index 0000000..73ddfa2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2019, 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.ir.desugar;
+
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.util.Map;
+
+public class DesugaredLibraryConfigurationParser {
+
+  private static final int MAX_SUPPORTED_VERSION = 1;
+
+  private final DesugaredLibraryConfiguration.Builder configurationBuilder;
+  private final Reporter reporter;
+  private final boolean libraryCompilation;
+  private final int minAPILevel;
+
+  public DesugaredLibraryConfigurationParser(
+      DexItemFactory dexItemFactory,
+      Reporter reporter,
+      boolean libraryCompilation,
+      int minAPILevel) {
+    this.reporter = reporter;
+    this.configurationBuilder = DesugaredLibraryConfiguration.builder(dexItemFactory, reporter);
+    this.minAPILevel = minAPILevel;
+    this.libraryCompilation = libraryCompilation;
+    if (libraryCompilation) {
+      configurationBuilder.setLibraryCompilation();
+    } else {
+      configurationBuilder.setProgramCompilation();
+    }
+  }
+
+  public DesugaredLibraryConfiguration parse(StringResource stringResource) {
+    String jsonConfigString;
+    try {
+      jsonConfigString = stringResource.getString();
+    } catch (Exception e) {
+      throw reporter.fatalError(
+          new StringDiagnostic("Cannot access desugared library configuration."));
+    }
+    JsonParser parser = new JsonParser();
+    JsonObject jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
+
+    int version = jsonConfig.get("configuration_format_version").getAsInt();
+    if (version > MAX_SUPPORTED_VERSION) {
+      throw reporter.fatalError(
+          new StringDiagnostic(
+              "Unsupported desugared library configuration version, please upgrade the D8/R8"
+                  + " compiler."));
+    }
+    JsonArray jsonFlags =
+        libraryCompilation
+            ? jsonConfig.getAsJsonArray("library_flags")
+            : jsonConfig.getAsJsonArray("program_flags");
+    for (JsonElement jsonFlagSet : jsonFlags) {
+      int api_level_below_or_equal =
+          jsonFlagSet.getAsJsonObject().get("api_level_below_or_equal").getAsInt();
+      if (minAPILevel > api_level_below_or_equal) {
+        continue;
+      }
+      parseFlags(jsonFlagSet.getAsJsonObject());
+    }
+    return configurationBuilder.build();
+  }
+
+  private void parseFlags(JsonObject jsonFlagSet) {
+    if (jsonFlagSet.has("rewrite_prefix")) {
+      for (Map.Entry<String, JsonElement> rewritePrefix :
+          jsonFlagSet.get("rewrite_prefix").getAsJsonObject().entrySet()) {
+        configurationBuilder.putRewritePrefix(
+            rewritePrefix.getKey(), rewritePrefix.getValue().getAsString());
+      }
+    }
+    if (jsonFlagSet.has("retarget_lib_member")) {
+      for (Map.Entry<String, JsonElement> retarget :
+          jsonFlagSet.get("retarget_lib_member").getAsJsonObject().entrySet()) {
+        configurationBuilder.putRetargetCoreLibMember(
+            retarget.getKey(), retarget.getValue().getAsString());
+      }
+    }
+    if (jsonFlagSet.has("backport")) {
+      for (Map.Entry<String, JsonElement> backport :
+          jsonFlagSet.get("backport").getAsJsonObject().entrySet()) {
+        configurationBuilder.putBackportCoreLibraryMember(
+            backport.getKey(), backport.getValue().getAsString());
+      }
+    }
+    if (jsonFlagSet.has("emulate_interface")) {
+      for (Map.Entry<String, JsonElement> itf :
+          jsonFlagSet.get("emulate_interface").getAsJsonObject().entrySet()) {
+        configurationBuilder.putEmulateLibraryInterface(itf.getKey(), itf.getValue().getAsString());
+      }
+    }
+    if (jsonFlagSet.has("dont_rewrite")) {
+      JsonArray dontRewrite = jsonFlagSet.get("dont_rewrite").getAsJsonArray();
+      for (JsonElement rewrite : dontRewrite) {
+        configurationBuilder.addDontRewriteInvocation(rewrite.getAsString());
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index a4fb48b..437d78d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -147,13 +147,14 @@
   }
 
   private void initializeEmulatedInterfaceVariables() {
-    for (String interfaceName : options.emulateLibraryInterface.keySet()) {
+    Map<String, String> emulateLibraryInterface =
+        options.libraryConfiguration.getEmulateLibraryInterface();
+    for (String interfaceName : emulateLibraryInterface.keySet()) {
       DexType interfaceType =
           factory.createType(DescriptorUtils.javaTypeToDescriptor(interfaceName));
       DexType rewrittenType =
           factory.createType(
-              DescriptorUtils.javaTypeToDescriptor(
-                  options.emulateLibraryInterface.get(interfaceName)));
+              DescriptorUtils.javaTypeToDescriptor(emulateLibraryInterface.get(interfaceName)));
       emulatedInterfaces.put(interfaceType, rewrittenType);
       addRewritePrefix(interfaceType, rewrittenType.toSourceString());
       DexClass emulatedInterfaceClass = appView.definitionFor(interfaceType);
@@ -164,10 +165,10 @@
         }
       }
     }
-    if (appView.options().coreLibraryCompilation) {
+    if (appView.options().isDesugaredLibraryCompilation()) {
       options.populateRetargetCoreLibMember(factory, retargetCoreMember);
     }
-    for (String dontRewrite : options.dontRewriteInvocations) {
+    for (String dontRewrite : options.libraryConfiguration.getDontRewriteInvocation()) {
       int index = dontRewrite.lastIndexOf('#');
       if (index <= 0 || index >= dontRewrite.length() - 1) {
         throw new CompilationError(
@@ -382,7 +383,7 @@
           // Outside of core libraries, only library classes are rewritten. In core libraries,
           // some classes are present both as program and library class, and definitionFor
           // answers the program class so this is not true.
-          if (!appView.options().coreLibraryCompilation && !dexClass.isLibraryClass()) {
+          if (!appView.options().isDesugaredLibraryCompilation() && !dexClass.isLibraryClass()) {
             continue;
           }
           // We always rewrite interfaces, but classes are rewritten only if they are not already
@@ -548,11 +549,11 @@
   }
 
   private boolean isInDesugaredLibrary(DexClass clazz) {
-    assert clazz.isLibraryClass() || options.coreLibraryCompilation;
+    assert clazz.isLibraryClass() || options.isDesugaredLibraryCompilation();
     if (emulatedInterfaces.containsKey(clazz.type)) {
       return true;
     }
-    return clazz.type.rewritingPrefixIn(options.rewritePrefix) != null;
+    return clazz.type.rewritingPrefixIn(options.libraryConfiguration.getRewritePrefix()) != null;
   }
 
   private boolean dontRewrite(DexMethod method) {
@@ -902,7 +903,9 @@
       if (clazz.isInterface()) {
         DexType newType = inferEmulatedInterfaceName(clazz);
         if (newType != null
-            && clazz.type.rewritingPrefixIn(appView.options().rewritePrefix) == null) {
+            && clazz.type.rewritingPrefixIn(
+                    appView.options().libraryConfiguration.getRewritePrefix())
+                == null) {
           // We do not rewrite if it is already going to be rewritten using the a rewritingPrefix.
           addRewritePrefix(clazz.type, newType.toString());
           renameEmulatedInterfaces(clazz, newType);
@@ -982,7 +985,7 @@
       Flavor flavour,
       ExecutorService executorService)
       throws ExecutionException {
-    if (appView.options().coreLibraryCompilation) {
+    if (appView.options().isDesugaredLibraryCompilation()) {
       generateEmulateInterfaceLibrary(builder);
     }
     duplicateEmulatedInterfaces();
@@ -1004,7 +1007,7 @@
       appInfo.addSynthesizedClass(synthesizedClass);
     }
 
-    if (appView.options().coreLibraryCompilation) {
+    if (appView.options().isDesugaredLibraryCompilation()) {
       renameEmulatedInterfaces();
     }
 
@@ -1086,7 +1089,8 @@
     // Companion/Emulated interface classes for desugared library won't be missing,
     // they are in the desugared library.
     if (prefixRewritingInterfaces.containsKey(missing.toString())
-        || missing.rewritingPrefixIn(appView.options().rewritePrefix) != null) {
+        || missing.rewritingPrefixIn(appView.options().libraryConfiguration.getRewritePrefix())
+            != null) {
       return;
     }
     DexMethod method = appView.graphLense().getOriginalMethodSignature(referencedFrom);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 550eb37..5b58d86 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -519,7 +519,7 @@
       // The only case where we do Lambda desugaring with Cf to Cf is in L8.
       // If the compilation is not coreLibraryCompilation, then the assertion
       // implMethodHolder != null may fail, hence the assertion.
-      assert rewriter.converter.appView.options().coreLibraryCompilation;
+      assert rewriter.converter.appView.options().isDesugaredLibraryCompilation();
       DexMethod implMethod = descriptor.implHandle.asMethod();
       DexClass implMethodHolder = definitionFor(implMethod.holder);
       if (implMethodHolder == null) {
@@ -527,7 +527,8 @@
             .converter
             .appView
             .options()
-            .backportCoreLibraryMembers
+            .libraryConfiguration
+            .getBackportCoreLibraryMember()
             .containsKey(implMethod.holder.toString());
         return false;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 3dbb5d0..d359630 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -280,11 +280,14 @@
               knownLambdaClasses,
               lambdaClassType,
               new LambdaClass(this, accessedFrom, lambdaClassType, descriptor));
-      if (appView.options().coreLibraryCompilation) {
+      if (appView.options().isDesugaredLibraryCompilation()) {
         Pair<String, String> rewriting =
-            accessedFrom.rewritingPrefixIn(appView.options().rewritePrefix);
+            accessedFrom.rewritingPrefixIn(
+                appView.options().libraryConfiguration.getRewritePrefix());
         if (rewriting == null) {
-          rewriting = accessedFrom.rewritingPrefixIn(appView.options().emulateLibraryInterface);
+          rewriting =
+              accessedFrom.rewritingPrefixIn(
+                  appView.options().libraryConfiguration.getEmulateLibraryInterface());
         }
         if (rewriting != null) {
           addRewritingPrefix(rewriting, lambdaClassType);
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 71def8b..abe34ca 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -116,7 +116,7 @@
     }
     String markerString = marker.toString();
     for (DexProgramClass clazz : application.classes()) {
-      if (clazz.getSynthesizedFrom().isEmpty() || options.coreLibraryCompilation) {
+      if (clazz.getSynthesizedFrom().isEmpty() || options.isDesugaredLibraryCompilation()) {
         writeClass(clazz, consumer, markerString);
       } else {
         throw new Unimplemented("No support for synthetics in the Java bytecode backend.");
@@ -205,7 +205,7 @@
       // In this case bridges have been introduced for the Cf back-end,
       // which do not have class file version.
       assert options.testing.enableForceNestBasedAccessDesugaringForTest
-          || options.coreLibraryCompilation;
+          || options.isDesugaredLibraryCompilation();
       return 0;
     }
     return method.getClassFileVersion();
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index 6b24a34..914176e 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -42,7 +42,8 @@
 
   public static NamingLens createPrefixRewritingNamingLens(
       InternalOptions options, Map<String, String> additionalRewritePrefix, NamingLens namingLens) {
-    if (options.rewritePrefix.isEmpty() && additionalRewritePrefix.isEmpty()) {
+    if (options.libraryConfiguration.getRewritePrefix().isEmpty()
+        && additionalRewritePrefix.isEmpty()) {
       return namingLens;
     }
     return new PrefixRewritingNamingLens(namingLens, options, additionalRewritePrefix);
@@ -59,7 +60,7 @@
             descriptorPrefixRewriting.put(
                 "L" + DescriptorUtils.getBinaryNameFromJavaType(from),
                 "L" + DescriptorUtils.getBinaryNameFromJavaType(to));
-    options.rewritePrefix.forEach(lambda);
+    options.libraryConfiguration.getRewritePrefix().forEach(lambda);
     additionalRewritePrefix.forEach(lambda);
     // Run over all types and remap types with matching prefixes.
     // TODO(134732760): Use a more efficient data structure (prefix tree/trie).
@@ -169,7 +170,7 @@
     // If compiling the program using a desugared library, nothing needs to be printed.
     // If compiling the desugared library, the mapping needs to be printed.
     // When debugging the program, both mapping files need to be merged.
-    if (options.coreLibraryCompilation) {
+    if (options.isDesugaredLibraryCompilation()) {
       classRenaming.keySet().forEach(consumer);
     }
     namingLens.forAllRenamedTypes(consumer);
diff --git a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
index 5c7deb1..0b6531e 100644
--- a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
@@ -36,14 +36,16 @@
   }
 
   private void initializeEmulatedInterfaces() {
-    for (String rewrittenName : options.emulateLibraryInterface.keySet()) {
+    for (String rewrittenName :
+        options.libraryConfiguration.getEmulateLibraryInterface().keySet()) {
       emulatedInterfaces.add(
           options.itemFactory.createType(DescriptorUtils.javaTypeToDescriptor(rewrittenName)));
     }
   }
 
   private void initializeBackports() {
-    for (String backportName : options.backportCoreLibraryMembers.keySet()) {
+    for (String backportName :
+        options.libraryConfiguration.getBackportCoreLibraryMember().keySet()) {
       backports.add(
           options.itemFactory.createType(DescriptorUtils.javaTypeToDescriptor(backportName)));
     }
@@ -56,7 +58,7 @@
     }
     List<DexProgramClass> toKeep = new ArrayList<>();
     for (DexProgramClass aClass : app.classes()) {
-      if (aClass.type.rewritingPrefixIn(options.rewritePrefix) != null
+      if (aClass.type.rewritingPrefixIn(options.libraryConfiguration.getRewritePrefix()) != null
           || emulatedInterfaces.contains(aClass.type)
           || interfaceImplementsEmulatedInterface(aClass, typeMap)) {
         toKeep.add(aClass);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 6c01902..51ea8a0 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -32,6 +32,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
@@ -43,7 +44,6 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import java.io.IOException;
@@ -307,8 +307,13 @@
     throw new UnsupportedOperationException("Cannot find internal output mode.");
   }
 
+  public boolean isDesugaredLibraryCompilation() {
+    return libraryConfiguration.isLibraryCompilation();
+  }
+
   public boolean shouldKeepStackMapTable() {
-    return coreLibraryCompilation || getProguardConfiguration().getKeepAttributes().stackMapTable;
+    return isDesugaredLibraryCompilation()
+        || getProguardConfiguration().getKeepAttributes().stackMapTable;
   }
 
   public boolean isGeneratingDex() {
@@ -439,15 +444,9 @@
    */
   public boolean enableInheritanceClassInDexDistributor = true;
 
-  public boolean coreLibraryCompilation = false;
-  public Map<String, String> rewritePrefix = ImmutableMap.of();
-  public Map<String, String> emulateLibraryInterface = ImmutableMap.of();
-  public Map<String, String> retargetCoreLibMember = ImmutableMap.of();
-  public Map<String, String> backportCoreLibraryMembers = ImmutableMap.of();
-  public List<String> dontRewriteInvocations = ImmutableList.of();
-
   public void populateRetargetCoreLibMember(
       DexItemFactory factory, Map<DexString, Map<DexType, DexType>> dest) {
+    Map<String, String> retargetCoreLibMember = libraryConfiguration.getRetargetCoreLibMember();
     for (String inputString : retargetCoreLibMember.keySet()) {
       int index = inputString.lastIndexOf('#');
       if (index <= 0 || index >= inputString.length() - 1) {
@@ -606,6 +605,10 @@
   // If non-null, configuration must be passed to the consumer.
   public StringConsumer configurationConsumer = null;
 
+  // If null, no desugaring of library is performed.
+  // If non null it contains flags describing library desugaring.
+  public DesugaredLibraryConfiguration libraryConfiguration = DesugaredLibraryConfiguration.empty();
+
   // If null, no keep rules are recorded.
   // If non null it records desugared library APIs used by the program.
   public StringConsumer desugaredLibraryKeepRuleConsumer = null;
@@ -1011,6 +1014,7 @@
         new ProguardIfRuleEvaluationData();
 
     public static class ProguardIfRuleEvaluationData {
+
       public int numberOfProguardIfRuleClassEvaluations = 0;
       public int numberOfProguardIfRuleMemberEvaluations = 0;
     }
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 870d7c8..ec83a9b 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -531,10 +531,10 @@
   public void warnForSpecialLibraryConfiguration() throws Throwable {
     Path emptyZip = temp.getRoot().toPath().resolve("empty.zip");
     DiagnosticsChecker.checkWarningsContains(
-        "Special library configuration is still work in progress",
+        "Desugared library configuration is still work in progress",
         handler ->
             D8Command.builder(handler)
-                .addSpecialLibraryConfiguration("default")
+                .addDesugaredLibraryConfiguration("default")
                 .setOutput(emptyZip, OutputMode.DexIndexed)
                 .build());
   }
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index cb1a8c4..39b9024 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -71,7 +71,7 @@
     if (minAPILevel.getLevel() < AndroidApiLevel.O.getLevel()) {
       // Use P to mimic current Android Studio.
       builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P));
-      builder.addSpecialLibraryConfiguration("default");
+      builder.addDesugaredLibraryConfiguration("default");
       builder.setDesugaredLibraryKeepRuleConsumer(keepRuleConsumer);
     }
     return self();
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index ca080de..b40d689 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -30,7 +30,7 @@
     verifyEmptyCommand(
         L8Command.builder()
             .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
-            .addSpecialLibraryConfiguration("default")
+            .addDesugaredLibraryConfiguration("default")
             .build());
   }
 
@@ -50,7 +50,7 @@
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
             .addProgramFiles(ToolHelper.getDesugarJDKLibs())
             .setMinApiLevel(20)
-            .addSpecialLibraryConfiguration("default")
+            .addDesugaredLibraryConfiguration("default")
             .setOutput(output, OutputMode.DexIndexed)
             .build());
     Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
@@ -97,32 +97,21 @@
   }
 
   @Test(expected = CompilationFailedException.class)
-  public void specialLibraryConfgurationRequired() throws Throwable {
+  public void specialLibraryConfigurationRequired() throws Throwable {
     DiagnosticsChecker.checkErrorsContains(
-        "L8 requires a special library configuration",
+        "L8 requires a desugared library configuration",
         (handler) ->
             prepareBuilder(handler).setProgramConsumer(ClassFileConsumer.emptyConsumer()).build());
   }
 
-  @Test(expected = CompilationFailedException.class)
-  public void specialLibraryConfigurationMustBeDefault() throws Throwable {
-    DiagnosticsChecker.checkErrorsContains(
-        "L8 currently requires the special library configuration to be \"default\"",
-        (handler) ->
-            prepareBuilder(handler)
-                .addSpecialLibraryConfiguration("not default")
-                .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
-                .build());
-  }
-
   @Test
   public void warnForSpecialLibraryConfiguration() throws Throwable {
     DiagnosticsChecker.checkWarningsContains(
-        "Special library configuration is still work in progress",
+        "Desugared library configuration is still work in progress",
         handler ->
             prepareBuilder(handler)
                 .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
-                .addSpecialLibraryConfiguration("default")
+                .addDesugaredLibraryConfiguration("default")
                 .build());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 68aea3d..57d278e 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -17,7 +17,6 @@
 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.Box;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
@@ -89,8 +88,7 @@
 
   @Test
   public void desugaredLibraryKeepRuleConsumer() throws Exception {
-    Box<String> holder = new Box<>("");
-    StringConsumer stringConsumer = (string, handler) -> holder.set(holder.get() + string);
+    StringConsumer stringConsumer = StringConsumer.emptyConsumer();
     D8Command command =
         D8Command.builder()
             .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
@@ -639,18 +637,6 @@
         "Missing parameter", handler -> parse(handler, "--output"));
   }
 
-  @Test(expected = CompilationFailedException.class)
-  public void specialLibraryConfgurationMustBeDefault() throws Throwable {
-    DiagnosticsChecker.checkErrorsContains(
-        "R8 currently requires the special library configuration to be \"default\"",
-        handler ->
-            R8.run(
-                R8Command.builder(handler)
-                    .addSpecialLibraryConfiguration("not default")
-                    .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
-                    .build()));
-  }
-
   private R8Command parse(String... args) throws CompilationFailedException {
     return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
   }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index d141a70..fbbf170 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -332,7 +332,7 @@
     if (minAPILevel.getLevel() < AndroidApiLevel.O.getLevel()) {
       // Use P to mimic current Android Studio.
       builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P));
-      builder.addSpecialLibraryConfiguration("default");
+      builder.addDesugaredLibraryConfiguration("default");
       builder.setDesugaredLibraryKeepRuleConsumer(keepRuleConsumer);
     }
     return self();
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 5be195a..a5f8124 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
@@ -74,7 +74,7 @@
               .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
               .addProgramFiles(ToolHelper.getDesugarJDKLibs())
               .addProgramFiles(additionalProgramFiles)
-              .addSpecialLibraryConfiguration("default")
+              .addDesugaredLibraryConfiguration("default")
               .setMinApiLevel(apiLevel.getLevel())
               .setOutput(desugaredLib, OutputMode.DexIndexed);
       if (shrink) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/DesugaredLibraryContentTest.java
index 8304f36..9b210df 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/DesugaredLibraryContentTest.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import org.junit.Test;
@@ -40,12 +41,14 @@
 
   @Test
   public void testDesugaredLibraryContent() throws Exception {
+    Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
     CodeInspector inspector = new CodeInspector(buildDesugaredLibrary(parameters.getApiLevel()));
     assertCorrect(inspector);
   }
 
   @Test
   public void testDesugaredLibraryContentWithCoreLambdaStubsAsProgram() throws Exception {
+    Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
     ArrayList<Path> coreLambdaStubs = new ArrayList<>();
     coreLambdaStubs.add(ToolHelper.getCoreLambdaStubs());
     CodeInspector inspector =
@@ -56,6 +59,7 @@
 
   @Test
   public void testDesugaredLibraryContentWithCoreLambdaStubsAsLibrary() throws Exception {
+    Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
     TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
     Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
     L8Command.Builder l8Builder =
@@ -63,7 +67,7 @@
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
             .addProgramFiles(ToolHelper.getDesugarJDKLibs())
             .addLibraryFiles(ToolHelper.getCoreLambdaStubs())
-            .addSpecialLibraryConfiguration("default")
+            .addDesugaredLibraryConfiguration("default")
             .setMinApiLevel(parameters.getApiLevel().getLevel())
             .setOutput(desugaredLib, OutputMode.DexIndexed);
     ToolHelper.runL8(l8Builder.build(), options -> {});
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java
index c6c7c00..ca2ff2b 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java
@@ -37,11 +37,11 @@
     messages.assertWarningsCount(1);
     assertThat(
         messages.getWarnings().get(0).getDiagnosticMessage(),
-        containsString("Special library configuration is still work in progress"));
+        containsString("Desugared library configuration is still work in progress"));
     messages.assertErrorsCount(1);
     assertThat(
         messages.getErrors().get(0).getDiagnosticMessage(),
-        containsString("Using special library configuration requires desugaring to be enabled"));
+        containsString("Using desugared library configuration requires desugaring to be enabled"));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilOptionalTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilOptionalTest.java
index 46f6418..7c13e5c 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilOptionalTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilOptionalTest.java
@@ -82,10 +82,10 @@
         .addInnerClasses(JavaUtilOptionalTest.class)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
         .setMinApi(parameters.getApiLevel())
-        .enableCoreLibraryDesugaring()
+        .enableCoreLibraryDesugaring(parameters.getApiLevel())
         .compile()
         .inspect(this::checkRewrittenInvokes)
-        .addRunClasspathFiles(buildDesugaredLibrary(parameters.getApiLevel()))
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(expectedOutput);
   }
@@ -95,11 +95,10 @@
     testForD8()
         .addProgramFiles(
             Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION))
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel())
         .compile()
-        .addRunClasspathFiles(buildDesugaredLibrary(parameters.getApiLevel()))
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
         .run(parameters.getRuntime(), "backport.OptionalBackportJava9Main")
         .assertSuccess();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/JsonCompatibilityTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/JsonCompatibilityTest.java
new file mode 100644
index 0000000..7051374
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/JsonCompatibilityTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2019, 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.desugar.corelib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DesugaredLibraryConfigurationForTesting;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import java.nio.file.Paths;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class JsonCompatibilityTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public JsonCompatibilityTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testCompatibilityProgram() {
+    InternalOptions options1 = new InternalOptions(new DexItemFactory(), new Reporter());
+    options1.minApiLevel = parameters.getApiLevel().getLevel();
+    options1.libraryConfiguration =
+        DesugaredLibraryConfigurationForTesting.configureLibraryDesugaringForProgramCompilation(
+            parameters.getApiLevel().getLevel());
+
+    DexItemFactory factory = new DexItemFactory();
+    Reporter reporter = new Reporter();
+    InternalOptions options2 = new InternalOptions(factory, reporter);
+    options2.minApiLevel = parameters.getApiLevel().getLevel();
+    options2.libraryConfiguration =
+        new DesugaredLibraryConfigurationParser(
+                factory, reporter, false, parameters.getApiLevel().getLevel())
+            .parse(
+                StringResource.fromFile(
+                    Paths.get(
+                        "src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json")));
+
+    assertConfigurationEquals(options1.libraryConfiguration, options2.libraryConfiguration);
+  }
+
+  @Test
+  public void testCompatibilityLibrary() {
+    InternalOptions options1 = new InternalOptions(new DexItemFactory(), new Reporter());
+    options1.minApiLevel = parameters.getApiLevel().getLevel();
+    options1.libraryConfiguration =
+        DesugaredLibraryConfigurationForTesting.configureLibraryDesugaringForLibraryCompilation(
+            parameters.getApiLevel().getLevel());
+
+    DexItemFactory factory = new DexItemFactory();
+    Reporter reporter = new Reporter();
+    InternalOptions options2 = new InternalOptions(factory, reporter);
+    options2.minApiLevel = parameters.getApiLevel().getLevel();
+    options2.libraryConfiguration =
+        new DesugaredLibraryConfigurationParser(
+                factory, reporter, true, parameters.getApiLevel().getLevel())
+            .parse(
+                StringResource.fromFile(
+                    Paths.get(
+                        "src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json")));
+
+    assertConfigurationEquals(options1.libraryConfiguration, options2.libraryConfiguration);
+  }
+
+  private void assertConfigurationEquals(
+      com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration libraryConfiguration1,
+      com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration libraryConfiguration2) {
+    assertDictEquals(
+        libraryConfiguration1.getRewritePrefix(), libraryConfiguration2.getRewritePrefix());
+    assertDictEquals(
+        libraryConfiguration1.getEmulateLibraryInterface(),
+        libraryConfiguration2.getEmulateLibraryInterface());
+    assertDictEquals(
+        libraryConfiguration1.getBackportCoreLibraryMember(),
+        libraryConfiguration2.getBackportCoreLibraryMember());
+    assertDictEquals(
+        libraryConfiguration1.getRetargetCoreLibMember(),
+        libraryConfiguration2.getRetargetCoreLibMember());
+    assertEquals(
+        libraryConfiguration1.getDontRewriteInvocation().size(),
+        libraryConfiguration1.getDontRewriteInvocation().size());
+  }
+
+  private void assertDictEquals(Map<String, String> map1, Map<String, String> map2) {
+    assertEquals(map1.size(), map2.size());
+    for (String key : map1.keySet()) {
+      assertTrue(map2.containsKey(key) && map1.get(key).equals(map2.get(key)));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/LongestPrefixlTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/LongestPrefixlTest.java
index f66628d..15e9f41 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/LongestPrefixlTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/LongestPrefixlTest.java
@@ -8,8 +8,11 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -40,7 +43,16 @@
 
       testForD8()
           .addProgramFiles(inputJar)
-          .addOptionsModification(options -> options.rewritePrefix = x)
+          .addOptionsModification(
+              options ->
+                  options.libraryConfiguration =
+                      new DesugaredLibraryConfiguration(
+                          false,
+                          x,
+                          ImmutableMap.of(),
+                          ImmutableMap.of(),
+                          ImmutableMap.of(),
+                          ImmutableList.of()))
           .compile()
           .inspect(
               inspector -> {
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 bdce532..d9255fe 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
@@ -56,7 +56,7 @@
         L8Command.builder()
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
             .addProgramFiles(ToolHelper.getDesugarJDKLibs())
-            .addSpecialLibraryConfiguration("default")
+            .addDesugaredLibraryConfiguration("default")
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
             .setOutput(outputDex, OutputMode.DexIndexed)
             .build());
@@ -71,7 +71,7 @@
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
             .addProgramFiles(JDK_11_JAVA_BASE_EXTENSION_COMPILED_FILES)
             .addClasspathFiles(ToolHelper.getDesugarJDKLibs())
-            .addSpecialLibraryConfiguration("default")
+            .addDesugaredLibraryConfiguration("default")
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
             .setOutput(outputCf, OutputMode.DexIndexed)
             .build());
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/NeverMergeCoreLibDesugarClasses.java b/src/test/java/com/android/tools/r8/desugar/corelib/NeverMergeCoreLibDesugarClasses.java
index 421a101..352fccf 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/NeverMergeCoreLibDesugarClasses.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/NeverMergeCoreLibDesugarClasses.java
@@ -65,6 +65,7 @@
 
   @Test
   public void testDesugaredCoreLibrary() throws Exception {
+    Assume.assumeTrue(parameters.getApiLevel().getLevel() < 26);
     try {
       testForD8()
           .addInnerClasses(NeverMergeCoreLibDesugarClasses.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
index cb63a44..af89cd4 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
@@ -9,13 +9,6 @@
         "java.time.": "j$.time.",
         "java.util.Desugar": "j$.util.Desugar"
       },
-      "retarget_lib_member": {
-        "java.util.Calendar#toInstant": "java.util.DesugarCalendar",
-        "java.util.Date#from": "java.util.DesugarDate",
-        "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
-      },
       "backport": {
         "java.lang.Double8": "java.lang.Double",
         "java.lang.Integer8": "java.lang.Integer",
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index 5935d18..8f90064 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.memberrebinding.b135627418.library.Drawable;
 import com.android.tools.r8.memberrebinding.b135627418.library.DrawableWrapper;
 import com.android.tools.r8.memberrebinding.b135627418.library.InsetDrawable;
@@ -19,6 +20,7 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -75,8 +77,14 @@
             .setMinApi(parameters.getRuntime())
             .addOptionsModification(
                 options ->
-                    options.rewritePrefix =
-                        ImmutableMap.of(packageName + ".runtime", packageName + ".library"))
+                    options.libraryConfiguration =
+                        new DesugaredLibraryConfiguration(
+                            false,
+                            ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
+                            ImmutableMap.of(),
+                            ImmutableMap.of(),
+                            ImmutableMap.of(),
+                            ImmutableList.of()))
             .compile();
 
     testForR8(parameters.getBackend())