aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
diff options
context:
space:
mode:
authorGravatar Han-Wen Nienhuys <hanwen@google.com>2015-02-25 16:45:20 +0100
committerGravatar Han-Wen Nienhuys <hanwen@google.com>2015-02-25 16:45:20 +0100
commitd08b27fa9701fecfdb69e1b0d1ac2459efc2129b (patch)
tree5d50963026239ca5aebfb47ea5b8db7e814e57c8 /src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
Update from Google.
-- MOE_MIGRATED_REVID=85702957
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java b/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
new file mode 100644
index 0000000000..9c92b338b2
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
@@ -0,0 +1,193 @@
+// Copyright 2014 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.packages;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.events.StoredEventHandler;
+import com.google.devtools.build.lib.packages.PackageIdentifier.RepositoryName;
+import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.Label.SyntaxException;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * This creates the //external package, where targets not homed in this repository can be bound.
+ */
+public class ExternalPackage extends Package {
+
+ private Map<RepositoryName, Rule> repositoryMap;
+
+ ExternalPackage() {
+ super(PackageIdentifier.createInDefaultRepo("external"));
+ }
+
+ /**
+ * Returns a description of the repository with the given name, or null if there's no such
+ * repository.
+ */
+ public Rule getRepositoryInfo(RepositoryName repositoryName) {
+ return repositoryMap.get(repositoryName);
+ }
+
+ /**
+ * Holder for a binding's actual label and location.
+ */
+ public static class Binding {
+ private final Label actual;
+ private final Location location;
+
+ public Binding(Label actual, Location location) {
+ this.actual = actual;
+ this.location = location;
+ }
+
+ public Label getActual() {
+ return actual;
+ }
+
+ public Location getLocation() {
+ return location;
+ }
+
+ /**
+ * Checks if the label is bound, i.e., starts with //external:.
+ */
+ public static boolean isBoundLabel(Label label) {
+ return label.getPackageName().equals("external");
+ }
+ }
+
+ /**
+ * Given a workspace file path, creates an ExternalPackage.
+ */
+ public static class ExternalPackageBuilder
+ extends AbstractBuilder<ExternalPackage, ExternalPackageBuilder> {
+ private Map<Label, Binding> bindMap = Maps.newHashMap();
+ private Map<RepositoryName, Rule> repositoryMap = Maps.newHashMap();
+
+ public ExternalPackageBuilder(Path workspacePath) {
+ super(new ExternalPackage());
+ setFilename(workspacePath);
+ setMakeEnv(new MakeEnvironment.Builder());
+ }
+
+ @Override
+ protected ExternalPackageBuilder self() {
+ return this;
+ }
+
+ @Override
+ public ExternalPackage build() {
+ pkg.repositoryMap = ImmutableMap.copyOf(repositoryMap);
+ return super.build();
+ }
+
+ public void addBinding(Label label, Binding binding) {
+ bindMap.put(label, binding);
+ }
+
+ public void resolveBindTargets(RuleClass ruleClass)
+ throws EvalException, NoSuchBindingException {
+ for (Entry<Label, Binding> entry : bindMap.entrySet()) {
+ resolveLabel(entry.getKey(), entry.getValue());
+ }
+
+ for (Entry<Label, Binding> entry : bindMap.entrySet()) {
+ try {
+ addRule(ruleClass, entry);
+ } catch (NameConflictException | InvalidRuleException e) {
+ throw new EvalException(entry.getValue().location, e.getMessage());
+ }
+ }
+ }
+
+ // Uses tortoise and the hare algorithm to detect cycles.
+ private void resolveLabel(final Label virtual, Binding binding)
+ throws NoSuchBindingException {
+ Label actual = binding.getActual();
+ Label tortoise = virtual;
+ Label hare = actual;
+ boolean moveTortoise = true;
+ while (Binding.isBoundLabel(actual)) {
+ if (tortoise == hare) {
+ throw new NoSuchBindingException("cycle detected resolving " + virtual + " binding");
+ }
+
+ Label previous = actual; // For the exception.
+ binding = bindMap.get(actual);
+ if (binding == null) {
+ throw new NoSuchBindingException("no binding found for target " + previous + " (via "
+ + virtual + ")");
+ }
+ actual = binding.getActual();
+ hare = actual;
+ moveTortoise = !moveTortoise;
+ if (moveTortoise) {
+ tortoise = bindMap.get(tortoise).getActual();
+ }
+ }
+ bindMap.put(virtual, binding);
+ }
+
+ private void addRule(RuleClass klass, Map.Entry<Label, Binding> bindingEntry)
+ throws InvalidRuleException, NameConflictException {
+ Label virtual = bindingEntry.getKey();
+ Label actual = bindingEntry.getValue().actual;
+ Location location = bindingEntry.getValue().location;
+
+ Map<String, Object> attributes = Maps.newHashMap();
+ // Bound rules don't have a name field, but this works because we don't want more than one
+ // with the same virtual name.
+ attributes.put("name", virtual.getName());
+ attributes.put("actual", actual);
+ StoredEventHandler handler = new StoredEventHandler();
+ Rule rule = RuleFactory.createAndAddRule(this, klass, attributes, handler, null, location);
+ rule.setVisibility(ConstantRuleVisibility.PUBLIC);
+ }
+
+ /**
+ * This is used when a binding is invalid, either because one of the targets is malformed,
+ * refers to a package that does not exist, or creates a circular dependency.
+ */
+ public class NoSuchBindingException extends NoSuchThingException {
+ public NoSuchBindingException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Creates an external repository rule.
+ * @throws SyntaxException if the repository name is invalid.
+ */
+ public ExternalPackageBuilder createAndAddRepositoryRule(RuleClass ruleClass,
+ Map<String, Object> kwargs, FuncallExpression ast)
+ throws InvalidRuleException, NameConflictException, SyntaxException {
+ StoredEventHandler eventHandler = new StoredEventHandler();
+ Rule rule = RuleFactory.createAndAddRule(this, ruleClass, kwargs, eventHandler, ast,
+ ast.getLocation());
+ // Propagate Rule errors to the builder.
+ addEvents(eventHandler.getEvents());
+ repositoryMap.put(RepositoryName.create("@" + rule.getName()), rule);
+ return this;
+ }
+ }
+}