Add integration tests for the aspect

Theses tests add support for downloading Bazel installer, and use it
for integration testing of the aspect to check that we can parse the
result.

Change-Id: I4e09afd894a5def21c18076c7e375df3bc4f0f30
diff --git a/WORKSPACE b/WORKSPACE
index 4fd9945..87c8078 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,8 +1,10 @@
 workspace(name = "build_bazel_eclipse")
 
 load("//tools/build_defs:eclipse.bzl", "load_eclipse_deps")
-load(":bazel_version.bzl", "check_bazel_version")
+load("//:bazel_version.bzl", "check_bazel_version")
+load("//tools/build_defs:bazel_integration_test.bzl", "bazel_binaries")
 check_bazel_version("0.5.0")
+bazel_binaries()
 
 load_eclipse_deps()
 
diff --git a/java/com/google/devtools/bazel/e4b/command/BUILD b/java/com/google/devtools/bazel/e4b/command/BUILD
index abad7f5..0652045 100644
--- a/java/com/google/devtools/bazel/e4b/command/BUILD
+++ b/java/com/google/devtools/bazel/e4b/command/BUILD
@@ -3,7 +3,7 @@
     srcs = glob(["*.java"]),
     visibility = [
         "//:__pkg__",
-        "//javatests/com/google/devtools/bazel/e4b/command:__pkg__",
+        "//javatests/com/google/devtools/bazel/e4b:__subpackages__",
     ],
     deps = [
         "@com_google_guava//jar",
diff --git a/java/com/google/devtools/bazel/e4b/command/Command.java b/java/com/google/devtools/bazel/e4b/command/Command.java
index 6217ae0..19eacfe 100644
--- a/java/com/google/devtools/bazel/e4b/command/Command.java
+++ b/java/com/google/devtools/bazel/e4b/command/Command.java
@@ -14,15 +14,16 @@
 
 package com.google.devtools.bazel.e4b.command;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.function.Function;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory;
+
 /**
  * A utility class to spawn a command and parse its output. It allow to filter the output,
  * redirecting part of it to the console and getting the rest in a list of string.
@@ -30,7 +31,7 @@
  * <p>
  * This class can only be initialized using a builder created with the {@link #builder()} method.
  */
-final class Command {
+final public class Command {
 
   private final File directory;
   private final ImmutableList<String> args;
@@ -127,7 +128,7 @@
    *
    * @see {@link Builder#setStderrLineSelector(Function)}
    */
-  ImmutableList<String> getSelectedErrorLines() {
+  public ImmutableList<String> getSelectedErrorLines() {
     return stderr.getLines();
   }
 
@@ -137,14 +138,14 @@
    *
    * @see {@link Builder#setStdoutLineSelector(Function)}
    */
-  ImmutableList<String> getSelectedOutputLines() {
+  public ImmutableList<String> getSelectedOutputLines() {
     return stdout.getLines();
   }
 
   /**
    * A builder class to generate a Command object.
    */
-  static class Builder {
+  public static class Builder {
 
     private String consoleName = null;
     private File directory;
diff --git a/java/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java b/java/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java
index 1f279fa..be33f11 100644
--- a/java/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java
+++ b/java/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java
@@ -23,6 +23,8 @@
 import org.json.JSONObject;
 import org.json.JSONTokener;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -32,6 +34,8 @@
  */
 public final class IdeBuildInfo {
 
+  private static final Joiner COMMA_JOINER = Joiner.on(",");
+
   /**
    * A structure containing the list of jar files generated by a target (interface, class and source
    * jars).
@@ -48,6 +52,19 @@
     }
 
     @Override
+    public String toString() {
+      StringBuffer builder = new  StringBuffer();
+      builder.append("Jars(jar = ").append(jar);
+      if (ijar != null) {
+        builder.append(", ijar = ").append(ijar);
+      }
+      if (srcjar != null) {
+        builder.append(", srcjar = ").append(srcjar);
+      }
+      return builder.append(")").toString();
+    }
+
+    @Override
     public int hashCode() {
       return Objects.hash(ijar, jar, srcjar);
     }
@@ -87,12 +104,27 @@
     this.sources = jsonToStringArray(object.getJSONArray("sources"));
   }
 
+  @Override
+  public String toString() {
+    StringBuffer builder = new StringBuffer();
+    builder.append("IdeBuildInfo(\n");
+    builder.append("  label = ").append(label).append(",\n");
+    builder.append("  location = ").append(location).append(",\n");
+    builder.append("  kind = ").append(kind).append(",\n");
+    builder.append("  jars = [").append(COMMA_JOINER.join(jars)).append("],\n");
+    builder.append("  generatedJars = [").append(COMMA_JOINER.join(generatedJars)).append("],\n");
+    builder.append("  deps = [").append(COMMA_JOINER.join(deps)).append("],\n");
+    builder.append("  sources = [").append(COMMA_JOINER.join(sources)).append("])");
+    return builder.toString();
+  }
+
   /**
    * Constructs a map of label -> {@link IdeBuildInfo} from a list of files, parsing each files into
    * a {@link JSONObject} and then converting that {@link JSONObject} to an {@link IdeBuildInfo}
    * object.
    */
-  static ImmutableMap<String, IdeBuildInfo> getInfo(List<String> files)
+  @VisibleForTesting
+  public static ImmutableMap<String, IdeBuildInfo> getInfo(List<String> files)
       throws IOException, InterruptedException {
     ImmutableMap.Builder<String, IdeBuildInfo> infos = ImmutableMap.builder();
     for (String s : files) {
diff --git a/javatests/com/google/devtools/bazel/e4b/integration/AspectIntegrationTest.java b/javatests/com/google/devtools/bazel/e4b/integration/AspectIntegrationTest.java
new file mode 100644
index 0000000..505f046
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/integration/AspectIntegrationTest.java
@@ -0,0 +1,144 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.integration;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.bazel.e4b.command.Command;
+import com.google.devtools.bazel.e4b.command.IdeBuildInfo;
+
+/** Integration test for the aspect used by the plugin. */
+public final class AspectIntegrationTest extends BazelBaseTestCase {
+
+  private File aspectWorkspace;
+
+  @Before
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    aspectWorkspace = workspace;
+    copyFromRunfiles("build_bazel_eclipse/resources/e4b_aspect.bzl", "e4b_aspect.bzl");
+    scratchFile("BUILD");
+    newWorkspace();
+    createJavaProgram();
+  }
+
+  private void createJavaProgram() throws Exception {
+    scratchFile("java/my/pkg/Main.java", // force-new-line
+        "package my.pkg;", // force-new-line
+        "import my.other.pkg.Annex;", // force-new-line
+        "public class Main {", // force-new-line
+        "  public static void main(String[] args) {", // force-new-line
+        "    System.out.println(new Annex().helloWorld());", // force-new-line
+        "  }", // force-new-line
+        "}");
+    scratchFile("java/my/pkg/BUILD", // force-new-line
+        "java_binary(name='pkg',", // force-new-line
+        "            srcs=['Main.java'],", // force-new-line
+        "            deps=['//java/my/other/pkg:Annex'])");
+    scratchFile("java/my/other/pkg/Annex.java", // force-new-line
+        "package my.other.pkg;", // force-new-line
+
+
+        "public class Annex {", // force-new-line
+        "  public Annex() {}", // force-new-line
+
+        "  public String helloWorld() {", // force-new-line
+        "    return \"Hello, World!\";", // force-new-line
+        "  }", // force-new-line
+        "}");
+    scratchFile("java/my/other/pkg/BUILD", // force-new-line
+        "java_library(name='Annex',", // force-new-line
+        "             srcs=['Annex.java'],", // force-new-line
+        "             visibility = ['//visibility:public'])");
+    scratchFile("javatests/my/other/pkg/AnnexTest.java", // force-new-line
+        "package my.other.pkg;", // force-new-line
+
+        "import static org.junit.Assert.assertEquals;", // force-new-line
+        "import org.junit.Test;", // force-new-line
+
+
+        "public class AnnexTest {", // force-new-line
+        "  @Test", // force-new-line
+        "  public void testAnnex() {", // force-new-line
+        "    assertEquals(\"Hello, World!\", new Annex().helloWorld());", // force-new-line
+        "  }", // force-new-line
+        "}");
+    scratchFile("javatests/my/other/pkg/BUILD", // force-new-line
+        "java_test(name='AnnexTest',", // force-new-line
+        "          srcs=['AnnexTest.java'],", // force-new-line
+        "          deps=['//java/my/other/pkg:Annex'])");
+  }
+
+  @Test
+  public void testAspectGenerateJson() throws Exception {
+    Command cmd = bazel("build", "--override_repository=local_eclipse_aspect=" + aspectWorkspace,
+        "--aspects=@local_eclipse_aspect//:e4b_aspect.bzl%e4b_aspect", "-k",
+        "--output_groups=ide-info-text,ide-resolve,-_,-defaults", "--experimental_show_artifacts",
+        "//...");
+    int retCode = cmd.run();
+    assertEquals("Bazel failed to build, stderr: " + LINE_JOINER.join(cmd.getSelectedErrorLines()),
+        0, retCode);
+    String[] jsonFiles = cmd.getSelectedErrorLines().stream().filter((s) -> {
+      return s.startsWith(">>>") && s.endsWith(".json");
+    }).map((s) -> {
+      return s.substring(3);
+    }).toArray(String[]::new);
+    assertThat(jsonFiles).hasLength(3);
+
+    ImmutableMap<String, IdeBuildInfo> infos =
+        IdeBuildInfo.getInfo(ImmutableList.<String>copyOf(jsonFiles));
+
+    assertThat(infos).hasSize(3);
+
+    assertThat(infos.get("//java/my/pkg:pkg").getLabel()).isEqualTo("//java/my/pkg:pkg");
+    assertThat(infos.get("//java/my/pkg:pkg").getLocation()).isEqualTo("java/my/pkg/BUILD");
+    assertThat(infos.get("//java/my/pkg:pkg").getKind()).isEqualTo("java_binary");
+    assertThat(infos.get("//java/my/pkg:pkg").getGeneratedJars()).isEmpty();
+    assertThat(infos.get("//java/my/pkg:pkg").getDeps())
+        .containsExactly("//java/my/other/pkg:Annex");
+    assertThat(infos.get("//java/my/pkg:pkg").getSources())
+        .containsExactly("java/my/pkg/Main.java");
+
+    assertThat(infos.get("//java/my/other/pkg:Annex").getLabel())
+        .isEqualTo("//java/my/other/pkg:Annex");
+    assertThat(infos.get("//java/my/other/pkg:Annex").getLocation())
+        .isEqualTo("java/my/other/pkg/BUILD");
+    assertThat(infos.get("//java/my/other/pkg:Annex").getKind()).isEqualTo("java_library");
+    assertThat(infos.get("//java/my/other/pkg:Annex").getGeneratedJars()).isEmpty();
+    assertThat(infos.get("//java/my/other/pkg:Annex").getDeps()).isEmpty();
+    assertThat(infos.get("//java/my/other/pkg:Annex").getSources())
+        .containsExactly("java/my/other/pkg/Annex.java");
+
+    assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getLabel())
+        .isEqualTo("//javatests/my/other/pkg:AnnexTest");
+    assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getLocation())
+        .isEqualTo("javatests/my/other/pkg/BUILD");
+    assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getKind()).isEqualTo("java_test");
+    assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getGeneratedJars()).isEmpty();
+    assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getDeps())
+        .containsExactly("//java/my/other/pkg:Annex");
+    assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getSources())
+        .containsExactly("javatests/my/other/pkg/AnnexTest.java");
+  }
+}
diff --git a/javatests/com/google/devtools/bazel/e4b/integration/BUILD b/javatests/com/google/devtools/bazel/e4b/integration/BUILD
new file mode 100644
index 0000000..1c15c75
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/integration/BUILD
@@ -0,0 +1,45 @@
+load("//tools/build_defs:bazel_integration_test.bzl", "BAZEL_VERSIONS")
+
+java_library(
+    name = "base",
+    srcs = ["BazelBaseTestCase.java"],
+    data = [
+        "@build_bazel_bazel_%s//:bazel_binary" % v.replace(".", "_")
+        for v in BAZEL_VERSIONS
+    ],
+    deps = [
+        "//java/com/google/devtools/bazel/e4b/command",
+        "@com_google_guava//jar",
+        "@org_junit//jar",
+    ],
+)
+
+[java_test(
+    name = "base_integration_test_with_bazel_" + version,
+    srcs = ["BazelBaseTestCaseTest.java"],
+    test_class = "com.google.devtools.bazel.e4b.integration.BazelBaseTestCaseTest",
+    jvm_flags = ["-Dbazel.version=" + version],
+    deps = [
+        ":base",
+        "//java/com/google/devtools/bazel/e4b/command",
+        "@com_google_truth//jar",
+        "@org_hamcrest_core//jar",
+        "@org_junit//jar",
+    ],
+)for version in BAZEL_VERSIONS]
+
+[java_test(
+    name = "aspect_integration_test_with_bazel_" + version,
+    srcs = ["AspectIntegrationTest.java"],
+    test_class = "com.google.devtools.bazel.e4b.integration.AspectIntegrationTest",
+    data = ["//resources:srcs"],
+    jvm_flags = ["-Dbazel.version=" + version],
+    deps = [
+        ":base",
+        "//java/com/google/devtools/bazel/e4b/command",
+        "@com_google_guava//jar",
+        "@com_google_truth//jar",
+        "@org_hamcrest_core//jar",
+        "@org_junit//jar",
+    ],
+) for version in BAZEL_VERSIONS]
diff --git a/javatests/com/google/devtools/bazel/e4b/integration/BazelBaseTestCase.java b/javatests/com/google/devtools/bazel/e4b/integration/BazelBaseTestCase.java
new file mode 100644
index 0000000..1ed8838
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/integration/BazelBaseTestCase.java
@@ -0,0 +1,175 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.integration;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.bazel.e4b.command.Command;
+
+/** A base class to do integration test that call Bazel */
+public abstract class BazelBaseTestCase {
+
+  protected final static Joiner PATH_JOINER = Joiner.on(File.separator);
+  protected final static Joiner LINE_JOINER = Joiner.on("\n");
+
+  private static File tmp;
+  private static Map<String, File> bazelVersions;
+  private static File runfileDirectory = new File(System.getenv("TEST_SRCDIR"));
+
+  private File currentBazel = null;
+
+  /** The current workspace. */
+  protected File workspace = null;
+
+  public static class BazelTestCaseException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    private BazelTestCaseException(String message) {
+      super(message);
+    }
+  }
+
+  @BeforeClass
+  public static void setUpClass() throws IOException {
+    // Get tempdir
+    String _tmp = System.getenv("TEST_TMPDIR");
+    if (_tmp == null) {
+      File p = Files.createTempDirectory("e4b-tests").toFile();
+      p.deleteOnExit();
+      tmp = p;
+    } else {
+      tmp = new File(_tmp);
+    }
+    bazelVersions = new HashMap<>();
+  }
+
+  /** Return a file in the runfiles whose path segments are given by the arguments. */
+  protected static File getRunfile(String... segments) {
+    return new File(PATH_JOINER.join(runfileDirectory, PATH_JOINER.join(segments)));
+  }
+
+  private static void unpackBazel(String version)
+      throws BazelTestCaseException, IOException, InterruptedException {
+    if (!bazelVersions.containsKey(version)) {
+      // Get bazel location
+      File bazelFile = getRunfile("build_bazel_bazel_" + version.replace('.', '_') + "/bazel");
+      if (!bazelFile.exists()) {
+        throw new BazelTestCaseException(
+            "Bazel version " + version + " not found");
+      }
+      bazelVersions.put(version, bazelFile);
+      // Unzip Bazel
+      prepareCommand(tmp,
+          ImmutableList.of(bazelVersions.get(version).getCanonicalPath(),
+              "--output_user_root=" + tmp,
+          "--nomaster_bazelrc",
+              "--max_idle_secs=30", "--bazelrc=/dev/null", "help")).run();
+    }
+  }
+
+  /** Specify with bazel version to use, required before calling bazel. */
+  protected void bazelVersion(String version)
+      throws BazelTestCaseException, IOException, InterruptedException {
+    unpackBazel(version);
+    currentBazel = bazelVersions.get(version);
+  }
+
+  /** Create a new workspace, previous one can still be used. */
+  protected void newWorkspace() throws IOException {
+    this.workspace = java.nio.file.Files.createTempDirectory(tmp.toPath(), "workspace").toFile();
+    this.scratchFile("WORKSPACE");
+  }
+
+  @Before
+  public void setUp() throws Exception {
+    this.currentBazel = null;
+    if (System.getProperty("bazel.version") != null) {
+      bazelVersion(System.getProperty("bazel.version"));
+    }
+    newWorkspace();
+  }
+
+  /** Prepare bazel for running, and return the {@link Command} object to run it. */
+  protected Command bazel(String... args) throws BazelTestCaseException, IOException {
+    return bazel(ImmutableList.copyOf(args));
+  }
+
+  /** Prepare bazel for running, and return the {@link Command} object to run it. */
+  protected Command bazel(Iterable<String> args) throws BazelTestCaseException, IOException {
+    if (currentBazel == null) {
+      throw new BazelTestCaseException("Cannot use bazel because no version was specified, "
+          + "please call bazelVersion(version) before calling bazel(...).");
+    }
+
+    return prepareCommand(workspace,
+        ImmutableList.<String>builder()
+            .add(currentBazel.getCanonicalPath(), "--output_user_root=" + tmp, "--nomaster_bazelrc",
+                "--max_idle_secs=10", "--bazelrc=/dev/null")
+            .addAll(args).build());
+  }
+
+  /**
+   * Copy a file from the runfiles under {@code path} into {@code destpath} under the current
+   * workspace.
+   */
+  protected void copyFromRunfiles(String path, String destpath) throws IOException {
+    File origin = getRunfile(path);
+    File dest = new File(workspace, destpath);
+    if (!dest.getParentFile().exists()) {
+      dest.getParentFile().mkdirs();
+    }
+    Files.copy(origin.toPath(), dest.toPath());
+  }
+
+  /**
+   * Copy a file from the runfiles under {@code path} into {@code path} under the current workspace.
+   */
+  protected void copyFromRunfiles(String path) throws IOException {
+    copyFromRunfiles(path, path);
+  }
+
+  /**
+   * Create a file under {@code path} in the current workspace, filling it with the lines given in
+   * {@code content}.
+   */
+  protected void scratchFile(String path, String... content) throws IOException {
+    File dest = new File(workspace,path);
+    if (!dest.getParentFile().exists()) {
+      dest.getParentFile().mkdirs();
+    }
+    Files.write(dest.toPath(), LINE_JOINER.join(content).getBytes(StandardCharsets.UTF_8));
+  }
+
+  private static Command prepareCommand(File folder, Iterable<String> command) throws IOException {
+    Command.Builder builder = Command.builder(null).setConsoleName(null).setDirectory(folder);
+    builder.addArguments(command);
+    builder.setStderrLineSelector((String x) -> {
+      return x;
+    }).setStdoutLineSelector((String x) -> {
+      return x;
+    });
+    return builder.build();
+  }
+}
diff --git a/javatests/com/google/devtools/bazel/e4b/integration/BazelBaseTestCaseTest.java b/javatests/com/google/devtools/bazel/e4b/integration/BazelBaseTestCaseTest.java
new file mode 100644
index 0000000..c7be128
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/integration/BazelBaseTestCaseTest.java
@@ -0,0 +1,34 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.integration;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.google.devtools.bazel.e4b.command.Command;
+
+/** {@link BazelBaseTestCase}Test */
+public final class BazelBaseTestCaseTest extends BazelBaseTestCase {
+
+  @Test
+  public void testVersion() throws Exception {
+    Command cmd = bazel("info", "release");
+    assertEquals(0, cmd.run());
+    assertThat(cmd.getSelectedOutputLines())
+        .contains("release " + System.getProperty("bazel.version"));
+  }
+}
diff --git a/tools/build_defs/BUILD b/tools/build_defs/BUILD
index 00dc44c..673e8ba 100644
--- a/tools/build_defs/BUILD
+++ b/tools/build_defs/BUILD
@@ -1,6 +1,12 @@
 package(default_visibility = ["//visibility:public"])
 
 py_binary(
+    name = "create_bazel_hashdict",
+    srcs = ["create_bazel_hashdict.py"],
+    deps = ["@com_google_python_gflags//:gflags"],
+)
+
+py_binary(
     name = "feature_builder",
     srcs = ["feature_builder.py"],
     deps = ["@com_google_python_gflags//:gflags"],
diff --git a/tools/build_defs/bazel_hash_dict.bzl b/tools/build_defs/bazel_hash_dict.bzl
new file mode 100644
index 0000000..40b16e0
--- /dev/null
+++ b/tools/build_defs/bazel_hash_dict.bzl
@@ -0,0 +1,20 @@
+# Automatically generated by create_hash_dict '--output=tools/build_defs/bazel_hash_dict.bzl'
+
+BAZEL_HASH_DICT = {
+    '0.5.0': {
+        'darwin-x86_64': '5ccdb953dc2b8d81f15ea185f47ecfc7ddc39ad0c6f71e561ec7398377c8cc1a',
+        'linux-x86_64': 'd026e581a860f305791f3ba839462ff02b1929858b37d1db2f27af212be73741',
+    },
+    '0.5.1': {
+        'darwin-x86_64': '8d92a67a204abdd84376a4265d372e4a9bfc31872e825c028ce261d20bad352a',
+        'linux-x86_64': '27bc739082a241e2f7f1a89fbaea3306e3edc40d930472c6789d49dc17fde3d2',
+    },
+    '0.5.2': {
+        'darwin-x86_64': '31b92de24cd251923b09773c4c20bcf2014390d930c6a3b7f043558975743510',
+        'linux-x86_64': '9a1b6fff69ba8aff460bd1883dd51702b7ad0e4c979c5dcab75baf65027684ef',
+    },
+    '0.5.3': {
+        'darwin-x86_64': '4bbcf198c9daeab8597f748aead68e10bcb3ce720fb8e3d474b2e72825c23fb0',
+        'linux-x86_64': '7545e5164450c8777aca07903328c9744f930bcba51f2a10fe54f3d1ece49097',
+    },
+}
diff --git a/tools/build_defs/bazel_integration_test.bzl b/tools/build_defs/bazel_integration_test.bzl
new file mode 100644
index 0000000..5b0b3ae
--- /dev/null
+++ b/tools/build_defs/bazel_integration_test.bzl
@@ -0,0 +1,56 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# My custom made integration test framework for using Bazel
+load(":bazel_hash_dict.bzl", "BAZEL_HASH_DICT")
+
+BAZEL_VERSIONS = BAZEL_HASH_DICT.keys()
+
+_BAZEL_BINARY_PACKAGE = "http://releases.bazel.build/{version}/release/bazel-{version}-without-jdk-installer-{platform}.sh"
+
+def _get_platform_name(rctx):
+  os_name = rctx.os.name.lower()
+  # We default on linux-x86_64 because we only support 2 platforms
+  return "darwin-x86_64" if os_name.startswith("mac os") else "linux-x86_64"
+
+def _get_installer(rctx):
+  platform = _get_platform_name(rctx)
+  version = rctx.attr.version
+  url = _BAZEL_BINARY_PACKAGE.format(version=version, platform=platform)
+  args = {"url": url, "type": "zip", "output": "bin"}
+  if version in BAZEL_HASH_DICT and platform in BAZEL_HASH_DICT[version]:
+    args["sha256"] = BAZEL_HASH_DICT[version][platform]
+  rctx.download_and_extract(**args)
+
+def _bazel_repository_impl(rctx):
+  _get_installer(rctx)
+  rctx.file("WORKSPACE", "workspace(name='%s')" % rctx.attr.name)
+  rctx.file("BUILD", """
+genrule(
+  name = "bazel_binary",
+  outs = ["bazel"],
+  srcs = ["bin/bazel-real"],
+  cmd = "cp $< $@",
+  output_to_bindir = True,
+  visibility = ["//visibility:public"])""")
+
+bazel_binary = repository_rule(
+  implementation=_bazel_repository_impl,
+  attrs = {"version": attr.string(default = "0.5.3")})
+
+def bazel_binaries(versions = BAZEL_VERSIONS):
+  for version in versions:
+    bazel_binary(
+      name = "build_bazel_bazel_" + version.replace(".", "_"),
+      version = version)
diff --git a/tools/build_defs/create_bazel_hashdict.py b/tools/build_defs/create_bazel_hashdict.py
new file mode 100644
index 0000000..f5a2ff2
--- /dev/null
+++ b/tools/build_defs/create_bazel_hashdict.py
@@ -0,0 +1,97 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a Skylark file containing a map of hash of bazel installers."""
+
+import gflags
+import urllib2
+import sys
+
+_URL_FORMAT = "http://releases.bazel.build/{version}/release/bazel-{version}-without-jdk-installer-{platform}.sh.sha256"
+_URL_EXISTS = "http://releases.bazel.build/{version}/release/index.html"
+
+gflags.DEFINE_string("output", "bazel_hash_dict.bzl", "The output file")
+
+gflags.DEFINE_string("map_name", "BAZEL_HASH_DICT",
+                     "The name of the generated map in the output file")
+
+gflags.DEFINE_multistring("platforms", ["darwin-x86_64", "linux-x86_64"],
+                          "List of platforms to download SHA-256.")
+
+gflags.DEFINE_string("minimum_version", "0.5.0",
+                     "The lowest version of Bazel supported")
+
+FLAGS = gflags.FLAGS
+
+
+def get_hash_map(f):
+    """Construct the hash map reading the release website, writing it to f."""
+    splitted_version = FLAGS.minimum_version.split(".")
+    if len(splitted_version) != 3:
+        sys.stderr.write(("Invalid version '%s', "
+                          "expected a dot-separated version with 3 components "
+                          "(e.g. 3.1.2)") % FLAGS.minimum_version)
+        sys.exit(-1)
+    version = [
+        int(splitted_version[0]),
+        int(splitted_version[1]),
+        int(splitted_version[2])
+    ]
+    while True:
+        try:
+            v = "%s.%s.%s" % (version[0], version[1], version[2])
+            print "Getting SHA-256 for version " + v
+            # Force 404 before we actually add the information
+            urllib2.urlopen(_URL_EXISTS.format(version=v)).read()
+            f.write("    '%s': {\n" % v)
+            for platform in FLAGS.platforms:
+                r = urllib2.urlopen(
+                    _URL_FORMAT.format(version=v, platform=platform))
+                f.write("        '%s': '%s',\n" % (platform,
+                                                   r.read().split(" ", 1)[0]))
+            f.write("    },\n")
+            version[2] += 1
+        except urllib2.HTTPError as e:
+            if e.code == 404:
+                print "  ==> Not a Bazel version"
+                # Current version does not exists, increase the lowest non null version number
+                if version[2] == 0:
+                    if version[1] == 0:
+                        return
+                    version[1] = 0
+                    version[0] += 1
+                else:
+                    version[2] = 0
+                    version[1] += 1
+            else:
+                raise e
+
+
+def print_command_line(f):
+    """Print the current command line."""
+    f.write("create_hash_dict")
+    for i in range(1, len(sys.argv)):
+        f.write(" '%s'" % (sys.argv[i].replace("'", "'\\''")))
+
+
+def main(unused_argv):
+    with open(FLAGS.output, "w") as f:
+        f.write("# Automatically generated by ")
+        print_command_line(f)
+        f.write("\n\n%s = {\n" % FLAGS.map_name)
+        get_hash_map(f)
+        f.write("}\n")
+
+
+if __name__ == "__main__":
+    main(FLAGS(sys.argv))