Extend the version API.

Bug: 144554842
Change-Id: I6290d0a644b49a41ea11fff7811e47a343acf19e
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 36adabe..ebebbf1 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -110,7 +110,7 @@
       return;
     }
     if (command.isPrintVersion()) {
-      Version.printToolVersion("D8");
+      System.out.println("D8 " + Version.getVersionString());
       return;
     }
     InternalOptions options = command.getInternalOptions();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ab6796b..9e40279 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -886,7 +886,7 @@
       return;
     }
     if (command.isPrintVersion()) {
-      Version.printToolVersion("R8");
+      System.out.println("R8 " + Version.getVersionString());
       return;
     }
     InternalOptions options = command.getInternalOptions();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6164e5e..98c654c 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -16,21 +16,79 @@
   private Version() {
   }
 
-  public static void printToolVersion(String toolName) {
-    System.out.println(toolName + " " + Version.LABEL);
-    System.out.println(VersionProperties.INSTANCE.getDescription());
+  /** Returns current R8 version (with additional info) as a string. */
+  public static String getVersionString() {
+    return LABEL + " (" + VersionProperties.INSTANCE.getDescription() + ")";
   }
 
-  /** Is this a development version of the D8/R8 library. */
-  public static boolean isDev() {
+  /**
+   * Returns the major version number of the compiler.
+   *
+   * @return Major version or -1 for an unreleased build.
+   */
+  public static int getMajorVersion() {
+    if (LABEL.equals("master")) {
+      return -1;
+    }
+    int start = 0;
+    int end = LABEL.indexOf('.');
+    return Integer.parseInt(LABEL.substring(start, end));
+  }
+
+  /**
+   * Returns the minor version number of the compiler.
+   *
+   * @return Minor version or -1 for an unreleased build.
+   */
+  public static int getMinorVersion() {
+    if (LABEL.equals("master")) {
+      return -1;
+    }
+    int start = LABEL.indexOf('.') + 1;
+    int end = LABEL.indexOf('.', start);
+    return Integer.parseInt(LABEL.substring(start, end));
+  }
+
+  /**
+   * Returns the patch version number of the compiler.
+   *
+   * @return Patch version or -1 for an unreleased build.
+   */
+  public static int getPatchVersion() {
+    if (LABEL.equals("master")) {
+      return -1;
+    }
+    int skip = LABEL.indexOf('.') + 1;
+    int start = LABEL.indexOf('.', skip) + 1;
+    int end = LABEL.indexOf('.', start);
+    return Integer.parseInt(LABEL.substring(start, end));
+  }
+
+  /**
+   * Returns the pre-release version information of the compiler.
+   *
+   * @return Pre-release information if present, the empty string if absent, and null for an
+   *     unreleased build.
+   */
+  public static String getPreReleaseString() {
+    if (LABEL.equals("master")) {
+      return null;
+    }
+    int start = LABEL.indexOf('-') + 1;
+    if (start > 0) {
+      return LABEL.substring(start);
+    }
+    return "";
+  }
+
+  /**
+   * Is this a development version of the D8/R8 library.
+   *
+   * @return True if the build is not a release or if it is a development release.
+   */
+  public static boolean isDevelopmentVersion() {
     return LABEL.equals("master")
         || LABEL.endsWith("-dev")
         || VersionProperties.INSTANCE.isEngineering();
   }
-
-  /** Returns current R8 version (with additional info) as a string. */
-  @SuppressWarnings("unused") // used by external tools to obtain R8 version
-  public static String getVersionString() {
-    return LABEL + " (" + VersionProperties.INSTANCE.getDescription() + ")";
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 68e9e69..a52c2ea 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -326,7 +326,7 @@
       return;
     }
     if (dexArgs.version) {
-      Version.printToolVersion("CompatDx");
+      System.out.println("CompatDx " + Version.getVersionString());
       return;
     }
     CompilationMode mode = CompilationMode.RELEASE;
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 514f785..177f2ae 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -173,7 +173,7 @@
   }
 
   private static void printVersion() {
-    Version.printToolVersion("CompatProguard");
+    System.out.println("CompatProguard " + Version.getVersionString());
   }
 
   private static void printHelp() {
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 011a7ba..0cee87f 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -91,7 +91,7 @@
     if (minApiLevel != null) {
       builder.append("# " + MARKER_KEY_MIN_API + ": " + minApiLevel + "\n");
     }
-    if (Version.isDev()) {
+    if (Version.isDevelopmentVersion()) {
       builder.append(
           "# " + MARKER_KEY_COMPILER_HASH + ": " + VersionProperties.INSTANCE.getSha() + "\n");
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 0652a33..cee2d66 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -395,7 +395,7 @@
       } else {
         String unknownOption = acceptString();
         String devMessage = "";
-        if (Version.isDev()
+        if (Version.isDevelopmentVersion()
             && unknownOption != null
             && (unknownOption.equals("forceinline") || unknownOption.equals("neverinline"))) {
           devMessage = ", this option needs to be turned on explicitly if used for tests.";
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 a8f6cbf..dc998e2 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -202,7 +202,8 @@
   public boolean enableDevirtualization = true;
   public boolean enableNonNullTracking = true;
   public boolean enableInlining =
-      !Version.isDev() || System.getProperty("com.android.tools.r8.disableinlining") == null;
+      !Version.isDevelopmentVersion()
+          || System.getProperty("com.android.tools.r8.disableinlining") == null;
   // TODO(b/141451716): Evaluate the effect of allowing inlining in the inlinee.
   public boolean applyInliningToInlinee = false;
   public int applyInliningToInlineeMaxDepth = 0;
@@ -293,7 +294,7 @@
     if (!isGeneratingClassFiles()) {
       marker.setMinApi(minApiLevel);
     }
-    if (Version.isDev()) {
+    if (Version.isDevelopmentVersion()) {
       marker.setSha1(VersionProperties.INSTANCE.getSha());
     }
     return marker;
@@ -985,7 +986,8 @@
     public boolean addCallEdgesForLibraryInvokes = false;
 
     public boolean allowTypeErrors =
-        !Version.isDev() || System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
+        !Version.isDevelopmentVersion()
+            || System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
     public boolean allowInvokeErrors = false;
     public boolean disableL8AnnotationRemoval = false;
     public boolean allowClassInlinerGracefulExit = true;
diff --git a/src/main/keep.txt b/src/main/keep.txt
index 2748f63..d33b879 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -12,7 +12,13 @@
 -keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
 -keep public class com.android.tools.r8.dexfilemerger.DexFileMerger { public static void main(java.lang.String[]); }
 -keep public class com.android.tools.r8.dexsplitter.DexSplitter { public static void main(java.lang.String[]); }
+
 -keep public class com.android.tools.r8.Version { public static java.lang.String getVersionString(); }
+-keep public class com.android.tools.r8.Version { public static int getMajorVersion(); }
+-keep public class com.android.tools.r8.Version { public static int getMinorVersion(); }
+-keep public class com.android.tools.r8.Version { public static int getPatchVersion(); }
+-keep public class com.android.tools.r8.Version { public static java.lang.String getPreReleaseString(); }
+-keep public class com.android.tools.r8.Version { public static boolean isDevelopmentVersion(); }
 
 -keepattributes LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
 
diff --git a/src/test/java/com/android/tools/r8/VersionTests.java b/src/test/java/com/android/tools/r8/VersionTests.java
new file mode 100644
index 0000000..ba5ee78
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/VersionTests.java
@@ -0,0 +1,65 @@
+// 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;
+
+import static com.android.tools.r8.Version.LABEL;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class VersionTests extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private final TestParameters parameters;
+
+  public VersionTests(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testSemVerInfo() {
+    int majorVersion = Version.getMajorVersion();
+    int minorVersion = Version.getMinorVersion();
+    int patchVersion = Version.getPatchVersion();
+    String preReleaseString = Version.getPreReleaseString();
+    if (LABEL.equals("master")) {
+      assertEquals(-1, majorVersion);
+      assertEquals(-1, minorVersion);
+      assertEquals(-1, patchVersion);
+      assertNull(preReleaseString);
+      assertTrue(Version.getVersionString().startsWith("master"));
+    } else {
+      assertTrue(majorVersion > 0);
+      assertTrue(minorVersion >= 0);
+      assertTrue(patchVersion >= 0);
+      assertNotNull(preReleaseString);
+      assertTrue(
+          Version.getVersionString()
+              .startsWith(
+                  ""
+                      + majorVersion
+                      + "."
+                      + minorVersion
+                      + "."
+                      + patchVersion
+                      + (preReleaseString.isEmpty() ? "" : "-" + preReleaseString)));
+    }
+  }
+
+  @Test
+  public void testDevelopmentPredicate() {
+    assertEquals(LABEL.equals("master") || LABEL.contains("-dev"), Version.isDevelopmentVersion());
+  }
+}