Report unsupported based on desugared library version.

Bug: 158645207
Change-Id: Iaf8ead86c709492e42c29fd55a812d78fbf5ac41
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
index a1af388..d687d19 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SemanticVersion;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
@@ -22,14 +23,7 @@
 public class DesugaredLibraryConfigurationParser {
 
   public static final int MAX_SUPPORTED_VERSION = 4;
-
-  private static final String MIN_DESUGARED_LIBRARY_WITH_COMMON_FLAGS = "1.0.9";
-
-  private static final String UNSUPPORTED_MESSAGE =
-      "Unsupported desugared library version, please upgrade the"
-          + " desugared library to at least version "
-          + MIN_DESUGARED_LIBRARY_WITH_COMMON_FLAGS
-          + ".";
+  public static final SemanticVersion MIN_SUPPORTED_VERSION = new SemanticVersion(1, 0, 9);
 
   static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version";
   static final String VERSION_KEY = "version";
@@ -73,15 +67,11 @@
   }
 
   private JsonElement required(JsonObject json, String key) {
-    return required(
-        json,
-        key,
-        "Invalid desugared library configuration. " + "Expected required key '" + key + "'");
-  }
-
-  private JsonElement required(JsonObject json, String key, String message) {
     if (!json.has(key)) {
-      throw reporter.fatalError(new StringDiagnostic(message, origin));
+      throw reporter.fatalError(
+          new StringDiagnostic(
+              "Invalid desugared library configuration. Expected required key '" + key + "'",
+              origin));
     }
     return json.get(key);
   }
@@ -115,6 +105,18 @@
     }
 
     String version = required(jsonConfig, VERSION_KEY).getAsString();
+    SemanticVersion semanticVersion = SemanticVersion.parse(version);
+    if (!semanticVersion.isNewerOrEqual(MIN_SUPPORTED_VERSION)) {
+      throw reporter.fatalError(
+          new StringDiagnostic(
+              "Unsupported desugared library version: "
+                  + version
+                  + ", please upgrade the desugared library to at least version "
+                  + MIN_SUPPORTED_VERSION
+                  + ".",
+              origin));
+    }
+
     String groupID = required(jsonConfig, GROUP_ID_KEY).getAsString();
     String artifactID = required(jsonConfig, ARTIFACT_ID_KEY).getAsString();
     String identifier = String.join(":", groupID, artifactID, version);
@@ -126,7 +128,7 @@
         required(jsonConfig, REQUIRED_COMPILATION_API_LEVEL_KEY).getAsInt();
     configurationBuilder.setRequiredCompilationAPILevel(
         AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level));
-    JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY, UNSUPPORTED_MESSAGE);
+    JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY);
     JsonElement libraryFlags = required(jsonConfig, LIBRARY_FLAGS_KEY);
     JsonElement programFlags = required(jsonConfig, PROGRAM_FLAGS_KEY);
     parseFlagsList(commonFlags.getAsJsonArray());
diff --git a/src/main/java/com/android/tools/r8/utils/SemanticVersion.java b/src/main/java/com/android/tools/r8/utils/SemanticVersion.java
new file mode 100644
index 0000000..977073d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SemanticVersion.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2020, 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.utils;
+
+import java.util.Objects;
+
+public class SemanticVersion {
+
+  public static SemanticVersion parse(String version) {
+    int majorEnd = version.indexOf('.');
+    if (majorEnd <= 0) {
+      throw new IllegalArgumentException("Invalid semantic version: " + version);
+    }
+    int minorEnd = version.indexOf('.', majorEnd + 1);
+    if (minorEnd <= majorEnd) {
+      throw new IllegalArgumentException("Invalid semantic version: " + version);
+    }
+    // No current support for extensions.
+    int patchEnd = version.length();
+    int major;
+    int minor;
+    int patch;
+    try {
+      major = Integer.parseInt(version.substring(0, majorEnd));
+      minor = Integer.parseInt(version.substring(majorEnd + 1, minorEnd));
+      patch = Integer.parseInt(version.substring(minorEnd + 1, patchEnd));
+    } catch (NumberFormatException e) {
+      throw new IllegalArgumentException("Invalid semantic version: " + version, e);
+    }
+    return new SemanticVersion(major, minor, patch);
+  }
+
+  private final int major;
+  private final int minor;
+  private final int patch;
+
+  public SemanticVersion(int major, int minor, int patch) {
+    this.major = major;
+    this.minor = minor;
+    this.patch = patch;
+  }
+
+  public int getMajor() {
+    return major;
+  }
+
+  public int getMinor() {
+    return minor;
+  }
+
+  public int getPatch() {
+    return patch;
+  }
+
+  public boolean isNewerOrEqual(SemanticVersion other) {
+    if (major != other.major) {
+      return major > other.major;
+    }
+    if (minor != other.minor) {
+      return minor > other.minor;
+    }
+    return patch >= other.patch;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof SemanticVersion)) {
+      return false;
+    }
+    SemanticVersion other = (SemanticVersion) obj;
+    return major == other.major && minor == other.minor && patch == other.patch;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(major, minor, patch);
+  }
+
+  @Override
+  public String toString() {
+    return "" + major + "." + minor + "." + patch;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
index 92fc7be..07c0703 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SemanticVersion;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.google.common.collect.ImmutableList;
@@ -73,7 +74,7 @@
               DesugaredLibraryConfigurationParser.MAX_SUPPORTED_VERSION)
           .put("group_id", "com.tools.android")
           .put("artifact_id", "desugar_jdk_libs")
-          .put("version", "0.0.0")
+          .put("version", DesugaredLibraryConfigurationParser.MIN_SUPPORTED_VERSION.toString())
           .put("required_compilation_api_level", 1)
           .put("synthesized_library_classes_package_prefix", "j$.")
           .put("common_flags", Collections.emptyList())
@@ -141,6 +142,7 @@
             "version",
             "required_compilation_api_level",
             "synthesized_library_classes_package_prefix",
+            "common_flags",
             "program_flags",
             "library_flags");
     for (String key : requiredKeys) {
@@ -158,9 +160,13 @@
   }
 
   @Test
-  public void testUnsupportedFormatMissingFlags() {
+  public void testUnsupportedVersion() {
     LinkedHashMap<String, Object> data = template();
-    data.remove("common_flags");
+    SemanticVersion minVersion = DesugaredLibraryConfigurationParser.MIN_SUPPORTED_VERSION;
+    data.put(
+        "version",
+        new SemanticVersion(minVersion.getMajor(), minVersion.getMinor(), minVersion.getPatch() - 1)
+            .toString());
     runFailing(
         toJson(data),
         diagnostics ->
diff --git a/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java b/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java
new file mode 100644
index 0000000..8f0dd61
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2020, 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.utils;
+
+import static com.android.tools.r8.utils.SemanticVersion.parse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SemanticVersionTests extends TestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public SemanticVersionTests(TestParameters parameters) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+  }
+
+  @Test
+  public void test() throws Exception {
+    assertTrue(parse("1.0.1").isNewerOrEqual(parse("1.0.0")));
+    assertFalse(parse("1.0.1").isNewerOrEqual(parse("1.1.0")));
+    assertTrue(parse("1.1.0").isNewerOrEqual(parse("1.0.1")));
+    assertFalse(parse("1.1.1").isNewerOrEqual(parse("2.0.0")));
+
+    assertTrue(parse("2.0.0").isNewerOrEqual(parse("1.1.1")));
+    assertTrue(parse("42.42.42").isNewerOrEqual(parse("9.9.9")));
+    assertTrue(parse("9.42.42").isNewerOrEqual(parse("9.9.9")));
+    assertTrue(parse("9.9.42").isNewerOrEqual(parse("9.9.9")));
+
+    assertFalse(parse("1.1.1").isNewerOrEqual(parse("2.0.0")));
+    assertFalse(parse("9.9.9").isNewerOrEqual(parse("42.42.42")));
+    assertFalse(parse("9.9.9").isNewerOrEqual(parse("9.42.42")));
+    assertFalse(parse("9.9.9").isNewerOrEqual(parse("9.9.42")));
+  }
+}