Add git_repository and new_git_repository workspace rules.
TESTED=Added integration tests.
--
MOS_MIGRATED_REVID=98396197
diff --git a/src/main/java/BUILD b/src/main/java/BUILD
index 45b6cf8..a8252ab 100644
--- a/src/main/java/BUILD
+++ b/src/main/java/BUILD
@@ -454,11 +454,13 @@
"//third_party:apache_commons_pool2",
"//third_party:auto_value",
"//third_party:guava",
+ "//third_party:jgit",
"//third_party:joda_time",
"//third_party:jsr305",
"//third_party:jsr330_inject",
"//third_party:maven_model",
"//third_party:protobuf",
+ "//third_party:slf4j",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
index 7e3265b..555f0cb 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
@@ -22,12 +22,15 @@
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.bazel.commands.FetchCommand;
+import com.google.devtools.build.lib.bazel.repository.GitCloneFunction;
+import com.google.devtools.build.lib.bazel.repository.GitRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.HttpArchiveFunction;
import com.google.devtools.build.lib.bazel.repository.HttpDownloadFunction;
import com.google.devtools.build.lib.bazel.repository.HttpJarFunction;
import com.google.devtools.build.lib.bazel.repository.JarFunction;
import com.google.devtools.build.lib.bazel.repository.LocalRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.MavenJarFunction;
+import com.google.devtools.build.lib.bazel.repository.NewGitRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.NewHttpArchiveFunction;
import com.google.devtools.build.lib.bazel.repository.NewLocalRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.RepositoryDelegatorFunction;
@@ -42,10 +45,12 @@
import com.google.devtools.build.lib.bazel.rules.android.AndroidRepositoryRules.AndroidHttpToolsRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.android.AndroidSdkRepositoryFunction;
import com.google.devtools.build.lib.bazel.rules.android.AndroidSdkRepositoryRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.GitRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.HttpArchiveRule;
import com.google.devtools.build.lib.bazel.rules.workspace.HttpJarRule;
import com.google.devtools.build.lib.bazel.rules.workspace.LocalRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.MavenJarRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.NewGitRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.NewHttpArchiveRule;
import com.google.devtools.build.lib.bazel.rules.workspace.NewLocalRepositoryRule;
import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
@@ -75,14 +80,17 @@
private final ImmutableMap<String, RepositoryFunction> repositoryHandlers;
private final AtomicBoolean isFetch = new AtomicBoolean(false);
private HttpDownloadFunction downloadFunction;
+ private GitCloneFunction gitCloneFunction;
public BazelRepositoryModule() {
repositoryHandlers = ImmutableMap.<String, RepositoryFunction>builder()
.put(LocalRepositoryRule.NAME, new LocalRepositoryFunction())
.put(HttpArchiveRule.NAME, new HttpArchiveFunction())
+ .put(GitRepositoryRule.NAME, new GitRepositoryFunction())
.put(HttpJarRule.NAME, new HttpJarFunction())
.put(MavenJarRule.NAME, new MavenJarFunction())
.put(NewHttpArchiveRule.NAME, new NewHttpArchiveFunction())
+ .put(NewGitRepositoryRule.NAME, new NewGitRepositoryFunction())
.put(NewLocalRepositoryRule.NAME, new NewLocalRepositoryFunction())
.put(AndroidSdkRepositoryRule.NAME, new AndroidSdkRepositoryFunction())
.put(AndroidNdkRepositoryRule.NAME, new AndroidNdkRepositoryFunction())
@@ -96,6 +104,7 @@
@Override
public void beforeCommand(BlazeRuntime runtime, Command command) {
downloadFunction.setReporter(runtime.getReporter());
+ gitCloneFunction.setReporter(runtime.getReporter());
}
@Override
@@ -154,6 +163,8 @@
// Helper SkyFunctions.
downloadFunction = new HttpDownloadFunction();
builder.put(SkyFunctionName.create(HttpDownloadFunction.NAME), downloadFunction);
+ gitCloneFunction = new GitCloneFunction();
+ builder.put(SkyFunctionName.create(GitCloneFunction.NAME), gitCloneFunction);
builder.put(JarFunction.NAME, new JarFunction());
builder.put(ZipFunction.NAME, new ZipFunction());
builder.put(TarGzFunction.NAME, new TarGzFunction());
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java
new file mode 100644
index 0000000..2c8e50a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java
@@ -0,0 +1,180 @@
+// Copyright 2015 Google Inc. 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.build.lib.bazel.repository;
+
+import com.google.devtools.build.lib.bazel.repository.RepositoryFunction.RepositoryFunctionException;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.Type;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
+import org.eclipse.jgit.api.errors.InvalidRemoteException;
+import org.eclipse.jgit.api.errors.RefNotFoundException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+
+/**
+ * Clones a Git repository, checks out the provided branch, tag, or commit, and
+ * clones submodules if specified.
+ */
+public class GitCloneFunction implements SkyFunction {
+ public static final String NAME = "GIT_CLONE";
+ private Reporter reporter;
+
+ public void setReporter(Reporter reporter) {
+ this.reporter = reporter;
+ }
+
+ @Nullable
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env) throws RepositoryFunctionException {
+ GitRepositoryDescriptor descriptor = (GitRepositoryDescriptor) skyKey.argument();
+ String outputDirectory = descriptor.directory.toString();
+
+ Git git = null;
+ try {
+ git = Git.cloneRepository()
+ .setURI(descriptor.remote)
+ .setDirectory(new File(outputDirectory))
+ .setCloneSubmodules(false)
+ .setProgressMonitor(
+ new GitProgressMonitor("Cloning " + descriptor.remote, reporter))
+ .call();
+ git.checkout()
+ .setCreateBranch(true)
+ .setName("bazel-checkout")
+ .setStartPoint(descriptor.checkout)
+ .call();
+
+ // Using CloneCommand.setCloneSubmodules() results in SubmoduleInitCommand and
+ // SubmoduleUpdateCommand to be called recursively for all submodules. This is not
+ // desirable for repositories, such as github.com/rust-lang/rust-installer, which
+ // recursively includes itself as a submodule, which would result in an infinite
+ // loop if submodules are cloned recursively. For now, limit submodules to only
+ // the first level.
+ if (descriptor.initSubmodules) {
+ if (!git.submoduleInit().call().isEmpty()) {
+ git.submoduleUpdate()
+ .setProgressMonitor(
+ new GitProgressMonitor("Cloning submodules for " + descriptor.remote, reporter))
+ .call();
+ }
+ }
+ } catch (InvalidRemoteException e) {
+ throw new RepositoryFunctionException(
+ new IOException("Invalid Git repository URI: " + e.getMessage()),
+ Transience.PERSISTENT);
+ } catch (RefNotFoundException|InvalidRefNameException e) {
+ throw new RepositoryFunctionException(
+ new IOException("Invalid branch, tag, or commit: " + e.getMessage()),
+ Transience.PERSISTENT);
+ } catch (GitAPIException e) {
+ throw new RepositoryFunctionException(
+ new IOException(e.getMessage()), Transience.TRANSIENT);
+ } finally {
+ if (git != null) {
+ git.close();
+ }
+ }
+ return new HttpDownloadValue(descriptor.directory);
+ }
+
+ @Nullable
+ @Override
+ public String extractTag(SkyKey skyKey) {
+ return null;
+ }
+
+ public static SkyKey key(Rule rule, Path outputDirectory)
+ throws RepositoryFunctionException {
+ AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
+ if ((mapper.has("commit", Type.STRING) == mapper.has("tag", Type.STRING))
+ && (mapper.get("commit", Type.STRING).isEmpty()
+ == mapper.get("tag", Type.STRING).isEmpty())) {
+ throw new RepositoryFunctionException(
+ new EvalException(rule.getLocation(), "One of either commit or tag must be defined"),
+ Transience.PERSISTENT);
+ }
+ String startingPoint;
+ if (mapper.has("commit", Type.STRING) && !mapper.get("commit", Type.STRING).isEmpty()) {
+ startingPoint = mapper.get("commit", Type.STRING);
+ } else {
+ startingPoint = "tags/" + mapper.get("tag", Type.STRING);
+ }
+
+ return new SkyKey(
+ SkyFunctionName.create(NAME),
+ new GitCloneFunction.GitRepositoryDescriptor(
+ mapper.get("remote", Type.STRING),
+ startingPoint,
+ mapper.get("init_submodules", Type.BOOLEAN),
+ outputDirectory));
+ }
+
+ static final class GitRepositoryDescriptor {
+ private String remote;
+ private String checkout;
+ private boolean initSubmodules;
+ private Path directory;
+
+ public GitRepositoryDescriptor(String remote, String checkout, boolean initSubmodules,
+ Path directory) {
+ this.remote = remote;
+ this.checkout = checkout;
+ this.initSubmodules = initSubmodules;
+ this.directory = directory;
+ }
+
+ @Override
+ public String toString() {
+ return remote + " -> " + directory + " (" + checkout + ") submodules: "
+ + initSubmodules;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || !(obj instanceof GitRepositoryDescriptor)) {
+ return false;
+ }
+ GitRepositoryDescriptor other = (GitRepositoryDescriptor) obj;
+ return Objects.equals(remote, other.remote)
+ && Objects.equals(checkout, other.checkout)
+ && Objects.equals(initSubmodules, other.initSubmodules)
+ && Objects.equals(directory, other.directory);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(remote, checkout, initSubmodules, directory);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/GitProgressMonitor.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitProgressMonitor.java
new file mode 100644
index 0000000..fce6ef1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitProgressMonitor.java
@@ -0,0 +1,72 @@
+// Copyright 2015 Google Inc. 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.build.lib.bazel.repository;
+
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.Reporter;
+
+import org.eclipse.jgit.lib.ProgressMonitor;
+
+/**
+ * ProgressMonitor for reporting progress for Git repository rules.
+ */
+class GitProgressMonitor implements ProgressMonitor {
+ private String message;
+ private Reporter reporter;
+ private int totalTasks;
+ private int currentTask;
+
+ private String workTitle;
+ private int totalWork;
+ private int completedWork;
+
+ GitProgressMonitor(String message, Reporter reporter) {
+ this.message = message;
+ this.reporter = reporter;
+ }
+
+ public void start(int totalTasks) {
+ this.totalTasks = totalTasks;
+ this.currentTask = 0;
+ }
+
+ private void report() {
+ reporter.handle(
+ Event.progress("[" + currentTask + " / " + totalTasks + "] "
+ + message + ": " + workTitle + " ("
+ + completedWork + " / " + totalWork + ")"));
+ }
+
+ public void beginTask(String title, int totalWork) {
+ ++currentTask;
+ // TODO(dzc): Remove this when jgit reports totalTasks correctly in start().
+ if (currentTask > totalTasks) {
+ totalTasks = currentTask;
+ }
+ this.totalWork = totalWork;
+ this.completedWork = 0;
+ this.workTitle = title;
+ report();
+ }
+
+ public boolean isCancelled() { return false; }
+
+ public void update(int completed) {
+ completedWork += completed;
+ report();
+ }
+
+ public void endTask() { }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/GitRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitRepositoryFunction.java
new file mode 100644
index 0000000..789e76c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitRepositoryFunction.java
@@ -0,0 +1,84 @@
+// Copyright 2015 Google Inc. 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.build.lib.bazel.repository;
+
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.bazel.rules.workspace.GitRepositoryRule;
+import com.google.devtools.build.lib.packages.PackageIdentifier.RepositoryName;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.skyframe.FileValue;
+import com.google.devtools.build.lib.skyframe.RepositoryValue;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.skyframe.SkyFunctionException;
+import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+
+import java.io.IOException;
+
+/**
+ * Clones a Git repository.
+ */
+public class GitRepositoryFunction extends RepositoryFunction {
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException {
+ RepositoryName repositoryName = (RepositoryName) skyKey.argument();
+ Rule rule = RepositoryFunction.getRule(repositoryName, GitRepositoryRule.NAME, env);
+ if (rule == null) {
+ return null;
+ }
+
+ Path outputDirectory = getExternalRepositoryDirectory().getRelative(rule.getName());
+ FileValue directoryValue = createDirectory(outputDirectory, env, rule);
+ if (directoryValue == null) {
+ return null;
+ }
+
+ try {
+ HttpDownloadValue value = (HttpDownloadValue) env.getValueOrThrow(
+ GitCloneFunction.key(rule, outputDirectory), IOException.class);
+ if (value == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(e, Transience.TRANSIENT);
+ }
+
+ return RepositoryValue.create(directoryValue);
+ }
+
+ protected FileValue createDirectory(Path path, Environment env, Rule rule)
+ throws RepositoryFunctionException {
+ try {
+ FileSystemUtils.createDirectoryAndParents(path);
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(new IOException("Could not create directory for "
+ + rule.getName() + ": " + e.getMessage()), Transience.TRANSIENT);
+ }
+ return getRepositoryDirectory(path, env);
+ }
+
+ @Override
+ public SkyFunctionName getSkyFunctionName() {
+ return SkyFunctionName.create(GitRepositoryRule.NAME.toUpperCase());
+ }
+
+ @Override
+ public Class<? extends RuleDefinition> getRuleDefinition() {
+ return GitRepositoryRule.class;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/NewGitRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/NewGitRepositoryFunction.java
new file mode 100644
index 0000000..5858332
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/NewGitRepositoryFunction.java
@@ -0,0 +1,66 @@
+// Copyright 2015 Google Inc. 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.build.lib.bazel.repository;
+
+import com.google.devtools.build.lib.bazel.rules.workspace.NewGitRepositoryRule;
+import com.google.devtools.build.lib.packages.PackageIdentifier.RepositoryName;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.skyframe.FileValue;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.skyframe.SkyFunctionException;
+import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+
+import java.io.IOException;
+
+/**
+ * Clones a Git repository, creates a WORKSPACE file, and adds a BUILD file for it.
+ */
+public class NewGitRepositoryFunction extends GitRepositoryFunction {
+ @Override
+ public SkyFunctionName getSkyFunctionName() {
+ return SkyFunctionName.create(NewGitRepositoryRule.NAME.toUpperCase());
+ }
+
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException {
+ RepositoryName repositoryName = (RepositoryName) skyKey.argument();
+ Rule rule = RepositoryFunction.getRule(repositoryName, NewGitRepositoryRule.NAME, env);
+ if (rule == null) {
+ return null;
+ }
+
+ Path outputDirectory = getExternalRepositoryDirectory().getRelative(rule.getName());
+ FileValue directoryValue = createDirectory(outputDirectory, env, rule);
+ if (directoryValue == null) {
+ return null;
+ }
+
+ try {
+ HttpDownloadValue value = (HttpDownloadValue) env.getValueOrThrow(
+ GitCloneFunction.key(rule, outputDirectory), IOException.class);
+ if (value == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(e, Transience.TRANSIENT);
+ }
+
+ createWorkspaceFile(outputDirectory, rule);
+ return symlinkBuildFile(rule, getWorkspace(), directoryValue, env);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index 8095976..d7ec9ff 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -60,10 +60,12 @@
import com.google.devtools.build.lib.bazel.rules.sh.BazelShLibraryRule;
import com.google.devtools.build.lib.bazel.rules.sh.BazelShRuleClasses;
import com.google.devtools.build.lib.bazel.rules.sh.BazelShTestRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.GitRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.HttpArchiveRule;
import com.google.devtools.build.lib.bazel.rules.workspace.HttpJarRule;
import com.google.devtools.build.lib.bazel.rules.workspace.LocalRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.MavenJarRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.NewGitRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.NewHttpArchiveRule;
import com.google.devtools.build.lib.bazel.rules.workspace.NewLocalRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.WorkspaceBaseRule;
@@ -338,11 +340,13 @@
builder.addRuleDefinition(new BazelActionListenerRule());
builder.addRuleDefinition(new BindRule());
+ builder.addRuleDefinition(new GitRepositoryRule());
builder.addRuleDefinition(new HttpArchiveRule());
builder.addRuleDefinition(new HttpJarRule());
builder.addRuleDefinition(new LocalRepositoryRule());
builder.addRuleDefinition(new MavenJarRule());
builder.addRuleDefinition(new NewHttpArchiveRule());
+ builder.addRuleDefinition(new NewGitRepositoryRule());
builder.addRuleDefinition(new NewLocalRepositoryRule());
builder.addRuleDefinition(new AndroidSdkRepositoryRule());
builder.addRuleDefinition(new AndroidNdkRepositoryRule());
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/GitRepositoryRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/GitRepositoryRule.java
new file mode 100644
index 0000000..5b5756e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/GitRepositoryRule.java
@@ -0,0 +1,126 @@
+// Copyright 2015 Google Inc. 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.build.lib.bazel.rules.workspace;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.Type.BOOLEAN;
+import static com.google.devtools.build.lib.packages.Type.STRING;
+
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.packages.RuleClass;
+
+/**
+ * Rule definition for the git_repository rule.
+ */
+public class GitRepositoryRule implements RuleDefinition {
+ public static final String NAME = "git_repository";
+
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
+ return builder
+ /* <!-- #BLAZE_RULE(git_repository).ATTRIBUTE(remote) -->
+ The URI of the remote Git repository.
+ ${SYNOPSIS}
+
+ <p>This must be a HTTP URL. There is currently no support for authentication.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("remote", STRING).mandatory())
+ /* <!-- #BLAZE_RULE(git_repository).ATTRIBUTE(commit) -->
+ The commit hash to check out in the repository.
+ ${SYNOPSIS}
+
+ <p>Note that one of either <code>commit</code> or <code>tag</code> must be defined.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("commit", STRING))
+ /* <!-- #BLAZE_RULE(git_repository).ATTRIBUTE(tag) -->
+ The Git tag to check out in the repository.
+ ${SYNOPSIS}
+
+ <p>Note that one of either <code>commit</code> or <code>tag</code> must be defined.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("tag", STRING))
+ /* <!-- #BLAZE_RULE(git_repository).ATTRIBUTE(init_submodules) -->
+ Whether to clone submodules in the repository.
+ ${SYNOPSIS}
+
+ <p>Currently, only cloning the top-level submodules is supported</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("init_submodules", BOOLEAN).value(false))
+ .setWorkspaceOnly()
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name(GitRepositoryRule.NAME)
+ .type(RuleClass.Builder.RuleClassType.WORKSPACE)
+ .ancestors(WorkspaceBaseRule.class)
+ .factoryClass(WorkspaceConfiguredTargetFactory.class)
+ .build();
+ }
+}
+
+/*<!-- #BLAZE_RULE (NAME = git_repository, TYPE = OTHER, FAMILY = Workspace)[GENERIC_RULE] -->
+
+${ATTRIBUTE_SIGNATURE}
+
+<p>Clones a Git repository, checks out the specified tag, or commit, and makes its targets
+available for binding.</p>
+
+${ATTRIBUTE_DEFINITION}
+
+<h4 id="git_repository_examples">Examples</h4>
+
+<p>Suppose the current repository contains the source code for a chat program, rooted at the
+ directory <i>~/chat-app</i>. It needs to depend on an SSL library which is available in the
+ remote Git repository <i>http://example.com/openssl/openssl.git</i>. The chat app depends
+ on version 1.0.2 of the SSL library, which is tagged by the v1.0.2 Git tag.<p>
+
+<p>This Git repository contains the following directory structure:</p>
+
+<pre class="code">
+WORKSPACE
+src/
+ BUILD
+ openssl.cc
+ openssl.h
+</pre>
+
+<p><i>src/BUILD</i> contains the following target definition:</p>
+
+<pre class="code">
+cc_library(
+ name = "openssl-lib",
+ srcs = ["openssl.cc"],
+ hdrs = ["openssl.h"],
+)
+</pre>
+
+<p>Targets in the <i>~/chat-app</i> repository can depend on this target if the following lines are
+ added to <i>~/chat-app/WORKSPACE</i>:</p>
+
+<pre class="code">
+git_repository(
+ name = "my-ssl",
+ remote = "http://example.com/openssl/openssl.git",
+ tag = "v1.0.2",
+)
+</pre>
+
+<p>Then targets would specify <code>@my-ssl//src:openssl-lib</code> as a dependency.</p>
+
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/NewGitRepositoryRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/NewGitRepositoryRule.java
new file mode 100644
index 0000000..2a57a9b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/NewGitRepositoryRule.java
@@ -0,0 +1,135 @@
+// Copyright 2015 Google Inc. 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.build.lib.bazel.rules.workspace;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.Type.BOOLEAN;
+import static com.google.devtools.build.lib.packages.Type.STRING;
+
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.packages.RuleClass;
+
+/**
+ * Rule definition for the new_git_repository rule.
+ */
+public class NewGitRepositoryRule implements RuleDefinition {
+ public static final String NAME = "new_git_repository";
+
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
+ return builder
+ /* <!-- #BLAZE_RULE(new_git_repository).ATTRIBUTE(remote) -->
+ The URI of the remote Git repository.
+ ${SYNOPSIS}
+
+ <p>This must be a HTTP URL. There is currently no support for authentication.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("remote", STRING).mandatory())
+ /* <!-- #BLAZE_RULE(git_repository).ATTRIBUTE(commit) -->
+ The commit hash to check out in the repository.
+ ${SYNOPSIS}
+
+ <p>Note that one of either <code>commit</code> or <code>tag</code> must be defined.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("commit", STRING))
+ /* <!-- #BLAZE_RULE(git_repository).ATTRIBUTE(tag) -->
+ The Git tag to check out in the repository.
+ ${SYNOPSIS}
+
+ <p>Note that one of either <code>commit</code> or <code>tag</code> must be defined.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("tag", STRING))
+ /* <!-- #BLAZE_RULE(new_git_repository).ATTRIBUTE(build_file) -->
+ A file to use as a BUILD file for this directory.
+ ${SYNOPSIS}
+
+ <p>This path is relative to the build's workspace. The file does not need to be named
+ BUILD, but can be (something like BUILD.new-repo-name may work well for distinguishing it
+ from the repository's actual BUILD files.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("build_file", STRING).mandatory())
+ /* <!-- #BLAZE_RULE(new_git_repository).ATTRIBUTE(init_submodules) -->
+ Whether to clone submodules in the repository.
+ ${SYNOPSIS}
+
+ <p>Currently, only cloning the top-level submodules is supported</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("init_submodules", BOOLEAN).value(false))
+ .setWorkspaceOnly()
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name(NewGitRepositoryRule.NAME)
+ .type(RuleClass.Builder.RuleClassType.WORKSPACE)
+ .ancestors(WorkspaceBaseRule.class)
+ .factoryClass(WorkspaceConfiguredTargetFactory.class)
+ .build();
+ }
+}
+
+/*<!-- #BLAZE_RULE (NAME = new_git_repository, TYPE = OTHER, FAMILY = Workspace)[GENERIC_RULE] -->
+
+${ATTRIBUTE_SIGNATURE}
+
+<p>Clones a Git repository, checks out the specified tag, or commit, and makes its targets
+available for binding.</p>
+
+${ATTRIBUTE_DEFINITION}
+
+<h4 id="git_repository_examples">Examples</h4>
+
+<p>Suppose the current repository contains the source code for a chat program, rooted at the
+ directory <i>~/chat-app</i>. It needs to depend on an SSL library which is available in the
+ remote Git repository <i>http://example.com/openssl/openssl.git</i>. The chat app depends
+ on version 1.0.2 of the SSL library, which is tagged by the v1.0.2 Git tag.<p>
+
+<p>This Git repository contains the following directory structure:</p>
+
+<pre class="code">
+src/
+ openssl.cc
+ openssl.h
+</pre>
+
+<p>In the local repository, the user creates a <i>ssl.BUILD</i> file which contains the following
+target definition:</p>
+
+<pre class="code">
+cc_library(
+ name = "openssl-lib",
+ srcs = ["openssl.cc"],
+ hdrs = ["openssl.h"],
+)
+</pre>
+
+<p>Targets in the <i>~/chat-app</i> repository can depend on this target if the following lines are
+ added to <i>~/chat-app/WORKSPACE</i>:</p>
+
+<pre class="code">
+new_git_repository(
+ name = "my-ssl",
+ remote = "http://example.com/openssl/openssl.git",
+ tag = "v1.0.2",
+ build_file = "ssl.BUILD",
+)
+</pre>
+
+<p>Then targets would specify <code>@my-ssl//src:openssl-lib</code> as a dependency.</p>
+
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index 3880ce0..58a37d8 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -95,6 +95,16 @@
)
sh_test(
+ name = "git_repository_test",
+ size = "medium",
+ srcs = ["git_repository_test.sh"],
+ data = [
+ ":test-deps",
+ "//src/test/shell/bazel/testdata:git-repos",
+ ],
+)
+
+sh_test(
name = "local_repository_test",
size = "medium",
srcs = ["local_repository_test.sh"],
diff --git a/src/test/shell/bazel/git_repository_test.sh b/src/test/shell/bazel/git_repository_test.sh
new file mode 100755
index 0000000..580808c
--- /dev/null
+++ b/src/test/shell/bazel/git_repository_test.sh
@@ -0,0 +1,313 @@
+#!/bin/bash
+#
+# Copyright 2015 Google Inc. 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.
+#
+# Test git_repository and new_git_repository workspace rules.
+#
+
+# Load test environment
+source $(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test-setup.sh \
+ || { echo "test-setup.sh not found!" >&2; exit 1; }
+
+# Global test setup.
+#
+# Unpacks the test Git repositories in the test temporary directory.
+function set_up() {
+ bazel clean --expunge
+ local repos_dir=$TEST_TMPDIR/repos
+ if [ -e "$repos_dir" ]; then
+ rm -rf $repos_dir
+ fi
+
+ mkdir -p $repos_dir
+ cp $testdata_path/pluto-repo.tar.gz $repos_dir
+ cp $testdata_path/outer-planets-repo.tar.gz $repos_dir
+ cd $repos_dir
+ tar zxvf pluto-repo.tar.gz
+ tar zxvf outer-planets-repo.tar.gz
+}
+
+# Test cloning a Git repository using the git_repository rule.
+#
+# This test uses the pluto Git repository at tag 1-build, which contains the
+# following files:
+#
+# pluto/
+# WORKSPACE
+# BUILD
+# info
+#
+# Then, set up workspace with the following files:
+#
+# $WORKSPACE_DIR/
+# WORKSPACE
+# planets/
+# BUILD
+# planet_info.sh
+#
+# //planets has a dependency on a target in the pluto Git repository.
+function test_git_repository() {
+ local pluto_repo_dir=$TEST_TMPDIR/repos/pluto
+ # Commit 85b8224 corresponds to tag 1-build. See testdata/pluto.git_log.
+ local commit_hash="b87de93"
+
+ # Create a workspace that clones the repository at the first commit.
+ cd $WORKSPACE_DIR
+ cat > WORKSPACE <<EOF
+git_repository(
+ name = "pluto",
+ remote = "$pluto_repo_dir",
+ commit = "$commit_hash",
+)
+EOF
+ mkdir -p planets
+ cat > planets/BUILD <<EOF
+sh_binary(
+ name = "planet-info",
+ srcs = ["planet_info.sh"],
+ data = ["@pluto//:pluto"],
+)
+EOF
+
+ cat > planets/planet_info.sh <<EOF
+#!/bin/bash
+cat external/pluto/info
+EOF
+ chmod +x planets/planet_info.sh
+
+ bazel run //planets:planet-info >& $TEST_log \
+ || echo "Expected build/run to succeed"
+ expect_log "Pluto is a dwarf planet"
+}
+
+# Test cloning a Git repository using the new_git_repository rule.
+#
+# This test uses the pluto Git repository at tag 0-initial, which contains the
+# following files:
+#
+# pluto/
+# info
+#
+# Set up workspace with the following files:
+#
+# $WORKSPACE_DIR/
+# WORKSPACE
+# pluto.BUILD
+# planets/
+# BUILD
+# planet_info.sh
+#
+# //planets has a dependency on a target in the $TEST_TMPDIR/pluto Git
+# repository.
+function test_new_git_repository() {
+ local pluto_repo_dir=$TEST_TMPDIR/repos/pluto
+
+ # Create a workspace that clones the repository at the first commit.
+ cd $WORKSPACE_DIR
+ cat > WORKSPACE <<EOF
+new_git_repository(
+ name = "pluto",
+ remote = "$pluto_repo_dir",
+ tag = "0-initial",
+ build_file = "pluto.BUILD",
+)
+EOF
+
+ cat > pluto.BUILD <<EOF
+filegroup(
+ name = "pluto",
+ srcs = ["info"],
+ visibility = ["//visibility:public"],
+)
+EOF
+
+ mkdir -p planets
+ cat > planets/BUILD <<EOF
+sh_binary(
+ name = "planet-info",
+ srcs = ["planet_info.sh"],
+ data = ["@pluto//:pluto"],
+)
+EOF
+
+ cat > planets/planet_info.sh <<EOF
+#!/bin/bash
+cat external/pluto/info
+EOF
+ chmod +x planets/planet_info.sh
+
+ bazel run //planets:planet-info >& $TEST_log \
+ || echo "Expected build/run to succeed"
+ expect_log "Pluto is a planet"
+}
+
+# Test cloning a Git repository that has a submodule using the
+# new_git_repository rule.
+#
+# This test uses the outer-planets Git repository at revision 1-submodule, which
+# contains the following files:
+#
+# outer_planets/
+# neptune/
+# info
+# pluto/ --> submodule ../pluto
+# info
+#
+# Set up workspace with the following files:
+#
+# $WORKSPACE_DIR/
+# WORKSPACE
+# outer_planets.BUILD
+# planets/
+# BUILD
+# planet_info.sh
+#
+# planets has a dependency on targets in the $TEST_TMPDIR/outer_planets Git
+# repository.
+function test_new_git_repository_submodules() {
+ local outer_planets_repo_dir=$TEST_TMPDIR/repos/outer-planets
+
+ # Create a workspace that clones the outer_planets repository.
+ cd $WORKSPACE_DIR
+ cat > WORKSPACE <<EOF
+new_git_repository(
+ name = "outer-planets",
+ remote = "$outer_planets_repo_dir",
+ tag = "1-submodule",
+ init_submodules = 1,
+ build_file = "outer_planets.BUILD",
+)
+EOF
+
+ cat > outer_planets.BUILD <<EOF
+filegroup(
+ name = "neptune",
+ srcs = ["neptune/info"],
+ visibility = ["//visibility:public"],
+)
+
+filegroup(
+ name = "pluto",
+ srcs = ["pluto/info"],
+ visibility = ["//visibility:public"],
+)
+EOF
+
+ mkdir -p planets
+ cat > planets/BUILD <<EOF
+sh_binary(
+ name = "planet-info",
+ srcs = ["planet_info.sh"],
+ data = [
+ "@outer-planets//:neptune",
+ "@outer-planets//:pluto",
+ ],
+)
+EOF
+
+ cat > planets/planet_info.sh <<EOF
+#!/bin/bash
+cat external/outer-planets/neptune/info
+cat external/outer-planets/pluto/info
+EOF
+ chmod +x planets/planet_info.sh
+
+ bazel run //planets:planet-info >& $TEST_log \
+ || echo "Expected build/run to succeed"
+ expect_log "Neptune is a planet"
+ expect_log "Pluto is a planet"
+}
+
+# Helper function for setting up the workspace as follows
+#
+# $WORKSPACE_DIR/
+# WORKSPACE
+# planets/
+# planet_info.sh
+# BUILD
+function setup_error_test() {
+ cd $WORKSPACE_DIR
+ mkdir -p planets
+ cat > planets/planet_info.sh <<EOF
+#!/bin/bash
+cat external/pluto/info
+EOF
+
+ cat > planets/BUILD <<EOF
+sh_binary(
+ name = "planet-info",
+ srcs = ["planet_info.sh"],
+ data = ["@pluto//:pluto"],
+)
+EOF
+}
+
+# Verifies that rule fails if both tag and commit are set.
+#
+# This test uses the pluto Git repository at tag 1-build, which contains the
+# following files:
+#
+# pluto/
+# WORKSPACE
+# BUILD
+# info
+function test_git_repository_both_commit_tag_error() {
+ setup_error_test
+ local pluto_repo_dir=$TEST_TMPDIR/pluto
+ # Commit 85b8224 corresponds to tag 1-build. See testdata/pluto.git_log.
+ local commit_hash="b87de93"
+
+ cd $WORKSPACE_DIR
+ cat > WORKSPACE <<EOF
+git_repository(
+ name = "pluto",
+ remote = "$pluto_repo_dir",
+ tag = "1-build",
+ commit = "$commit_hash",
+)
+EOF
+
+ bazel fetch //planets:planet-info >& $TEST_log \
+ || echo "Expect run to fail."
+ expect_log "One of either commit or tag must be defined"
+}
+
+# Verifies that rule fails if neither tag or commit are set.
+#
+# This test uses the pluto Git repository at tag 1-build, which contains the
+# following files:
+#
+# pluto/
+# WORKSPACE
+# BUILD
+# info
+function test_git_repository_no_commit_tag_error() {
+ setup_error_test
+ local pluto_repo_dir=$TEST_TMPDIR/pluto
+
+ cd $WORKSPACE_DIR
+ cat > WORKSPACE <<EOF
+git_repository(
+ name = "pluto",
+ remote = "$pluto_repo_dir",
+)
+EOF
+
+ bazel fetch //planets:planet-info >& $TEST_log \
+ || echo "Expect run to fail."
+ expect_log "One of either commit or tag must be defined"
+}
+
+run_suite "git_repository tests"
diff --git a/src/test/shell/bazel/testdata/BUILD b/src/test/shell/bazel/testdata/BUILD
new file mode 100644
index 0000000..291d17a
--- /dev/null
+++ b/src/test/shell/bazel/testdata/BUILD
@@ -0,0 +1,9 @@
+filegroup(
+ name = "git-repos",
+ testonly = 1,
+ srcs = [
+ "outer-planets-repo.tar.gz",
+ "pluto-repo.tar.gz",
+ ],
+ visibility = ["//src/test/shell/bazel:__pkg__"],
+)
diff --git a/src/test/shell/bazel/testdata/outer-planets-repo.tar.gz b/src/test/shell/bazel/testdata/outer-planets-repo.tar.gz
new file mode 100644
index 0000000..254a638
--- /dev/null
+++ b/src/test/shell/bazel/testdata/outer-planets-repo.tar.gz
Binary files differ
diff --git a/src/test/shell/bazel/testdata/outer-planets.git_log b/src/test/shell/bazel/testdata/outer-planets.git_log
new file mode 100644
index 0000000..fc26500
--- /dev/null
+++ b/src/test/shell/bazel/testdata/outer-planets.git_log
@@ -0,0 +1,36 @@
+commit c6ffd1361759036836186f06078af8ebc297bf39 (HEAD -> master, tag: 1-submodule)
+Author: John Doe <john@foo.com>
+Date: Thu Jul 16 04:53:18 2015 -0700
+
+ Add pluto submodule.
+
+diff --git a/.gitmodules b/.gitmodules
+new file mode 100644
+index 0000000..1d2f9b1
+--- /dev/null
++++ b/.gitmodules
+@@ -0,0 +1,3 @@
++[submodule "pluto"]
++ path = pluto
++ url = ../pluto
+diff --git a/pluto b/pluto
+new file mode 160000
+index 0000000..36db6be
+--- /dev/null
++++ b/pluto
+@@ -0,0 +1 @@
++Subproject commit 36db6be50fd9f33cf89fc5b8206f2e3714b520e3
+
+commit eaf1b34982ead69228b6f3f894a0a34c59c07f17 (tag: 0-initial)
+Author: John Doe <john@foo.com>
+Date: Thu Jul 16 04:52:23 2015 -0700
+
+ Initial commit.
+
+diff --git a/neptune/info b/neptune/info
+new file mode 100644
+index 0000000..41cf7b5
+--- /dev/null
++++ b/neptune/info
+@@ -0,0 +1 @@
++Neptune is a planet
diff --git a/src/test/shell/bazel/testdata/pluto-repo.tar.gz b/src/test/shell/bazel/testdata/pluto-repo.tar.gz
new file mode 100644
index 0000000..a347a37
--- /dev/null
+++ b/src/test/shell/bazel/testdata/pluto-repo.tar.gz
Binary files differ
diff --git a/src/test/shell/bazel/testdata/pluto.git_log b/src/test/shell/bazel/testdata/pluto.git_log
new file mode 100644
index 0000000..fb98d77
--- /dev/null
+++ b/src/test/shell/bazel/testdata/pluto.git_log
@@ -0,0 +1,41 @@
+commit b87de9346bb1a4a3d4d2ab1a567b07c5d11a486a (HEAD -> master, tag: 1-build)
+Author: John Doe <john@foo.com>
+Date: Thu Jul 16 04:50:53 2015 -0700
+
+ Add WORKSPACE and BUILD file. Update info because Pluto is no longer a planet.
+
+diff --git a/BUILD b/BUILD
+new file mode 100644
+index 0000000..874e03f
+--- /dev/null
++++ b/BUILD
+@@ -0,0 +1,5 @@
++filegroup(
++ name = "pluto",
++ srcs = ["info"],
++ visibility = ["//visibility:public"],
++)
+diff --git a/WORKSPACE b/WORKSPACE
+new file mode 100644
+index 0000000..e69de29
+diff --git a/info b/info
+index 8f442be..36cbae4 100644
+--- a/info
++++ b/info
+@@ -1 +1 @@
+-Pluto is a planet
++Pluto is a dwarf planet
+
+commit 36db6be50fd9f33cf89fc5b8206f2e3714b520e3 (tag: 0-initial)
+Author: John Doe <john@foo.com>
+Date: Thu Jul 16 04:49:35 2015 -0700
+
+ Initial commit.
+
+diff --git a/info b/info
+new file mode 100644
+index 0000000..8f442be
+--- /dev/null
++++ b/info
+@@ -0,0 +1 @@
++Pluto is a planet
diff --git a/src/test/shell/bazel/testenv.sh b/src/test/shell/bazel/testenv.sh
index 44d15f3..dc0a5c3 100755
--- a/src/test/shell/bazel/testenv.sh
+++ b/src/test/shell/bazel/testenv.sh
@@ -44,6 +44,9 @@
genclass_path="${TEST_SRCDIR}/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClass_deploy.jar"
ijar_path="${TEST_SRCDIR}/third_party/ijar/ijar"
+# Test data
+testdata_path=${TEST_SRCDIR}/src/test/shell/bazel/testdata
+
# Third-party
PLATFORM="$(uname -s | tr 'A-Z' 'a-z')"
MACHINE_TYPE="$(uname -m)"
diff --git a/third_party/BUILD b/third_party/BUILD
index 91135d1..1d47272 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -217,6 +217,11 @@
)
java_import(
+ name = "jgit",
+ jars = ["jgit/org.eclipse.jgit-4.0.1.201506240215-r.jar"],
+)
+
+java_import(
name = "joda_time",
jars = ["joda_time/joda-time-2.3.jar"],
)
@@ -274,6 +279,14 @@
)
java_import(
+ name = "slf4j",
+ jars = [
+ "slf4j/slf4j-api-1.7.7.jar",
+ "slf4j/slf4j-jdk14-1.7.7.jar",
+ ],
+)
+
+java_import(
name = "tomcat_annotations_api",
jars = ["tomcat_annotations_api/tomcat-annotations-api-8.0.5.jar"],
)
diff --git a/third_party/README.md b/third_party/README.md
index 65f3941..e5ce426 100644
--- a/third_party/README.md
+++ b/third_party/README.md
@@ -154,6 +154,13 @@
* License: Apache License 2.0
+[jgit](https://eclipse.org/jgit/)
+------
+
+* Version: 4.0.1.201506240215-r
+* License: Eclipse Distribution License 1.0
+
+
[joda_time](http://www.joda.org/joda-time/)
-----------
@@ -175,6 +182,7 @@
[jsr330_inject](https://code.google.com/p/atinject/)
+---------------
* Version: 1
* License: Apache License 2.0
@@ -213,6 +221,13 @@
* License: Apache License 2.0
+[slf4j](http://www.slf4j.org/)
+-------
+
+* Version: 1.7.7
+* License: MIT license
+
+
Testing
=======