aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.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/AggregatingAttributeMapper.java
Update from Google.
-- MOE_MIGRATED_REVID=85702957
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java
new file mode 100644
index 0000000000..2aef2242bb
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java
@@ -0,0 +1,218 @@
+// 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.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.syntax.Label;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/**
+ * {@link AttributeMap} implementation that provides the ability to retrieve *all possible*
+ * values an attribute might take.
+ */
+public class AggregatingAttributeMapper extends AbstractAttributeMapper {
+
+ /**
+ * Store for all of this rule's attributes that are non-configurable. These are
+ * unconditionally available to computed defaults no matter what dependencies
+ * they've declared.
+ */
+ private final List<String> nonconfigurableAttributes;
+
+ private AggregatingAttributeMapper(Rule rule) {
+ super(rule.getPackage(), rule.getRuleClassObject(), rule.getLabel(),
+ rule.getAttributeContainer());
+
+ ImmutableList.Builder<String> nonconfigurableAttributesBuilder = ImmutableList.builder();
+ for (Attribute attr : rule.getAttributes()) {
+ if (!attr.isConfigurable()) {
+ nonconfigurableAttributesBuilder.add(attr.getName());
+ }
+ }
+ nonconfigurableAttributes = nonconfigurableAttributesBuilder.build();
+ }
+
+ public static AggregatingAttributeMapper of(Rule rule) {
+ return new AggregatingAttributeMapper(rule);
+ }
+
+ /**
+ * Override that also visits the rule's configurable attribute keys (which are
+ * themselves labels).
+ */
+ @Override
+ public void visitLabels(AcceptsLabelAttribute observer) {
+ super.visitLabels(observer);
+ for (String attrName : getAttributeNames()) {
+ Attribute attribute = getAttributeDefinition(attrName);
+ Type.Selector<?> selector = getSelector(attrName, attribute.getType());
+ if (selector != null) {
+ for (Label configLabel : selector.getEntries().keySet()) {
+ if (!Type.Selector.isReservedLabel(configLabel)) {
+ observer.acceptLabelAttribute(configLabel, attribute);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a list of all possible values an attribute can take for this rule.
+ */
+ @Override
+ public <T> Iterable<T> visitAttribute(String attributeName, Type<T> type) {
+ // If this attribute value is configurable, visit all possible values.
+ Type.Selector<T> selector = getSelector(attributeName, type);
+ if (selector != null) {
+ ImmutableList.Builder<T> builder = ImmutableList.builder();
+ for (Map.Entry<Label, T> entry : selector.getEntries().entrySet()) {
+ builder.add(entry.getValue());
+ }
+ return builder.build();
+ }
+
+ // If this attribute is a computed default, feed it all possible value combinations of
+ // its declared dependencies and return all computed results. For example, if this default
+ // uses attributes x and y, x can configurably be x1 or x2, and y can configurably be y1
+ // or y1, then compute default values for the (x1,y1), (x1,y2), (x2,y1), and (x2,y2) cases.
+ Attribute.ComputedDefault computedDefault = getComputedDefault(attributeName, type);
+ if (computedDefault != null) {
+ // This will hold every (value1, value2, ..) combination of the declared dependencies.
+ List<Map<String, Object>> depMaps = new LinkedList<>();
+ // Collect those combinations.
+ mapDepsForComputedDefault(computedDefault.dependencies(), depMaps,
+ ImmutableMap.<String, Object>of());
+ List<T> possibleValues = new ArrayList<>(); // Not ImmutableList.Builder: values may be null.
+ // For each combination, call getDefault on a specialized AttributeMap providing those values.
+ for (Map<String, Object> depMap : depMaps) {
+ possibleValues.add(type.cast(computedDefault.getDefault(mapBackedAttributeMap(depMap))));
+ }
+ return possibleValues;
+ }
+
+ // For any other attribute, just return its direct value.
+ T value = get(attributeName, type);
+ return value == null ? ImmutableList.<T>of() : ImmutableList.of(value);
+ }
+
+ /**
+ * Given (possibly configurable) attributes that a computed default depends on, creates an
+ * {attrName -> attrValue} map for every possible combination of those attribute values and
+ * returns a list of all the maps. This defines the complete dependency space that can affect
+ * the computed default's values.
+ *
+ * <p>For example, given dependencies x and y, which might respectively have values x1, x2 and
+ * y1, y2, this returns:
+ * <pre>
+ * [
+ * {x: x1, y: y1},
+ * {x: x1, y: y2},
+ * {x: x2, y: y1},
+ * {x: x2, y: y2}
+ * ]
+ * </pre>
+ *
+ * @param depAttributes the names of the attributes this computed default depends on
+ * @param mappings the list of {attrName --> attrValue} maps defining the computed default's
+ * dependency space. This is where this method's results are written.
+ * @param currentMap a (possibly non-empty) map to add {attrName --> attrValue}
+ * entries to. Outside callers can just pass in an empty map.
+ */
+ private void mapDepsForComputedDefault(List<String> depAttributes,
+ List<Map<String, Object>> mappings, Map<String, Object> currentMap) {
+ // Because this method uses exponential time/space on the number of inputs, keep the
+ // maximum number of inputs conservatively small.
+ Preconditions.checkState(depAttributes.size() <= 2);
+
+ if (depAttributes.isEmpty()) {
+ // Recursive base case: store whatever's already been populated in currentMap.
+ mappings.add(currentMap);
+ return;
+ }
+
+ // Take the first attribute in the dependency list and iterate over all its values. For each
+ // value x, copy currentMap with the additional entry { firstAttrName: x }, then feed
+ // this recursively into a subcall over all remaining dependencies. This recursively
+ // continues until we run out of values.
+ String firstAttribute = depAttributes.get(0);
+ for (Object value : visitAttribute(firstAttribute, getAttributeType(firstAttribute))) {
+ Map<String, Object> newMap = new HashMap<>();
+ newMap.putAll(currentMap);
+ newMap.put(firstAttribute, value);
+ mapDepsForComputedDefault(depAttributes.subList(1, depAttributes.size()), mappings, newMap);
+ }
+ }
+
+ /**
+ * A custom {@link AttributeMap} that reads attribute values from the given Map. All
+ * non-configurable attributes are also readable. Any attempt to read an attribute
+ * that's not in one of these two cases triggers an IllegalArgumentException.
+ */
+ private AttributeMap mapBackedAttributeMap(final Map<String, Object> directMap) {
+ final AggregatingAttributeMapper owner = AggregatingAttributeMapper.this;
+ return new AttributeMap() {
+
+ @Override
+ public <T> T get(String attributeName, Type<T> type) {
+ owner.checkType(attributeName, type);
+ if (nonconfigurableAttributes.contains(attributeName)) {
+ return owner.get(attributeName, type);
+ }
+ if (!directMap.containsKey(attributeName)) {
+ throw new IllegalArgumentException("attribute \"" + attributeName
+ + "\" isn't available in this computed default context");
+ }
+ return type.cast(directMap.get(attributeName));
+ }
+
+ @Override public String getName() { return owner.getName(); }
+ @Override public Label getLabel() { return owner.getLabel(); }
+ @Override public Iterable<String> getAttributeNames() {
+ return ImmutableList.<String>builder()
+ .addAll(directMap.keySet()).addAll(nonconfigurableAttributes).build();
+ }
+ @Override
+ public void visitLabels(AcceptsLabelAttribute observer) { owner.visitLabels(observer); }
+ @Override
+ public String getPackageDefaultHdrsCheck() { return owner.getPackageDefaultHdrsCheck(); }
+ @Override
+ public Boolean getPackageDefaultObsolete() { return owner.getPackageDefaultObsolete(); }
+ @Override
+ public Boolean getPackageDefaultTestOnly() { return owner.getPackageDefaultTestOnly(); }
+ @Override
+ public String getPackageDefaultDeprecation() { return owner.getPackageDefaultDeprecation(); }
+ @Override
+ public ImmutableList<String> getPackageDefaultCopts() {
+ return owner.getPackageDefaultCopts();
+ }
+ @Nullable @Override
+ public Type<?> getAttributeType(String attrName) { return owner.getAttributeType(attrName); }
+ @Nullable @Override public Attribute getAttributeDefinition(String attrName) {
+ return owner.getAttributeDefinition(attrName);
+ }
+ @Override public boolean isAttributeValueExplicitlySpecified(String attributeName) {
+ return owner.isAttributeValueExplicitlySpecified(attributeName);
+ }
+ };
+ }
+}