Add r8.jar to third_party and BootstrapTest

r8.jar from http://storage.googleapis.com/r8-releases/raw/master/a4aa7a933b7f78deda862b2ce51944b28d3b78ab/r8.jar

The jar was built by the buildbot based on commit a4aa7a933
(Merge "Avoid multiple nops before payload instructions", 2018-04-25).

Change-Id: I463e8cdc393e5bf932e9cfaf8cc4a7d6f82e24e4
diff --git a/src/test/java/com/android/tools/r8/cf/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/BootstrapTest.java
new file mode 100644
index 0000000..0f92fb3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/BootstrapTest.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2018, 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.cf;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.google.common.io.ByteStreams.toByteArray;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.base.Charsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class BootstrapTest extends TestBase {
+
+  private static final Path R8_STABLE_JAR = Paths.get("third_party/r8/r8.jar");
+
+  private static final String R8_NAME = "com.android.tools.r8.R8";
+  private static final String[] KEEP_R8 = {
+      "-keep public class " + R8_NAME + " {",
+      "  public static void main(...);",
+      "}",
+  };
+
+  private static final String HELLO_NAME = "hello.Hello";
+  private static final String[] KEEP_HELLO = {
+      "-keep class " + HELLO_NAME + " {",
+      "  public static void main(...);",
+      "}",
+  };
+
+  private class R8Result {
+
+    final ProcessResult processResult;
+    final Path outputJar;
+    final String pgMap;
+
+    R8Result(ProcessResult processResult, Path outputJar, String pgMap) {
+      this.processResult = processResult;
+      this.outputJar = outputJar;
+      this.pgMap = pgMap;
+    }
+
+    @Override
+    public String toString() {
+      // TODO(mathiasr): Add pgMap to output when resource API (go/r8g/19460) has landed.
+      // Without resource API, comparing pgMaps will fail because R8 does not keep the resource
+      // indicating which Git revision R8 was compiled from.
+      return processResult.toString();
+    }
+  }
+
+  @Test
+  public void test() throws Exception {
+    // Run hello.jar to ensure it exists and is valid.
+    Path hello = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
+    ProcessResult runHello = ToolHelper.runJava(hello, "hello.Hello");
+    assertEquals(0, runHello.exitCode);
+
+    // Run r8.jar on hello.jar to ensure that r8.jar is a working compiler.
+    R8Result runInputR8 = runExternalR8(R8_STABLE_JAR, hello, "input", KEEP_HELLO);
+    ProcessResult runHelloR8 = ToolHelper.runJava(runInputR8.outputJar, "hello.Hello");
+    assertEquals(runHello.toString(), runHelloR8.toString());
+
+    // Run R8 on r8.jar, and run the resulting compiler on hello.jar.
+    Path output = runR8(R8_STABLE_JAR, "r8-r8", KEEP_R8);
+    R8Result runR8R8 = runExternalR8(output, hello, "output", KEEP_HELLO);
+    // Check that the process outputs (exit code, stdout, stderr) are the same.
+    assertEquals(runInputR8.toString(), runR8R8.toString());
+    // Check that the output jars are the same.
+    assertProgramsEqual(runInputR8.outputJar, runR8R8.outputJar);
+  }
+
+  private Path runR8(Path inputJar, String outputFolder, String[] keepRules) throws Exception {
+    Path outputPath = temp.newFolder(outputFolder).toPath();
+    Path outputJar = outputPath.resolve("output.jar");
+    Path pgConfigFile = outputPath.resolve("keep.rules");
+    FileUtils.writeTextFile(pgConfigFile, keepRules);
+    ToolHelper.runR8(
+        R8Command.builder()
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.JAVA_8_RUNTIME))
+            // TODO(mathiasr): Add resources to output when resource API (go/r8g/19460) has landed.
+            .setProgramConsumer(new ClassFileConsumer.ArchiveConsumer(outputJar))
+            .addProgramFiles(inputJar)
+            .addProguardConfigurationFiles(pgConfigFile)
+            .build());
+    return outputJar;
+  }
+
+  private R8Result runExternalR8(Path r8Jar, Path inputJar, String outputFolder, String[] keepRules)
+      throws Exception {
+    Path outputPath = temp.newFolder(outputFolder).toPath();
+    Path pgConfigFile = outputPath.resolve("keep.rules");
+    Path outputJar = outputPath.resolve("output.jar");
+    Path pgMapFile = outputPath.resolve("map.txt");
+    FileUtils.writeTextFile(pgConfigFile, keepRules);
+    ProcessResult processResult =
+        ToolHelper.runJava(
+            r8Jar,
+            R8_NAME,
+            "--lib",
+            ToolHelper.JAVA_8_RUNTIME,
+            "--classfile",
+            inputJar.toString(),
+            "--output",
+            outputJar.toString(),
+            "--pg-conf",
+            pgConfigFile.toString(),
+            "--debug",
+            "--pg-map-output",
+            pgMapFile.toString());
+    if (processResult.exitCode != 0) {
+      System.out.println(processResult);
+    }
+    assertEquals(0, processResult.exitCode);
+    String pgMap = FileUtils.readTextFile(pgMapFile, Charsets.UTF_8);
+    return new R8Result(processResult, outputJar, pgMap);
+  }
+
+  private static void assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
+    ArchiveClassFileProvider expected = new ArchiveClassFileProvider(expectedJar);
+    ArchiveClassFileProvider actual = new ArchiveClassFileProvider(actualJar);
+    assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual));
+    for (String descriptor : expected.getClassDescriptors()) {
+      assertArrayEquals(
+          "Class " + descriptor + " differs",
+          getClassAsBytes(expected, descriptor),
+          getClassAsBytes(actual, descriptor));
+    }
+  }
+
+  private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) {
+    ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors());
+    Collections.sort(descriptorList);
+    return descriptorList;
+  }
+
+  private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor)
+      throws Exception {
+    return toByteArray(inputJar.getProgramResource(descriptor).getByteStream());
+  }
+}
diff --git a/third_party/r8.tar.gz.sha1 b/third_party/r8.tar.gz.sha1
new file mode 100644
index 0000000..fbb2aa8
--- /dev/null
+++ b/third_party/r8.tar.gz.sha1
@@ -0,0 +1 @@
+38b85dcea75f12c37332a5425c87733e78754ba6
\ No newline at end of file