Add compiler API for retrieving map id

This introduces a new interface com.android.tools.r8.MapConsumer that can be used to retrieve the map id. The MapConsumer interface has a acceptMapId(String) method that will be called by R8 when the map id is known.

Clients can pass an implementation of MapConsumer to R8 using the R8Command.Builder#setProguardMapConsumer(StringConsumer) method.

This change is backwards compatible since clients can continue to pass a com.android.tools.r8.StringConsumer to setProguardMapConsumer.

Bug: b/146403477
Change-Id: I1e56ef98cb57cafc25390c79eaa033dd685000cc
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index ca01413..f5b61ea 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MapConsumerUtils;
 import com.android.tools.r8.utils.PartitionMapZipContainer;
 import com.android.tools.r8.utils.ProgramConsumerUtils;
 import com.android.tools.r8.utils.Reporter;
@@ -292,7 +293,7 @@
     private BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
     private final List<AssertionsConfiguration> assertionsConfiguration = new ArrayList<>();
     private final List<Consumer<Inspector>> outputInspections = new ArrayList<>();
-    protected StringConsumer proguardMapConsumer = null;
+    protected MapConsumer proguardMapConsumer = null;
     protected PartitionMapConsumer partitionMapConsumer = null;
     private DumpInputFlags dumpInputFlags = DumpInputFlags.getDefault();
     private MapIdProvider mapIdProvider = null;
@@ -372,7 +373,7 @@
     /**
      * Set an output destination to which proguard-map content should be written.
      *
-     * <p>This is a short-hand for setting a {@link StringConsumer.FileConsumer} using {@link
+     * <p>This is a short-hand for setting a {@link MapConsumer.FileConsumer} using {@link
      * #setProguardMapConsumer}. Note that any subsequent call to this method or {@link
      * #setProguardMapConsumer} will override the previous setting.
      *
@@ -380,19 +381,22 @@
      */
     B setProguardMapOutputPath(Path proguardMapOutput) {
       assert proguardMapOutput != null;
-      return setProguardMapConsumer(new StringConsumer.FileConsumer(proguardMapOutput));
+      return setProguardMapConsumer(new MapConsumer.FileConsumer(proguardMapOutput));
     }
 
     /**
      * Set a consumer for receiving the proguard-map content.
      *
+     * <p>It is possible to also retrieve the map id by passing an instance of {@link
+     * com.android.tools.r8.MapConsumer}.
+     *
      * <p>Note that any subsequent call to this method or {@link #setProguardMapOutputPath} will
      * override the previous setting.
      *
      * @param proguardMapConsumer Consumer to receive the content once produced.
      */
     B setProguardMapConsumer(StringConsumer proguardMapConsumer) {
-      this.proguardMapConsumer = proguardMapConsumer;
+      this.proguardMapConsumer = MapConsumerUtils.createFromStringConsumer(proguardMapConsumer);
       return self();
     }
 
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 74c5484..41db107 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
-import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingMapConsumerIfNotNull;
+import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingInternalMapConsumerIfNotNull;
 
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.dump.DumpOptions;
@@ -15,8 +15,8 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.keepanno.annotations.KeepForApi;
 import com.android.tools.r8.metadata.D8BuildMetadata;
-import com.android.tools.r8.naming.MapConsumer;
-import com.android.tools.r8.naming.ProguardMapStringConsumer;
+import com.android.tools.r8.naming.InternalMapConsumer;
+import com.android.tools.r8.naming.InternalMapConsumerImpl;
 import com.android.tools.r8.origin.ArchiveEntryOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
@@ -614,7 +614,7 @@
   private final boolean enableMainDexListCheck;
   private final boolean minimalMainDex;
   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
-  private final StringConsumer proguardMapConsumer;
+  private final MapConsumer proguardMapConsumer;
   private final PartitionMapConsumer partitionMapConsumer;
   private final boolean enableMissingLibraryApiModeling;
   private final boolean enableRewritingOfArtProfilesIsNopCheck;
@@ -691,7 +691,7 @@
       int threadCount,
       DumpInputFlags dumpInputFlags,
       MapIdProvider mapIdProvider,
-      StringConsumer proguardMapConsumer,
+      MapConsumer proguardMapConsumer,
       PartitionMapConsumer partitionMapConsumer,
       boolean enableMissingLibraryApiModeling,
       boolean enableRewritingOfArtProfilesIsNopCheck,
@@ -787,15 +787,15 @@
     internal.setSyntheticInfoConsumer(syntheticInfoConsumer);
     internal.desugarGraphConsumer = desugarGraphConsumer;
     internal.mainDexKeepRules = mainDexKeepRules;
-    MapConsumer mapConsumer =
-        wrapExistingMapConsumerIfNotNull(
+    InternalMapConsumer mapConsumer =
+        wrapExistingInternalMapConsumerIfNotNull(
             internal.mapConsumer, partitionMapConsumer, MapConsumerToPartitionMapConsumer::new);
     internal.mapConsumer =
-        wrapExistingMapConsumerIfNotNull(
+        wrapExistingInternalMapConsumerIfNotNull(
             mapConsumer,
             proguardMapConsumer,
             nonNullStringConsumer ->
-                ProguardMapStringConsumer.builder().setStringConsumer(proguardMapConsumer).build());
+                InternalMapConsumerImpl.builder().setMapConsumer(proguardMapConsumer).build());
     internal.lineNumberOptimization =
         !internal.debug && proguardMapConsumer != null
             ? LineNumberOptimization.ON
diff --git a/src/main/java/com/android/tools/r8/MapConsumer.java b/src/main/java/com/android/tools/r8/MapConsumer.java
new file mode 100644
index 0000000..7ebadaa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/MapConsumer.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2025, 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;
+
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+import java.nio.file.Path;
+
+/** Interface for retrieving the generated mapping and its id. */
+@KeepForApi
+public interface MapConsumer extends StringConsumer {
+
+  /**
+   * Called by R8 when the mapping id has been generated. This is always called prior to {@link
+   * #finished(DiagnosticsHandler)}.
+   */
+  default void acceptMapId(String mapId) {}
+
+  @KeepForApi
+  class FileConsumer extends StringConsumer.FileConsumer implements MapConsumer {
+
+    private final MapConsumer consumer;
+
+    public FileConsumer(Path outputPath) {
+      this(outputPath, null);
+    }
+
+    public FileConsumer(Path outputPath, MapConsumer consumer) {
+      super(outputPath, consumer);
+      this.consumer = consumer;
+    }
+
+    @Override
+    public void acceptMapId(String mapId) {
+      if (consumer != null) {
+        consumer.acceptMapId(mapId);
+      }
+    }
+  }
+
+  @KeepForApi
+  class ForwardingConsumer extends StringConsumer.ForwardingConsumer implements MapConsumer {
+
+    private final MapConsumer consumer;
+
+    /**
+     * @param consumer Consumer to forward to, if null, nothing will be forwarded.
+     */
+    public ForwardingConsumer(MapConsumer consumer) {
+      super(consumer);
+      this.consumer = consumer;
+    }
+
+    @Override
+    public void acceptMapId(String mapId) {
+      if (consumer != null) {
+        consumer.acceptMapId(mapId);
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java b/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
index 56a5446..3f8214d 100644
--- a/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
+++ b/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
@@ -6,12 +6,12 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.MapConsumer;
+import com.android.tools.r8.naming.InternalMapConsumer;
 import com.android.tools.r8.retrace.ProguardMapPartitioner;
 import com.android.tools.r8.retrace.internal.ProguardMapProducerInternal;
 import java.io.IOException;
 
-public class MapConsumerToPartitionMapConsumer implements MapConsumer {
+public class MapConsumerToPartitionMapConsumer implements InternalMapConsumer {
 
   protected final PartitionMapConsumer partitionMapConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index cace694..6a49f4d 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
-import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingMapConsumerIfNotNull;
+import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingInternalMapConsumerIfNotNull;
 
 import com.android.build.shrinker.r8integration.LegacyResourceShrinker;
 import com.android.tools.r8.ProgramResource.Kind;
@@ -28,8 +28,8 @@
 import com.android.tools.r8.metadata.R8BuildMetadata;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.ClassNamingForNameMapper;
-import com.android.tools.r8.naming.MapConsumer;
-import com.android.tools.r8.naming.ProguardMapStringConsumer;
+import com.android.tools.r8.naming.InternalMapConsumer;
+import com.android.tools.r8.naming.InternalMapConsumerImpl;
 import com.android.tools.r8.naming.SourceFileRewriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
@@ -61,6 +61,7 @@
 import com.android.tools.r8.utils.InternalOptions.MappingComposeOptions;
 import com.android.tools.r8.utils.InternalProgramClassProvider;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MapConsumerUtils;
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.SemanticVersion;
@@ -346,6 +347,9 @@
     /**
      * Set a consumer for receiving the proguard-map content.
      *
+     * <p>It is possible to also retrieve the map id by passing an instance of {@link
+     * com.android.tools.r8.MapConsumer}.
+     *
      * <p>Note that any subsequent call to this method or {@link #setProguardMapOutputPath} will
      * override the previous setting.
      *
@@ -1155,7 +1159,7 @@
   private final boolean forceProguardCompatibility;
   private final boolean protectApiSurface;
   private final Optional<Boolean> includeDataResources;
-  private final StringConsumer proguardMapConsumer;
+  private final MapConsumer proguardMapConsumer;
   private final PartitionMapConsumer partitionMapConsumer;
   private final StringConsumer proguardUsageConsumer;
   private final StringConsumer proguardSeedsConsumer;
@@ -1242,7 +1246,7 @@
       boolean forceProguardCompatibility,
       boolean protectApiSurface,
       Optional<Boolean> includeDataResources,
-      StringConsumer proguardMapConsumer,
+      MapConsumer proguardMapConsumer,
       PartitionMapConsumer partitionMapConsumer,
       StringConsumer proguardUsageConsumer,
       StringConsumer proguardSeedsConsumer,
@@ -1423,24 +1427,24 @@
     }
 
     // Amend the proguard-map consumer with options from the proguard configuration.
-    StringConsumer stringConsumer =
-        wrapStringConsumer(
+    MapConsumer mapConsumer =
+        wrapMapConsumer(
             proguardMapConsumer,
             proguardConfiguration.isPrintMapping(),
             proguardConfiguration.getPrintMappingFile());
-    MapConsumer mapConsumer =
-        wrapExistingMapConsumerIfNotNull(
+    InternalMapConsumer internalMapConsumer =
+        wrapExistingInternalMapConsumerIfNotNull(
             internal.mapConsumer, partitionMapConsumer, MapConsumerToPartitionMapConsumer::new);
-    mapConsumer =
-        wrapExistingMapConsumerIfNotNull(
+    internalMapConsumer =
+        wrapExistingInternalMapConsumerIfNotNull(
+            internalMapConsumer,
             mapConsumer,
-            stringConsumer,
             nonNullStringConsumer ->
-                ProguardMapStringConsumer.builder().setStringConsumer(stringConsumer).build());
+                InternalMapConsumerImpl.builder().setMapConsumer(mapConsumer).build());
 
     internal.mapConsumer =
-        wrapExistingMapConsumerIfNotNull(
-            mapConsumer,
+        wrapExistingInternalMapConsumerIfNotNull(
+            internalMapConsumer,
             androidResourceConsumer,
             nonNulStringConsumer -> new ResourceShrinkerMapStringConsumer(internal));
     // Amend the usage information consumer with options from the proguard configuration.
@@ -1563,6 +1567,18 @@
     return internal;
   }
 
+  private static MapConsumer wrapMapConsumer(
+      MapConsumer consumer, boolean optionsFlag, Path optionFile) {
+    if (optionsFlag) {
+      if (optionFile != null) {
+        return new MapConsumer.FileConsumer(optionFile, consumer);
+      } else {
+        return MapConsumerUtils.createStandardOutConsumer(consumer);
+      }
+    }
+    return consumer;
+  }
+
   private static StringConsumer wrapStringConsumer(
       StringConsumer optionConsumer, boolean optionsFlag, Path optionFile) {
     if (optionsFlag) {
@@ -1575,7 +1591,7 @@
     return optionConsumer;
   }
 
-  private static class ResourceShrinkerMapStringConsumer implements MapConsumer {
+  private static class ResourceShrinkerMapStringConsumer implements InternalMapConsumer {
 
     private final InternalOptions internal;
     private final Predicate<String> classNamePredicate;
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 226ba81..396e0ed 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.naming.MapConsumer;
+import com.android.tools.r8.naming.InternalMapConsumer;
 import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
@@ -183,7 +183,7 @@
     InternalOptions options = app.options;
     // Save the original consumers, so they can be unwrapped after write.
     ProgramConsumer programConsumer = options.programConsumer;
-    MapConsumer mapConsumer = options.mapConsumer;
+    InternalMapConsumer mapConsumer = options.mapConsumer;
     AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
     ApplicationWriter writer =
         ApplicationWriter.create(
diff --git a/src/main/java/com/android/tools/r8/naming/MapConsumer.java b/src/main/java/com/android/tools/r8/naming/InternalMapConsumer.java
similarity index 71%
rename from src/main/java/com/android/tools/r8/naming/MapConsumer.java
rename to src/main/java/com/android/tools/r8/naming/InternalMapConsumer.java
index 01c497f..97cc5b5 100644
--- a/src/main/java/com/android/tools/r8/naming/MapConsumer.java
+++ b/src/main/java/com/android/tools/r8/naming/InternalMapConsumer.java
@@ -11,9 +11,9 @@
  * This is an internal consumer that can accept our internal representation of a mapping format.
  * This should not be exposed.
  */
-public interface MapConsumer extends Finishable {
+public interface InternalMapConsumer extends Finishable {
 
-  void accept(
-      DiagnosticsHandler diagnosticsHandler,
-      ClassNameMapper classNameMapper);
+  void accept(DiagnosticsHandler diagnosticsHandler, ClassNameMapper classNameMapper);
+
+  default void acceptMapId(String mapId) {}
 }
diff --git a/src/main/java/com/android/tools/r8/naming/InternalMapConsumerImpl.java b/src/main/java/com/android/tools/r8/naming/InternalMapConsumerImpl.java
new file mode 100644
index 0000000..83b5edb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/InternalMapConsumerImpl.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2023, 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.naming;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.MapConsumer;
+import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.StringUtils;
+
+/***
+ * Default implementation of an {@link InternalMapConsumer} that wraps around a {@link
+ * com.android.tools.r8.MapConsumer} for streamed string output.
+ */
+public class InternalMapConsumerImpl implements InternalMapConsumer, ChainableStringConsumer {
+
+  private final MapConsumer mapConsumer;
+  private DiagnosticsHandler diagnosticsHandler;
+
+  private InternalMapConsumerImpl(MapConsumer mapConsumer) {
+    assert mapConsumer != null;
+    this.mapConsumer = mapConsumer;
+  }
+
+  @Override
+  public void accept(DiagnosticsHandler diagnosticsHandler, ClassNameMapper classNameMapper) {
+    this.diagnosticsHandler = diagnosticsHandler;
+    accept(StringUtils.unixLines(classNameMapper.getPreamble()));
+    classNameMapper.write(this);
+  }
+
+  @Override
+  public void acceptMapId(String mapId) {
+    mapConsumer.acceptMapId(mapId);
+  }
+
+  @Override
+  public ChainableStringConsumer accept(String string) {
+    assert diagnosticsHandler != null;
+    mapConsumer.accept(string, diagnosticsHandler);
+    return this;
+  }
+
+  public MapConsumer getMapConsumer() {
+    return mapConsumer;
+  }
+
+  @Override
+  public void finished(DiagnosticsHandler handler) {
+    mapConsumer.finished(handler);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private MapConsumer mapConsumer;
+
+    public Builder setMapConsumer(MapConsumer mapConsumer) {
+      this.mapConsumer = mapConsumer;
+      return this;
+    }
+
+    public InternalMapConsumerImpl build() {
+      return new InternalMapConsumerImpl(mapConsumer);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java b/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
deleted file mode 100644
index 6f2091c..0000000
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2023, 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.naming;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.utils.ChainableStringConsumer;
-import com.android.tools.r8.utils.StringUtils;
-
-/***
- * Default implementation of a MapConsumer that wraps around a string consumer for streamed string
- * output.
- */
-public class ProguardMapStringConsumer implements MapConsumer, ChainableStringConsumer {
-
-  private final StringConsumer stringConsumer;
-  private DiagnosticsHandler diagnosticsHandler;
-
-  private ProguardMapStringConsumer(StringConsumer stringConsumer) {
-    assert stringConsumer != null;
-    this.stringConsumer = stringConsumer;
-  }
-
-  @Override
-  public void accept(
-      DiagnosticsHandler diagnosticsHandler,
-      ClassNameMapper classNameMapper) {
-    this.diagnosticsHandler = diagnosticsHandler;
-    accept(StringUtils.unixLines(classNameMapper.getPreamble()));
-    classNameMapper.write(this);
-  }
-
-  @Override
-  public ChainableStringConsumer accept(String string) {
-    assert diagnosticsHandler != null;
-    stringConsumer.accept(string, diagnosticsHandler);
-    return this;
-  }
-
-  public StringConsumer getStringConsumer() {
-    return stringConsumer;
-  }
-
-  @Override
-  public void finished(DiagnosticsHandler handler) {
-    stringConsumer.finished(handler);
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static class Builder {
-
-    private StringConsumer stringConsumer;
-
-    public Builder setStringConsumer(StringConsumer stringConsumer) {
-      this.stringConsumer = stringConsumer;
-      return this;
-    }
-
-    public ProguardMapStringConsumer build() {
-      return new ProguardMapStringConsumer(stringConsumer);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 3c7fad9..f8da2d6 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -83,7 +83,7 @@
 
   private final ClassNameMapper classNameMapper;
   private final InternalOptions options;
-  private final MapConsumer consumer;
+  private final InternalMapConsumer consumer;
   private final Reporter reporter;
   private final Tool compiler;
 
@@ -110,7 +110,7 @@
     try (Timing t0 = timing.begin("Prepare write")) {
       classNameMapper.prepareWrite(appView, executorService);
     }
-    AwaitableFutureValue<ProguardMapId> proguardMapId =
+    AwaitableFutureValue<ProguardMapId> proguardMapIdSupplier =
         computeProguardMapId(appView, executorService, timing);
     AwaitableFuture mapWritten;
     mapWritten =
@@ -118,10 +118,11 @@
             () -> {
               try (Timing threadTiming =
                   timing.createThreadTiming("Write proguard map to consumer", appView.options())) {
+                ProguardMapId proguardMapId = proguardMapIdSupplier.awaitValue();
                 ProguardMapMarkerInfo markerInfo =
                     ProguardMapMarkerInfo.builder()
                         .setCompilerName(compiler.name())
-                        .setProguardMapId(proguardMapId.awaitValue())
+                        .setProguardMapId(proguardMapId)
                         .setGeneratingDex(options.isGeneratingDex())
                         .setApiLevel(options.getMinApiLevel())
                         .setMapVersion(options.getMapFileVersion())
@@ -132,13 +133,14 @@
                     ListUtils.concat(markerInfo.toPreamble(), classNameMapper.getPreamble()));
 
                 consumer.accept(reporter, classNameMapper);
+                consumer.acceptMapId(proguardMapId.id);
                 ExceptionUtils.withConsumeResourceHandler(reporter, this.consumer::finished);
               }
               return null;
             },
             appView.options().getThreadingModule(),
             executorService);
-    return new ProguardMapSupplierResult(proguardMapId, mapWritten);
+    return new ProguardMapSupplierResult(proguardMapIdSupplier, mapWritten);
   }
 
   private AwaitableFutureValue<ProguardMapId> computeProguardMapId(
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index 8142f7b..8a49577 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingMapConsumer;
+import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingInternalMapConsumer;
 
 import com.android.tools.r8.BaseCompilerCommand;
 import com.android.tools.r8.ByteDataView;
@@ -15,11 +15,11 @@
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.DexIndexedConsumer.ForwardingConsumer;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.MapConsumer;
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.naming.MapConsumer;
-import com.android.tools.r8.naming.ProguardMapStringConsumer;
+import com.android.tools.r8.naming.InternalMapConsumer;
+import com.android.tools.r8.naming.InternalMapConsumerImpl;
 import com.android.tools.r8.origin.Origin;
 import com.google.common.io.ByteStreams;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -37,7 +37,7 @@
   private boolean closed = false;
 
   private ProgramConsumer programConsumer = null;
-  private MapConsumer mapConsumer = null;
+  private InternalMapConsumer mapConsumer = null;
 
   public AndroidAppConsumers() {
     // Nothing to do.
@@ -69,15 +69,15 @@
     return programConsumer;
   }
 
-  public MapConsumer wrapProguardMapConsumer(MapConsumer consumer) {
+  public InternalMapConsumer wrapProguardMapConsumer(InternalMapConsumer consumer) {
     assert mapConsumer == null;
     if (consumer != null) {
       mapConsumer =
-          wrapExistingMapConsumer(
+          wrapExistingInternalMapConsumer(
               consumer,
-              ProguardMapStringConsumer.builder()
-                  .setStringConsumer(
-                      new StringConsumer() {
+              InternalMapConsumerImpl.builder()
+                  .setMapConsumer(
+                      new MapConsumer() {
                         StringBuilder stringBuilder = null;
 
                         @Override
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 6614fb8..227b00b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -77,7 +77,7 @@
 import com.android.tools.r8.metadata.D8BuildMetadata;
 import com.android.tools.r8.metadata.R8BuildMetadata;
 import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.MapConsumer;
+import com.android.tools.r8.naming.InternalMapConsumer;
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.optimize.accessmodification.AccessModifierOptions;
@@ -1319,7 +1319,7 @@
 
   // If null, no proguard map needs to be computed.
   // If non null it must be and passed to the consumer.
-  public MapConsumer mapConsumer = null;
+  public InternalMapConsumer mapConsumer = null;
 
   public boolean hasMappingFileSupport() {
     return mapConsumer != null;
diff --git a/src/main/java/com/android/tools/r8/utils/MapConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/MapConsumerUtils.java
index c6056ea..d99f96c 100644
--- a/src/main/java/com/android/tools/r8/utils/MapConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapConsumerUtils.java
@@ -5,18 +5,52 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.MapConsumer;
+import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.MapConsumer;
+import com.android.tools.r8.naming.InternalMapConsumer;
 import java.util.function.Function;
 
 public class MapConsumerUtils {
 
-  public static MapConsumer wrapExistingMapConsumer(
-      MapConsumer existingMapConsumer, MapConsumer newConsumer) {
+  public static MapConsumer createFromStringConsumer(StringConsumer consumer) {
+    if (consumer == null) {
+      return null;
+    }
+    if (consumer instanceof MapConsumer) {
+      return (MapConsumer) consumer;
+    }
+    return new MapConsumer() {
+
+      @Override
+      public void accept(String string, DiagnosticsHandler handler) {
+        consumer.accept(string, handler);
+      }
+
+      @Override
+      public void finished(DiagnosticsHandler handler) {
+        consumer.finished(handler);
+      }
+    };
+  }
+
+  public static MapConsumer createStandardOutConsumer(MapConsumer consumer) {
+    return new MapConsumer.ForwardingConsumer(consumer) {
+
+      @Override
+      public void accept(String string, DiagnosticsHandler handler) {
+        super.accept(string, handler);
+        System.out.print(string);
+      }
+    };
+  }
+
+  public static InternalMapConsumer wrapExistingInternalMapConsumer(
+      InternalMapConsumer existingMapConsumer, InternalMapConsumer newConsumer) {
     if (existingMapConsumer == null) {
       return newConsumer;
     }
-    return new MapConsumer() {
+    return new InternalMapConsumer() {
       @Override
       public void accept(DiagnosticsHandler diagnosticsHandler, ClassNameMapper classNameMapper) {
         existingMapConsumer.accept(diagnosticsHandler, classNameMapper);
@@ -31,11 +65,13 @@
     };
   }
 
-  public static <T> MapConsumer wrapExistingMapConsumerIfNotNull(
-      MapConsumer existingMapConsumer, T object, Function<T, MapConsumer> producer) {
+  public static <T> InternalMapConsumer wrapExistingInternalMapConsumerIfNotNull(
+      InternalMapConsumer existingMapConsumer,
+      T object,
+      Function<T, InternalMapConsumer> producer) {
     if (object == null) {
       return existingMapConsumer;
     }
-    return wrapExistingMapConsumer(existingMapConsumer, producer.apply(object));
+    return wrapExistingInternalMapConsumer(existingMapConsumer, producer.apply(object));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 8a9c360..5272775 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -18,7 +18,7 @@
 import com.android.tools.r8.StringConsumer.FileConsumer;
 import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
 import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.naming.ProguardMapStringConsumer;
+import com.android.tools.r8.naming.InternalMapConsumerImpl;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.references.Reference;
@@ -192,10 +192,10 @@
     assertNotNull(parsedCommand.getR8Command());
     InternalOptions internalOptions = parsedCommand.getR8Command().getInternalOptions();
     assertNotNull(internalOptions);
-    assertTrue(internalOptions.mapConsumer instanceof ProguardMapStringConsumer);
-    ProguardMapStringConsumer mapStringConsumer =
-        (ProguardMapStringConsumer) internalOptions.mapConsumer;
-    FileConsumer proguardMapConsumer = (FileConsumer) mapStringConsumer.getStringConsumer();
+    assertTrue(internalOptions.mapConsumer instanceof InternalMapConsumerImpl);
+    InternalMapConsumerImpl mapStringConsumer =
+        (InternalMapConsumerImpl) internalOptions.mapConsumer;
+    FileConsumer proguardMapConsumer = (FileConsumer) mapStringConsumer.getMapConsumer();
     assertEquals(pgMap, proguardMapConsumer.getOutputPath());
   }
 
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mapid/CustomMapIdTest.java b/src/test/java/com/android/tools/r8/compilerapi/mapid/CustomMapIdTest.java
index d1d4330..df63857 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/mapid/CustomMapIdTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/mapid/CustomMapIdTest.java
@@ -10,6 +10,8 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.MapConsumer;
 import com.android.tools.r8.MapIdEnvironment;
 import com.android.tools.r8.MarkerMatcher;
 import com.android.tools.r8.ProgramConsumer;
@@ -22,10 +24,13 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.ThrowingBiConsumer;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.List;
 import java.util.function.Function;
 import org.junit.Test;
 
@@ -47,6 +52,12 @@
   }
 
   @Test
+  public void testDefaultMapIdWithPrintConfiguration() throws Exception {
+    ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+    runTest(test::runDefaultMapIdWithPrintConfiguration, hash -> hash);
+  }
+
+  @Test
   public void testCustomMapId() throws Exception {
     ApiTest test = new ApiTest(ApiTest.PARAMETERS);
     runTest(test::runCustomMapId, hash -> hash);
@@ -68,13 +79,34 @@
     Path output = temp.newFolder().toPath().resolve("out.jar");
     StringBuilder mappingBuilder = new StringBuilder();
     BooleanBox didGetMappingContent = new BooleanBox(false);
+    BooleanBox finished = new BooleanBox();
+    Box<String> consumedMapId = new Box<>();
     test.accept(
         new DexIndexedConsumer.ArchiveConsumer(output),
-        (mappingContent, handler) -> {
-          mappingBuilder.append(mappingContent);
-          didGetMappingContent.set(true);
+        new MapConsumer() {
+          @Override
+          public void accept(String string, DiagnosticsHandler handler) {
+            assertTrue(finished.isFalse());
+            mappingBuilder.append(string);
+            didGetMappingContent.set(true);
+          }
+
+          @Override
+          public void acceptMapId(String mapId) {
+            assertTrue(finished.isFalse());
+            consumedMapId.set(mapId);
+          }
+
+          @Override
+          public void finished(DiagnosticsHandler handler) {
+            assertTrue(finished.isFalse());
+            MapConsumer.super.finished(handler);
+            finished.set();
+          }
         });
-    assertTrue(didGetMappingContent.get());
+    assertTrue(didGetMappingContent.isTrue());
+    assertTrue(consumedMapId.isSet());
+    assertTrue(finished.isTrue());
 
     // Extract the map hash from the file. This is always set by R8 to a SHA 256 hash.
     String mappingContent = mappingBuilder.toString();
@@ -83,6 +115,7 @@
 
     // Check the map id is also defined in the map file.
     String mapId = hashToId.apply(mapHash);
+    assertEquals(mapId, consumedMapId.get());
     assertThat(mappingContent, containsString("pg_map_id: " + mapId + "\n"));
 
     // Check that the map id is also present in the markers.
@@ -100,10 +133,26 @@
 
     public void runDefaultMapId(ProgramConsumer programConsumer, StringConsumer mappingConsumer)
         throws Exception {
+      internalRunDefaultMapId(programConsumer, mappingConsumer, false);
+    }
+
+    public void runDefaultMapIdWithPrintConfiguration(
+        ProgramConsumer programConsumer, StringConsumer mappingConsumer) throws Exception {
+      internalRunDefaultMapId(programConsumer, mappingConsumer, true);
+    }
+
+    private void internalRunDefaultMapId(
+        ProgramConsumer programConsumer, StringConsumer mappingConsumer, boolean printConfiguration)
+        throws Exception {
+      List<String> keepRules = getKeepMainRules(getMockClass());
+      if (printConfiguration) {
+        keepRules =
+            ImmutableList.<String>builder().addAll(keepRules).add("-printconfiguration").build();
+      }
       R8.run(
           R8Command.builder()
               .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
-              .addProguardConfiguration(getKeepMainRules(getMockClass()), Origin.unknown())
+              .addProguardConfiguration(keepRules, Origin.unknown())
               .addLibraryFiles(getJava8RuntimeJar())
               .setProgramConsumer(programConsumer)
               .setProguardMapConsumer(mappingConsumer)
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
index 707acf0..709686c 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-481d303c480e2ac2a6dc71518a28846fc79fc76e
\ No newline at end of file
+c3f45ad6bf68e9f396bdf331b38f512582add212
\ No newline at end of file