Enable ART 12 as a valid VM for dex2oat.

Bug: b/258170524
Change-Id: I7685058cb9530cc2ecc0f7cf6d6aeb63bb6e1e17
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index cdac93d..443b9e7 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -4,12 +4,11 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.structural.Ordered;
 import java.util.Optional;
 
-/**
- * Android dex version
- */
-public enum DexVersion {
+/** Android dex version */
+public enum DexVersion implements Ordered<DexVersion> {
   V35(35, new byte[] {'0', '3', '5'}),
   V37(37, new byte[] {'0', '3', '7'}),
   V38(38, new byte[] {'0', '3', '8'}),
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d84fe19..1577312 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.keepanno.utils.Unimplemented;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.shaking.FilteredClassPath;
@@ -32,6 +33,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.DexVersion;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -66,6 +68,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -413,9 +416,6 @@
 
     private final Version version;
     private final Kind kind;
-
-    // TODO(b/258170524): Should be a new version that works.
-    public static final DexVm LATEST_DEX2OAT = ART_6_0_1_HOST;
   }
 
 
@@ -715,12 +715,28 @@
   private static final Path DX = getDxExecutablePath();
 
   private static Path getDexVmPath(DexVm vm) {
-    return Paths.get(
-        TOOLS,
-        "linux",
-        vm.getVersion() == DexVm.Version.DEFAULT
-            ? "art"
-            : "art-" + vm.getVersion());
+    DexVm.Version version = vm.getVersion();
+    Path base = Paths.get(TOOLS, "linux");
+    switch (version) {
+      case DEFAULT:
+        return base.resolve("art");
+      case V4_0_4:
+      case V4_4_4:
+      case V5_1_1:
+      case V6_0_1:
+      case V7_0_0:
+      case V8_1_0:
+      case V9_0_0:
+      case V10_0_0:
+        return base.resolve("art-" + version);
+      case V12_0_0:
+        return base.resolve("host").resolve("art-12.0.0-beta4");
+      case V13_0_0:
+      case MASTER:
+        return base.resolve("host").resolve("art-" + version);
+      default:
+        throw new Unreachable();
+    }
   }
 
   private static Path getDexVmLibPath(DexVm vm) {
@@ -736,7 +752,25 @@
   }
 
   private static String getArchString(DexVm vm) {
-    return vm.isOlderThanOrEqual(DexVm.ART_5_1_1_HOST) ? "arm" : "arm64";
+    switch (vm.getVersion()) {
+      case V4_0_4:
+      case V4_4_4:
+      case V5_1_1:
+        return "arm";
+      case V6_0_1:
+      case V7_0_0:
+      case V8_1_0:
+      case DEFAULT:
+      case V9_0_0:
+      case V10_0_0:
+        return "arm64";
+      case V12_0_0:
+      case V13_0_0:
+      case MASTER:
+        return "x86_64";
+      default:
+        throw new Unimplemented();
+    }
   }
 
   private static Path getProductBootImagePath(DexVm vm) {
@@ -1060,6 +1094,10 @@
     }
   }
 
+  public static DexVersion getDexFileVersionForVm(DexVm vm) {
+    return DexVersion.getDexVersion(getMinApiLevelForDexVm(vm));
+  }
+
   private static String getPlatform() {
     return System.getProperty("os.name");
   }
@@ -1995,27 +2033,56 @@
     }
   }
 
-  public static ProcessResult runDex2OatRaw(Path file, Path outFile, DexVm vm) throws IOException {
+  // Checked in VMs for which dex2oat should work specified in decreasing order.
+  private static List<DexVm> SUPPORTED_DEX2OAT_VMS =
+      ImmutableList.of(DexVm.ART_12_0_0_HOST, DexVm.ART_6_0_1_HOST);
+
+  public static ProcessResult runDex2OatRaw(Path file, Path outFile, DexVm targetVm)
+      throws IOException {
     Assume.assumeTrue(ToolHelper.isDex2OatSupported());
-    // TODO(b/258170524): Add working dex2oat. For now we just use latest if it is recent enough.
-    Assume.assumeTrue("b/144975341 & b/258170524", DexVm.LATEST_DEX2OAT.isNewerThanOrEqual(vm));
-    vm = DexVm.LATEST_DEX2OAT;
     assert Files.exists(file);
     assert ByteStreams.toByteArray(Files.newInputStream(file)).length > 0;
+    assert SUPPORTED_DEX2OAT_VMS.stream()
+        .sorted(Comparator.comparing(DexVm::getVersion).reversed())
+        .collect(Collectors.toList())
+        .equals(SUPPORTED_DEX2OAT_VMS);
+    DexVersion requiredDexFileVersion = getDexFileVersionForVm(targetVm);
+    DexVm vm = null;
+    for (DexVm supported : SUPPORTED_DEX2OAT_VMS) {
+      DexVersion supportedDexFileVersion = getDexFileVersionForVm(supported);
+      // This and remaining VMs can't compile code at the required DEX version.
+      if (supportedDexFileVersion.isLessThan(requiredDexFileVersion)) {
+        break;
+      }
+      // Find the "oldest" supported VM. Only consider VMs older than targetVm if no VM matched.
+      if (supported.isNewerThanOrEqual(targetVm) || vm == null) {
+        vm = supported;
+      }
+    }
+    if (vm == null) {
+      throw new Unimplemented("Unable to find a supported dex2oat for VM " + vm);
+    }
     List<String> command = new ArrayList<>();
-    command.add(getDex2OatPath(vm).toString());
-    command.add("--android-root=" + getProductPath(vm) + "/system");
+    command.add(getDex2OatPath(vm).toAbsolutePath().toString());
+    command.add("--android-root=" + getProductPath(vm).toAbsolutePath() + "/system");
     command.add("--runtime-arg");
     command.add("-verbose:verifier");
     command.add("--runtime-arg");
     command.add("-Xnorelocate");
     command.add("--dex-file=" + file.toAbsolutePath());
     command.add("--oat-file=" + outFile.toAbsolutePath());
-    // TODO(zerny): Create a proper interface for invoking dex2oat. Hardcoding arch here is a hack!
     command.add("--instruction-set=" + getArchString(vm));
+    if (vm.version.equals(DexVm.Version.V12_0_0)) {
+      command.add(
+          "--boot-image="
+              + getDexVmPath(vm).toAbsolutePath()
+              + "/apex/art_boot_images/javalib/boot.art");
+    }
     ProcessBuilder builder = new ProcessBuilder(command);
+    builder.directory(getDexVmPath(vm).toFile());
     builder.environment().put("LD_LIBRARY_PATH", getDexVmLibPath(vm).toString());
-    return runProcess(builder);
+    ProcessResult processResult = runProcess(builder);
+    return processResult;
   }
 
   public static ProcessResult runProguardRaw(
@@ -2188,6 +2255,7 @@
 
   public static ProcessResult runProcess(ProcessBuilder builder, PrintStream out)
       throws IOException {
+    out.println("In " + builder.directory());
     String command = String.join(" ", builder.command());
     out.println(command);
     return drainProcessOutputStreams(builder.start(), command);
diff --git a/tools/dex2oat.py b/tools/dex2oat.py
index 04422cf..10c4732 100755
--- a/tools/dex2oat.py
+++ b/tools/dex2oat.py
@@ -12,16 +12,21 @@
 
 LINUX_DIR = os.path.join(utils.TOOLS_DIR, 'linux')
 
+LATEST = '12.0.0'
+
 VERSIONS = [
-  'default',
-  '9.0.0',
-  '8.1.0',
-  '7.0.0',
+  '12.0.0',
+  # TODO(b/258170524): Fix the broken dex2oat versions.
+  # 'default',
+  # '9.0.0',
+  # '8.1.0',
+  # '7.0.0',
   '6.0.1',
-  '5.1.1',
+  # '5.1.1',
 ]
 
 DIRS = {
+  '12.0.0': 'host/art-12.0.0-beta4',
   'default': 'art',
   '9.0.0': 'art-9.0.0',
   '8.1.0': 'art-8.1.0',
@@ -31,6 +36,7 @@
 }
 
 PRODUCTS = {
+  '12.0.0': 'redfin',
   'default': 'angler',
   '9.0.0': 'marlin',
   '8.1.0': 'marlin',
@@ -40,6 +46,7 @@
 }
 
 ARCHS = {
+  '12.0.0': 'x86_64',
   'default': 'arm64',
   '9.0.0': 'arm64',
   '8.1.0': 'arm64',
@@ -58,12 +65,16 @@
   'all',
 ]
 
+BOOT_IMAGE = {
+  '12.0.0': 'apex/art_boot_images/javalib/boot.art'
+}
+
 def ParseOptions():
   parser = optparse.OptionParser()
   parser.add_option('--version',
-                    help='Version of dex2oat. (defaults to latest, eg, tools/linux/art).',
+                    help='Version of dex2oat. (defaults to latest: ' + LATEST + ').',
                     choices=VERSIONS,
-                    default='default')
+                    default=LATEST)
   parser.add_option('--all',
                     help='Run dex2oat on all possible versions',
                     default=False,
@@ -96,7 +107,9 @@
     print("")
   return 0
 
-def run(dexfile, oatfile=None, version='default', verbose=[]):
+def run(dexfile, oatfile=None, version=None, verbose=[]):
+  if not version:
+    version = LATEST
   # dex2oat accepts non-existent dex files, check here instead
   if not os.path.exists(dexfile):
     raise Exception('DEX file not found: "{}"'.format(dexfile))
@@ -117,9 +130,12 @@
     ]
     for flag in verbose:
       cmd += ['--runtime-arg', '-verbose:' + flag]
+    if version in BOOT_IMAGE:
+      cmd += ['--boot-image=' + BOOT_IMAGE[version]]
     env = {"LD_LIBRARY_PATH": os.path.join(base, 'lib')}
     utils.PrintCmd(cmd)
-    subprocess.check_call(cmd, env = env)
+    with utils.ChangedWorkingDirectory(base):
+      subprocess.check_call(cmd, env = env)
 
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions
index 5e38ebd..611c06e 100644
--- a/tools/linux/README.art-versions
+++ b/tools/linux/README.art-versions
@@ -115,6 +115,10 @@
      --art-dir host/art-12.0.0-beta4 \
      --android-product redfin
 
+The dex2oat tool expects to find core-oj.dex in out/host/linux-x86 so copy it.
+
+(cd tools/linux/host; cp -r apex out/host/linux-x86/apex)
+
 (cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-12.0.0-beta4)
 
 art-10.0.0 (Android Q)
diff --git a/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1 b/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1
index b95d909..9f80774 100644
--- a/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1
+++ b/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1
@@ -1 +1 @@
-9b6d0b061669e0fb1b09ba92c3650d5c7bdc9e85
\ No newline at end of file
+df7267e9eff9cc1812b5a7b4111e7175d5e186e9
\ No newline at end of file