Allow R8 markers without a library desugaring identifier when merging

An R8 marker without a library desugaring identifier could come from a
library built with R8 and class file output, so always allow that when
merging.

Right now it is not possible to distinguish this from a similar marker
in DEX. However, merging multiple R8 processed DEX files is an
exceptional case, so not catching that is acceptable with the currrent
marker content.

Bug: 158644894
Bug: 160282413
Change-Id: Ia5c6195694a4f96dfedcfa72c104e1c285afdb51
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index 0b1fead..c8d6295 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -49,6 +49,11 @@
     return extractMarker(appBuilder.build());
   }
 
+  public static Collection<Marker> extractMarkerFromJarFile(Path file)
+      throws IOException, ExecutionException {
+    return extractMarker(AndroidApp.builder().addProgramFile(file).build());
+  }
+
   public static int extractDexSize(Path file) throws IOException, ResourceException {
     AndroidApp.Builder appBuilder = AndroidApp.builder();
     addDexResources(appBuilder, file);
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 1484f02..e15a4eb 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -88,6 +88,9 @@
                 new StringDiagnostic(
                     "Merging program compiled with multiple desugared libraries."));
         }
+        if (identifier.equals(NO_LIBRARY_DESUGARING) && marker.tool == Tool.R8) {
+          continue;
+        }
         desugaredLibraryIdentifiers.add(identifier);
       }
     }
@@ -131,6 +134,10 @@
     return this;
   }
 
+  public boolean hasMinApi() {
+    return jsonObject.has(MIN_API);
+  }
+
   public Long getMinApi() {
     return jsonObject.get(MIN_API).getAsLong();
   }
@@ -141,6 +148,10 @@
     return this;
   }
 
+  public boolean hasDesugaredLibraryIdentifiers() {
+    return jsonObject.has(DESUGARED_LIBRARY_IDENTIFIERS);
+  }
+
   public String[] getDesugaredLibraryIdentifiers() {
     if (jsonObject.has(DESUGARED_LIBRARY_IDENTIFIERS)) {
       JsonArray array = jsonObject.get(DESUGARED_LIBRARY_IDENTIFIERS).getAsJsonArray();
diff --git a/src/test/java/com/android/tools/r8/MarkerMatcher.java b/src/test/java/com/android/tools/r8/MarkerMatcher.java
index 90aec54..13065b7 100644
--- a/src/test/java/com/android/tools/r8/MarkerMatcher.java
+++ b/src/test/java/com/android/tools/r8/MarkerMatcher.java
@@ -117,6 +117,20 @@
     };
   }
 
+  public static Matcher<Marker> markerHasMinApi() {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        return marker.hasMinApi();
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description.appendText(Marker.MIN_API + " found");
+      }
+    };
+  }
+
   public static Matcher<Marker> markerHasChecksums(boolean value) {
     return new MarkerMatcher() {
       @Override
@@ -165,6 +179,25 @@
     };
   }
 
+  public static Matcher<Marker> markerHasDesugaredLibraryIdentifier() {
+    return markerHasDesugaredLibraryIdentifier(true);
+  }
+
+  public static Matcher<Marker> markerHasDesugaredLibraryIdentifier(boolean value) {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        return marker.hasDesugaredLibraryIdentifiers() == value;
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description.appendText(
+            Marker.DESUGARED_LIBRARY_IDENTIFIERS + (value ? " found" : " not found"));
+      }
+    };
+  }
+
   @Override
   protected boolean matchesSafely(Marker marker) {
     return eval(marker);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
index 03052cc..f485a25 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
@@ -4,22 +4,35 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary;
 
+import static com.android.tools.r8.MarkerMatcher.assertMarkersMatch;
+import static com.android.tools.r8.MarkerMatcher.markerCompilationMode;
+import static com.android.tools.r8.MarkerMatcher.markerHasDesugaredLibraryIdentifier;
+import static com.android.tools.r8.MarkerMatcher.markerHasMinApi;
+import static com.android.tools.r8.MarkerMatcher.markerTool;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.ExtractMarker;
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11DesugaredLibraryTestBase;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import org.hamcrest.Matcher;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -69,6 +82,47 @@
         .assertSuccessWithOutputLines(JAVA_RESULT);
   }
 
+  @Test
+  public void testMergeDesugaredWithShrunkenLib() throws Exception {
+    // Compile a library with R8 to CF.
+    Path shrunkenLib =
+        testForR8(Backend.CF)
+            .addProgramClasses(Part2.class)
+            .addKeepClassRules(Part2.class)
+            .compile()
+            .writeToZip();
+
+    // R8 class file output marker has no library desugaring identifier.
+    Matcher<Marker> libraryMatcher =
+        allOf(
+            markerTool(Tool.R8),
+            markerCompilationMode(CompilationMode.RELEASE),
+            not(markerHasMinApi()),
+            not(markerHasDesugaredLibraryIdentifier()));
+    assertMarkersMatch(
+        ExtractMarker.extractMarkerFromJarFile(shrunkenLib), ImmutableList.of(libraryMatcher));
+
+    // Build an app with the R8 compiled library.
+    Path app =
+        testForD8()
+            .addProgramFiles(buildPart1DesugaredLibrary(), shrunkenLib)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel())
+            .compile()
+            .writeToZip();
+
+    // The app has both the R8 marker from the library compilation and the D8 marker from dexing.
+    Matcher<Marker> d8Matcher =
+        allOf(
+            markerTool(Tool.D8),
+            markerHasMinApi(),
+            markerHasDesugaredLibraryIdentifier(
+                parameters.getApiLevel().isLessThan(AndroidApiLevel.O)));
+    assertMarkersMatch(
+        ExtractMarker.extractMarkerFromDexFile(app), ImmutableList.of(libraryMatcher, d8Matcher));
+  }
+
   private void assertError(TestDiagnosticMessages m) {
     List<Diagnostic> errors = m.getErrors();
     if (expectError()) {