aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java
diff options
context:
space:
mode:
authorGravatar Tobias Werth <twerth@google.com>2016-09-02 13:56:25 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2016-09-06 15:37:42 +0000
commit0cfa5d6d89f66cc6be9668dedb61042acd841268 (patch)
tree5e7354f49df2cfb36cd88a3d6bfcd82bc36db596 /src/main/java
parentd9d524ab77ebff673009e7405678c237bdb510fc (diff)
-- MOS_MIGRATED_REVID=132058819
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java69
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/Attribute.java493
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/RuleClass.java60
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java20
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java23
7 files changed, 147 insertions, 526 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
index abaccccdff..4fab639d96 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java
@@ -42,7 +42,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.CollectionUtils;
-import com.google.devtools.build.lib.packages.Attribute.ComputationLimiter;
import com.google.devtools.build.lib.packages.BuildType.Selector;
import com.google.devtools.build.lib.packages.BuildType.SelectorList;
import com.google.devtools.build.lib.syntax.Type;
@@ -56,7 +55,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
/**
@@ -69,12 +67,18 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
private static final ImmutableSet<Type<?>> scalarTypes =
ImmutableSet.of(INTEGER, STRING, LABEL, NODEP_LABEL, OUTPUT, BOOLEAN, TRISTATE, LICENSE);
- private final Rule rule;
+ /**
+ * 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());
- this.rule = rule;
+
+ nonConfigurableAttributes = rule.getRuleClassObject().getNonConfigurableAttributes();
}
public static AggregatingAttributeMapper of(Rule rule) {
@@ -82,14 +86,6 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
}
/**
- * Returns 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 List<String> getNonConfigurableAttributes() {
- return rule.getRuleClassObject().getNonConfigurableAttributes();
- }
-
- /**
* Override that also visits the rule's configurable attribute keys (which are themselves labels).
*
* <p>Note that we directly parse the selectors rather than just calling {@link #visitAttribute}
@@ -329,7 +325,16 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
// 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) {
- return computedDefault.getPossibleValues(type, rule);
+ // The depMaps list holds every assignment of possible values to the computed default's
+ // declared possibly-configurable dependencies.
+ List<Map<String, Object>> depMaps = visitAttributes(computedDefault.dependencies());
+
+ 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.
@@ -456,20 +461,18 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
* ]
* </pre>
*
- * <p>The work done by this method may be limited by providing a {@link ComputationLimiter} that
- * throws if too much work is attempted.
+ * <p>This uses time and space exponential on the number of inputs. To guard against misuse,
+ * {@code attributes.size()} must be two or less.
*/
- <TException extends Exception> List<Map<String, Object>> visitAttributes(
- List<String> attributes, ComputationLimiter<TException> limiter) throws TException {
+ private List<Map<String, Object>> visitAttributes(List<String> attributes) {
+ Preconditions.checkState(attributes.size() <= 2);
List<Map<String, Object>> depMaps = new LinkedList<>();
- AtomicInteger combinationsSoFar = new AtomicInteger(0);
- visitAttributesInner(
- attributes, depMaps, ImmutableMap.<String, Object>of(), combinationsSoFar, limiter);
+ visitAttributesInner(attributes, depMaps, ImmutableMap.<String, Object>of());
return depMaps;
}
/**
- * A recursive function used in the implementation of {@link #visitAttributes}.
+ * A recursive function used in the implementation of {@link #visitAttributes(List)}.
*
* @param attributes a list of attributes that are not yet assigned values in the {@code
* currentMap} parameter.
@@ -477,21 +480,10 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
* will add newly discovered maps to the list.
* @param currentMap a (possibly non-empty) map holding {attrName --> attrValue} assignments for
* attributes not in the {@code attributes} list.
- * @param combinationsSoFar a counter for all previously processed combinations of possible
- * values.
- * @param limiter a strategy to limit the work done by invocations of this method.
*/
- private <TException extends Exception> void visitAttributesInner(
- List<String> attributes,
- List<Map<String, Object>> mappings,
- Map<String, Object> currentMap,
- AtomicInteger combinationsSoFar,
- ComputationLimiter<TException> limiter)
- throws TException {
+ private void visitAttributesInner(
+ List<String> attributes, List<Map<String, Object>> mappings, Map<String, Object> currentMap) {
if (attributes.isEmpty()) {
- // Because this method uses exponential time/space on the number of inputs, we may limit
- // the total number of method calls.
- limiter.onComputationCount(combinationsSoFar.incrementAndGet());
// Recursive base case: store whatever's already been populated in currentMap.
mappings.add(currentMap);
return;
@@ -508,8 +500,7 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
Map<String, Object> newMap = new HashMap<>();
newMap.putAll(currentMap);
newMap.put(firstAttribute, value);
- visitAttributesInner(
- attributes.subList(1, attributes.size()), mappings, newMap, combinationsSoFar, limiter);
+ visitAttributesInner(attributes.subList(1, attributes.size()), mappings, newMap);
}
}
@@ -520,14 +511,14 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
* configurable attribute that's not in {@code directMap} causes an {@link
* IllegalArgumentException} to be thrown.
*/
- AttributeMap createMapBackedAttributeMap(final Map<String, Object> directMap) {
+ 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 (getNonConfigurableAttributes().contains(attributeName)) {
+ if (nonConfigurableAttributes.contains(attributeName)) {
return owner.get(attributeName, type);
}
if (!directMap.containsKey(attributeName)) {
@@ -558,7 +549,7 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper {
public Iterable<String> getAttributeNames() {
return ImmutableList.<String>builder()
.addAll(directMap.keySet())
- .addAll(getNonConfigurableAttributes())
+ .addAll(nonConfigurableAttributes)
.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
index 68540c8b87..996371494d 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
@@ -22,18 +22,14 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.syntax.ClassObject;
import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.EvalUtils;
-import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.syntax.Type.ConversionException;
@@ -41,7 +37,6 @@ import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.StringUtil;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
@@ -50,7 +45,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.concurrent.Immutable;
/**
@@ -633,14 +627,16 @@ public final class Attribute implements Comparable<Attribute> {
}
/**
- * Sets the attribute default value to a computed default value - use this when the default
- * value is a function of other attributes of the Rule. The type of the computed default value
- * for a mandatory attribute must match the type parameter: (e.g. list=[], integer=0, string="",
+ * Sets the attribute default value to a computed default value - use
+ * this when the default value is a function of other attributes of the
+ * Rule. The type of the computed default value for a mandatory attribute
+ * must match the type parameter: (e.g. list=[], integer=0, string="",
* label=null). The {@code defaultValue} implementation must be immutable.
*
- * <p>If the computed default returns a Label that is a target, that target will become an
- * implicit dependency of this Rule; we will load the target (and its dependencies) if it
- * encounters the Rule and build the target if needs to apply the Rule.
+ * <p>If computedDefault returns a Label that is a target, that target will
+ * become an implicit dependency of this Rule; we will load the target
+ * (and its dependencies) if it encounters the Rule and build the target if
+ * needs to apply the Rule.
*/
public Builder<TYPE> value(ComputedDefault defaultValue) {
Preconditions.checkState(!valueSet, "the default value is already set");
@@ -651,28 +647,6 @@ public final class Attribute implements Comparable<Attribute> {
}
/**
- * Sets the attribute default value to a Skylark computed default template. Like a native
- * Computed Default, this allows a Skylark-defined Rule Class to specify that the default value
- * of an attribute is a function of other attributes of the Rule.
- *
- * <p>During the loading phase, the computed default template will be specialized for each rule
- * it applies to. Those rules' attribute values will not be references to {@link
- * SkylarkComputedDefaultTemplate}s, but instead will be references to {@link
- * SkylarkComputedDefault}s.
- *
- * <p>If the computed default returns a Label that is a target, that target will become an
- * implicit dependency of this Rule; we will load the target (and its dependencies) if it
- * encounters the Rule and build the target if needs to apply the Rule.
- */
- public Builder<TYPE> value(SkylarkComputedDefaultTemplate skylarkComputedDefaultTemplate) {
- Preconditions.checkState(!valueSet, "the default value is already set");
- value = skylarkComputedDefaultTemplate;
- valueSource = AttributeValueSource.COMPUTED_DEFAULT;
- valueSet = true;
- return this;
- }
-
- /**
* Sets the attribute default value to be late-bound, i.e., it is derived from the build
* configuration.
*/
@@ -1047,154 +1021,28 @@ public final class Attribute implements Comparable<Attribute> {
}
/**
- * A strategy for dealing with too many computations, used when creating lookup tables for {@link
- * ComputedDefault}s.
+ * A computed default is a default value for a Rule attribute that is a
+ * function of other attributes of the rule.
*
- * @param <TException> The type of exception this strategy throws if too many computations are
- * attempted.
- */
- interface ComputationLimiter<TException extends Exception> {
- void onComputationCount(int count) throws TException;
- }
-
- /**
- * An implementation of {@link ComputationLimiter} that never throws. For use with
- * natively-defined {@link ComputedDefault}s, which are limited in the number of configurable
- * attributes they depend on, not on the number of different combinations of possible inputs.
- */
- private static final ComputationLimiter<RuntimeException> NULL_COMPUTATION_LIMITER =
- new ComputationLimiter<RuntimeException>() {
- @Override
- public void onComputationCount(int count) throws RuntimeException {}
- };
-
- /** Exception for computed default attributes that depend on too many configurable attributes. */
- private static class TooManyConfigurableAttributesException extends Exception {
- TooManyConfigurableAttributesException(int max) {
- super(
- String.format(
- "Too many configurable attributes to compute all possible values: "
- + "Found more than %d possible values.",
- max));
- }
- }
-
- private static class FixedComputationLimiter
- implements ComputationLimiter<TooManyConfigurableAttributesException> {
-
- /** Upper bound of the number of combinations of values for a computed default attribute. */
- private static final int COMPUTED_DEFAULT_MAX_COMBINATIONS = 64;
-
- private static final FixedComputationLimiter INSTANCE = new FixedComputationLimiter();
-
- @Override
- public void onComputationCount(int count) throws TooManyConfigurableAttributesException {
- if (count > COMPUTED_DEFAULT_MAX_COMBINATIONS) {
- throw new TooManyConfigurableAttributesException(COMPUTED_DEFAULT_MAX_COMBINATIONS);
- }
- }
- }
-
- /**
- * Specifies how values of {@link ComputedDefault} attributes are computed based on the values of
- * other attributes.
+ * <p>Attributes whose defaults are computed are first initialized to the default
+ * for their type, and then the computed defaults are evaluated after all
+ * non-computed defaults have been initialized. There is no defined order
+ * among computed defaults, so they must not depend on each other.
*
- * <p>The {@code TComputeException} type parameter allows the two specializations of this class to
- * describe whether and how their computations throw. For natively defined computed defaults,
- * computation does not throw, but for Skylark-defined computed defaults, computation may throw
- * {@link InterruptedException}.
- */
- private abstract static class ComputationStrategy<TComputeException extends Exception> {
- abstract Object compute(AttributeMap map) throws TComputeException;
-
- /**
- * Returns a lookup table mapping from:
- *
- * <ul>
- * <li>tuples of values that may be assigned by {@code rule} to attributes with names in {@code
- * dependencies} (note that there may be more than one such tuple for any given rule, if any
- * of the dependencies are configurable)
- * </ul>
- *
- * <p>to:
- *
- * <ul>
- * <li>the value {@link #compute(AttributeMap)} evaluates to when the provided {@link
- * AttributeMap} contains the values specified by that assignment, or {@code null} if the
- * {@link ComputationStrategy} failed to evaluate.
- * </ul>
- *
- * <p>The lookup table contains a tuple for each possible assignment to the {@code dependencies}
- * attributes. The meaning of each tuple is well-defined because {@code dependencies} is
- * ordered.
- *
- * <p>This is useful because configurable attributes may have many possible values. During the
- * loading phase a configurable attribute can't be resolved to a single value. Configuration
- * information, needed to resolve such an attribute, is only available during analysis. However,
- * any labels that a ComputedDefault attribute may evaluate to must be loaded during the loading
- * phase.
- */
- <T, TLimitException extends Exception> Map<List<Object>, T> computeValuesForAllCombinations(
- List<String> dependencies,
- Type<T> type,
- Rule rule,
- ComputationLimiter<TLimitException> limiter)
- throws TComputeException, TLimitException {
- // This will hold every (value1, value2, ..) combination of the declared dependencies.
- // Collect those combinations.
- AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
- List<Map<String, Object>> depMaps = mapper.visitAttributes(dependencies, limiter);
- // For each combination, call compute() on a specialized AttributeMap providing those
- // values.
- Map<List<Object>, T> valueMap = new HashMap<>();
- for (Map<String, Object> depMap : depMaps) {
- AttributeMap attrMap = mapper.createMapBackedAttributeMap(depMap);
- Object value = compute(attrMap);
- List<Object> key = createDependencyAssignmentTuple(dependencies, attrMap);
- valueMap.put(key, type.cast(value));
- }
- return valueMap;
- }
-
- /**
- * Given an {@link AttributeMap}, containing an assignment to each attribute in {@code
- * dependencies}, this returns a list of the assigned values, ordered as {@code dependencies} is
- * ordered.
- */
- static List<Object> createDependencyAssignmentTuple(
- List<String> dependencies, AttributeMap attrMap) {
- ArrayList<Object> tuple = new ArrayList<>(dependencies.size());
- for (String attrName : dependencies) {
- Type<?> attrType = attrMap.getAttributeType(attrName);
- tuple.add(attrMap.get(attrName, attrType));
- }
- return tuple;
- }
- }
-
- /**
- * A computed default is a default value for a Rule attribute that is a function of other
- * attributes of the rule.
- *
- * <p>Attributes whose defaults are computed are first initialized to the default for their type,
- * and then the computed defaults are evaluated after all non-computed defaults have been
- * initialized. There is no defined order among computed defaults, so they must not depend on each
- * other.
- *
- * <p>If a computed default reads the value of another attribute, at least one of the following
- * must be true:
+ * <p>If a computed default reads the value of another attribute, at least one of
+ * the following must be true:
*
* <ol>
- * <li>The other attribute must be declared in the computed default's constructor
- * <li>The other attribute must be non-configurable ({@link Builder#nonconfigurable}
+ * <li>The other attribute must be declared in the computed default's constructor</li>
+ * <li>The other attribute must be non-configurable ({@link Builder#nonconfigurable}</li>
* </ol>
*
- * <p>The reason for enforced declarations is that, since attribute values might be configurable,
- * a computed default that depends on them may itself take multiple values. Since we have no
- * access to a target's configuration at the time these values are computed, we need the ability
- * to probe the default's *complete* dependency space. Declared dependencies allow us to do so
- * sanely. Non-configurable attributes don't have this problem because their value is fixed and
- * known even without configuration information.
+ * <p>The reason for enforced declarations is that, since attribute values might be
+ * configurable, a computed default that depends on them may itself take multiple
+ * values. Since we have no access to a target's configuration at the time these values
+ * are computed, we need the ability to probe the default's *complete* dependency space.
+ * Declared dependencies allow us to do so sanely. Non-configurable attributes don't have
+ * this problem because their value is fixed and known even without configuration information.
*
* <p>Implementations of this interface must be immutable.
*/
@@ -1206,7 +1054,7 @@ public final class Attribute implements Comparable<Attribute> {
* configurable attribute values.
*/
public ComputedDefault() {
- this(ImmutableList.<String>of());
+ dependencies = ImmutableList.of();
}
/**
@@ -1214,7 +1062,7 @@ public final class Attribute implements Comparable<Attribute> {
* explicitly specified configurable attribute value
*/
public ComputedDefault(String depAttribute) {
- this(ImmutableList.of(depAttribute));
+ dependencies = ImmutableList.of(depAttribute);
}
/**
@@ -1222,38 +1070,7 @@ public final class Attribute implements Comparable<Attribute> {
* explicitly specified configurable attribute values.
*/
public ComputedDefault(String depAttribute1, String depAttribute2) {
- this(ImmutableList.of(depAttribute1, depAttribute2));
- }
-
- /**
- * Creates a computed default that can read all non-configurable attributes and some explicitly
- * specified configurable attribute values.
- *
- * <p>This constructor should not be used by native {@link ComputedDefault} functions. The limit
- * of at-most-two depended-on configurable attributes is intended, to limit the exponential
- * growth of possible values. {@link SkylarkComputedDefault} uses this, but is limited by {@link
- * FixedComputationLimiter#COMPUTED_DEFAULT_MAX_COMBINATIONS}.
- */
- protected ComputedDefault(ImmutableList<String> dependencies) {
- // Order is important for #createDependencyAssignmentTuple.
- this.dependencies = Ordering.natural().immutableSortedCopy(dependencies);
- }
-
- <T> Iterable<T> getPossibleValues(Type<T> type, Rule rule) {
- final ComputedDefault owner = ComputedDefault.this;
- ComputationStrategy<RuntimeException> strategy =
- new ComputationStrategy<RuntimeException>() {
- @Override
- public Object compute(AttributeMap map) {
- return owner.getDefault(map);
- }
- };
- // Note that this uses ArrayList instead of something like ImmutableList because some
- // values may be null.
- return new ArrayList<>(
- strategy
- .computeValuesForAllCombinations(dependencies, type, rule, NULL_COMPUTATION_LIMITER)
- .values());
+ dependencies = ImmutableList.of(depAttribute1, depAttribute2);
}
/** The list of configurable attributes this ComputedDefault declares it may read. */
@@ -1261,211 +1078,10 @@ public final class Attribute implements Comparable<Attribute> {
return dependencies;
}
- /**
- * Returns the value this {@link ComputedDefault} evaluates to, given the inputs contained in
- * {@code rule}.
- */
public abstract Object getDefault(AttributeMap rule);
}
/**
- * A Skylark-defined computed default, which can be precomputed for a specific {@link Rule} by
- * calling {@link #computePossibleValues}, which returns a {@link SkylarkComputedDefault} that
- * contains a lookup table.
- */
- public static final class SkylarkComputedDefaultTemplate {
- private final Type<?> type;
- private final SkylarkCallbackFunction callback;
- private final Location location;
- private final ImmutableList<String> dependencies;
-
- /**
- * Creates a new SkylarkComputedDefaultTemplate that allows the computation of attribute values
- * via a callback function during loading phase.
- *
- * @param type The type of the value of this attribute.
- * @param dependencies A list of all names of other attributes that are accessed by this
- * attribute.
- * @param callback A function to compute the actual attribute value.
- * @param location The location of the Skylark function.
- */
- public SkylarkComputedDefaultTemplate(
- Type<?> type,
- ImmutableList<String> dependencies,
- SkylarkCallbackFunction callback,
- Location location) {
- this.type = Preconditions.checkNotNull(type);
- this.dependencies = Preconditions.checkNotNull(dependencies);
- this.callback = Preconditions.checkNotNull(callback);
- this.location = Preconditions.checkNotNull(location);
- }
-
- /**
- * Returns a {@link SkylarkComputedDefault} containing a lookup table specifying the output of
- * this {@link SkylarkComputedDefaultTemplate}'s callback given each possible assignment {@code
- * rule} might make to the attributes specified by {@link #dependencies}.
- *
- * <p>If the rule is missing an attribute specified by {@link #dependencies}, or if there are
- * too many possible assignments, or if any evaluation fails, this throws {@link
- * CannotPrecomputeDefaultsException}.
- *
- * <p>May only be called after all non-{@link ComputedDefault} attributes have been set on the
- * {@code rule}.
- */
- SkylarkComputedDefault computePossibleValues(
- Attribute attr, final Rule rule, final EventHandler eventHandler)
- throws InterruptedException, CannotPrecomputeDefaultsException {
-
- final SkylarkComputedDefaultTemplate owner = SkylarkComputedDefaultTemplate.this;
- final String msg =
- String.format(
- "Cannot compute default value of attribute '%s' in rule '%s': ",
- attr.getName().replace('$', '_'), rule.getLabel());
- final AtomicReference<EvalException> caughtEvalExceptionIfAny = new AtomicReference<>();
- ComputationStrategy<InterruptedException> strategy =
- new ComputationStrategy<InterruptedException>() {
- @Override
- public Object compute(AttributeMap map) throws InterruptedException {
- try {
- return owner.computeValue(map);
- } catch (EvalException ex) {
- caughtEvalExceptionIfAny.compareAndSet(null, ex);
- return null;
- }
- }
- };
-
- ImmutableList.Builder<Type<?>> dependencyTypesBuilder = ImmutableList.builder();
- Map<List<Object>, Object> lookupTable = new HashMap<>();
- try {
- for (String dependency : dependencies) {
- Attribute attribute = rule.getRuleClassObject().getAttributeByNameMaybe(dependency);
- if (attribute == null) {
- throw new AttributeNotFoundException(
- String.format("No such attribute %s in rule %s", dependency, rule.getLabel()));
- }
- dependencyTypesBuilder.add(attribute.getType());
- }
- lookupTable.putAll(
- strategy.computeValuesForAllCombinations(
- dependencies, attr.getType(), rule, FixedComputationLimiter.INSTANCE));
- if (caughtEvalExceptionIfAny.get() != null) {
- throw caughtEvalExceptionIfAny.get();
- }
- } catch (AttributeNotFoundException
- | TooManyConfigurableAttributesException
- | EvalException ex) {
- String error = msg + ex.getMessage();
- rule.reportError(error, eventHandler);
- throw new CannotPrecomputeDefaultsException(error);
- }
- return new SkylarkComputedDefault(dependencies, dependencyTypesBuilder.build(), lookupTable);
- }
-
- private Object computeValue(AttributeMap rule) throws EvalException, InterruptedException {
- Map<String, Object> attrValues = new HashMap<>();
- for (String attrName : rule.getAttributeNames()) {
- Attribute attr = rule.getAttributeDefinition(attrName);
- if (!attr.hasComputedDefault()) {
- Object value = rule.get(attrName, attr.getType());
- if (!EvalUtils.isNullOrNone(value)) {
- attrValues.put(attr.getName(), value);
- }
- }
- }
- return invokeCallback(attrValues);
- }
-
- private Object invokeCallback(Map<String, Object> attrValues)
- throws EvalException, InterruptedException {
- ClassObject attrs =
- SkylarkClassObjectConstructor.STRUCT.create(
- attrValues, "No such regular (non computed) attribute '%s'.");
- Object result = callback.call(attrs);
- try {
- return type.cast((result == Runtime.NONE) ? type.getDefaultValue() : result);
- } catch (ClassCastException ex) {
- throw new EvalException(
- location,
- String.format(
- "Expected '%s', but got '%s'", type, EvalUtils.getDataTypeName(result, true)));
- }
- }
-
- private static class AttributeNotFoundException extends Exception {
- private AttributeNotFoundException(String message) {
- super(message);
- }
- }
-
- static class CannotPrecomputeDefaultsException extends Exception {
- private CannotPrecomputeDefaultsException(String message) {
- super(message);
- }
- }
- }
-
- /**
- * A class for computed attributes defined in Skylark.
- *
- * <p>Unlike {@link ComputedDefault}, instances of this class contain a pre-computed table of all
- * possible assignments of depended-on attributes and what the Skylark function evaluates to, and
- * {@link #getPossibleValues(Type, Rule)} and {@link #getDefault(AttributeMap)} do lookups in that
- * table.
- */
- static final class SkylarkComputedDefault extends ComputedDefault {
-
- private final List<Type<?>> dependencyTypes;
- private final Map<List<Object>, Object> lookupTable;
-
- /**
- * Creates a new SkylarkComputedDefault containing a lookup table.
- *
- * @param requiredAttributes A list of all names of other attributes that are accessed by this
- * attribute.
- * @param dependencyTypes A list of requiredAttributes' types.
- * @param lookupTable An exhaustive mapping from requiredAttributes assignments to values this
- * computed default evaluates to.
- */
- SkylarkComputedDefault(
- ImmutableList<String> requiredAttributes,
- ImmutableList<Type<?>> dependencyTypes,
- Map<List<Object>, Object> lookupTable) {
- super(Preconditions.checkNotNull(requiredAttributes));
- this.dependencyTypes = Preconditions.checkNotNull(dependencyTypes);
- this.lookupTable = Preconditions.checkNotNull(lookupTable);
- }
-
- List<Type<?>> getDependencyTypes() {
- return dependencyTypes;
- }
-
- Map<List<Object>, Object> getLookupTable() {
- return lookupTable;
- }
-
- @Override
- public Object getDefault(AttributeMap rule) {
- List<Object> key = ComputationStrategy.createDependencyAssignmentTuple(dependencies(), rule);
- Preconditions.checkState(
- lookupTable.containsKey(key),
- "Error in rule '%s': precomputed value missing for dependencies: %s",
- rule.getLabel(),
- Iterables.toString(key));
- return lookupTable.get(key);
- }
-
- @Override
- <T> Iterable<T> getPossibleValues(Type<T> type, Rule rule) {
- List<T> result = new ArrayList<>(lookupTable.size());
- for (Object obj : lookupTable.values()) {
- result.add(type.cast(obj));
- }
- return result;
- }
- }
-
- /**
* Marker interface for late-bound values. Unfortunately, we can't refer to BuildConfiguration
* right now, since that is in a separate compilation unit.
*
@@ -1596,6 +1212,51 @@ public final class Attribute implements Comparable<Attribute> {
public abstract List<Label> resolve(Rule rule, AttributeMap attributes, T configuration);
}
+ /**
+ * A class for late bound attributes defined in Skylark.
+ */
+ public static final class SkylarkLateBound implements LateBoundDefault<Object> {
+
+ private final SkylarkCallbackFunction callback;
+
+ public SkylarkLateBound(SkylarkCallbackFunction callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public boolean useHostConfiguration() {
+ return false;
+ }
+
+ @Override
+ public ImmutableSet<Class<?>> getRequiredConfigurationFragments() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Object getDefault() {
+ return null;
+ }
+
+ @Override
+ public Object resolve(Rule rule, AttributeMap attributes, Object o)
+ throws EvalException, InterruptedException {
+ Map<String, Object> attrValues = new HashMap<>();
+ for (Attribute attr : rule.getAttributes()) {
+ if (!attr.isLateBound()) {
+ Object value = attributes.get(attr.getName(), attr.getType());
+ if (value != null) {
+ attrValues.put(attr.getName(), value);
+ }
+ }
+ }
+ ClassObject attrs = SkylarkClassObjectConstructor.STRUCT.create(
+ attrValues,
+ "No such regular (non late-bound) attribute '%s'.");
+ return callback.call(attrs, o);
+ }
+ }
+
private final String name;
private final Type<?> type;
@@ -1606,10 +1267,8 @@ public final class Attribute implements Comparable<Attribute> {
// 1. defaultValue == null.
// 2. defaultValue instanceof ComputedDefault &&
// type.isValid(defaultValue.getDefault())
- // 3. defaultValue instanceof SkylarkComputedDefaultTemplate &&
- // type.isValid(defaultValue.computePossibleValues().getDefault())
- // 4. type.isValid(defaultValue).
- // 5. defaultValue instanceof LateBoundDefault &&
+ // 3. type.isValid(defaultValue).
+ // 4. defaultValue instanceof LateBoundDefault &&
// type.isValid(defaultValue.getDefault(configuration))
// (We assume a hypothetical Type.isValid(Object) predicate.)
private final Object defaultValue;
@@ -1997,9 +1656,7 @@ public final class Attribute implements Comparable<Attribute> {
* @see #getDefaultValue(Rule)
*/
boolean hasComputedDefault() {
- return (defaultValue instanceof ComputedDefault)
- || (defaultValue instanceof SkylarkComputedDefaultTemplate)
- || (condition != null);
+ return (defaultValue instanceof ComputedDefault) || (condition != null);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java b/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java
index 7acaa58444..30999c283f 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java
@@ -34,8 +34,7 @@ public enum AttributeValueSource {
* Creates a new instance and defines the prefixes for both Skylark and native.
*
* @param nativePrefix The prefix when converted to a native attribute name.
- * @param mustHaveSkylarkPrefix Whether the Skylark name must start with {@link
- * AttributeValueSource#SKYLARK_PREFIX}.
+ * @param mustHaveSkylarkPrefix Whether the Skylark name must start with {@link SKYLARK_PREFIX}.
*/
AttributeValueSource(String nativePrefix, boolean mustHaveSkylarkPrefix) {
this.nativePrefix = nativePrefix;
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index cba10a705f..751e6cf7f6 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -14,8 +14,8 @@
package com.google.devtools.build.lib.packages;
-import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
@@ -34,8 +34,6 @@ import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.events.NullEventHandler;
-import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate;
-import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate.CannotPrecomputeDefaultsException;
import com.google.devtools.build.lib.packages.BuildType.SelectorList;
import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy.MissingFragmentPolicy;
import com.google.devtools.build.lib.packages.RuleFactory.AttributeValuesMap;
@@ -1312,15 +1310,13 @@ public final class RuleClass {
* attributeValues} or the default attribute values associated with this {@link RuleClass} and
* {@code pkgBuilder}.
*
- * <p>The created {@link Rule} will also be populated with output files. These output files will
- * have been collected from the explicitly provided values of type {@link BuildType#OUTPUT} and
- * {@link BuildType#OUTPUT_LIST} as well as from the implicit outputs determined by this {@link
- * RuleClass} and the values in {@code attributeValues}.
+ * <p>The created {@link Rule} will also be populated with output files. These output files
+ * will have been collected from the explicitly provided values of type {@link BuildType#OUTPUT}
+ * and {@link BuildType#OUTPUT_LIST} as well as from the implicit outputs determined by this
+ * {@link RuleClass} and the values in {@code attributeValues}.
*
* <p>This performs several validity checks. Invalid output file labels result in a thrown {@link
- * LabelSyntaxException}. Computed default attributes that fail during precomputation result in a
- * {@link CannotPrecomputeDefaultsException}. All other errors are reported on {@code
- * eventHandler}.
+ * LabelSyntaxException}. All other errors are reported on {@code eventHandler}.
*/
Rule createRule(
Package.Builder pkgBuilder,
@@ -1330,7 +1326,7 @@ public final class RuleClass {
@Nullable FuncallExpression ast,
Location location,
AttributeContainer attributeContainer)
- throws LabelSyntaxException, InterruptedException, CannotPrecomputeDefaultsException {
+ throws LabelSyntaxException, InterruptedException {
Rule rule = pkgBuilder.createRule(ruleLabel, this, location, attributeContainer);
populateRuleAttributeValues(rule, pkgBuilder, attributeValues, eventHandler);
checkAspectAllowedValues(rule, eventHandler);
@@ -1358,7 +1354,7 @@ public final class RuleClass {
Location location,
AttributeContainer attributeContainer,
ImplicitOutputsFunction implicitOutputsFunction)
- throws LabelSyntaxException, InterruptedException, CannotPrecomputeDefaultsException {
+ throws LabelSyntaxException, InterruptedException {
Rule rule = pkgBuilder.createRule(
ruleLabel,
this,
@@ -1381,8 +1377,7 @@ public final class RuleClass {
Rule rule,
Package.Builder pkgBuilder,
AttributeValuesMap attributeValues,
- EventHandler eventHandler)
- throws InterruptedException, CannotPrecomputeDefaultsException {
+ EventHandler eventHandler) {
BitSet definedAttrIndices =
populateDefinedRuleAttributeValues(rule, attributeValues, eventHandler);
populateDefaultRuleAttributeValues(rule, pkgBuilder, definedAttrIndices, eventHandler);
@@ -1462,15 +1457,14 @@ public final class RuleClass {
/**
* Populates the attributes table of the new {@link Rule} with default values provided by this
- * {@link RuleClass} and the {@code pkgBuilder}. This will only provide values for attributes that
- * haven't already been populated, using {@code definedAttrIndices} to determine whether an
+ * {@link RuleClass} and the {@code pkgBuilder}. This will only provide values for attributes
+ * that haven't already been populated, using {@code definedAttrIndices} to determine whether an
* attribute was populated.
*
* <p>Errors are reported on {@code eventHandler}.
*/
private void populateDefaultRuleAttributeValues(
- Rule rule, Package.Builder pkgBuilder, BitSet definedAttrIndices, EventHandler eventHandler)
- throws InterruptedException, CannotPrecomputeDefaultsException {
+ Rule rule, Package.Builder pkgBuilder, BitSet definedAttrIndices, EventHandler eventHandler) {
// Set defaults; ensure that every mandatory attribute has a value. Use the default if none
// is specified.
List<Attribute> attrsWithComputedDefaults = new ArrayList<>();
@@ -1507,29 +1501,13 @@ public final class RuleClass {
// Set computed default attribute values now that all other (i.e. non-computed) default values
// have been set.
for (Attribute attr : attrsWithComputedDefaults) {
- // If Attribute#hasComputedDefault was true above, Attribute#getDefaultValue returns the
- // computed default function object or a Skylark computed default template. Note that we
- // cannot determine the exact value of the computed default function here because it may
- // depend on other attribute values that are configurable (i.e. they came from select({..})
- // expressions in the build language, and they require configuration data from the analysis
- // phase to be resolved). Instead, we're setting the attribute value to a reference to the
- // computed default function, or if #getDefaultValue is a Skylark computed default
- // template, setting the attribute value to a reference to the SkylarkComputedDefault
- // returned from SkylarkComputedDefaultTemplate#computePossibleValues.
- //
- // SkylarkComputedDefaultTemplate#computePossibleValues pre-computes all possible values the
- // function may evaluate to, and records them in a lookup table. By calling it here, with an
- // EventHandler, any errors that might occur during the function's evaluation can
- // be discovered and propagated here.
- Object valueToSet;
- Object defaultValue = attr.getDefaultValue(rule);
- if (defaultValue instanceof SkylarkComputedDefaultTemplate) {
- SkylarkComputedDefaultTemplate template = (SkylarkComputedDefaultTemplate) defaultValue;
- valueToSet = template.computePossibleValues(attr, rule, eventHandler);
- } else {
- valueToSet = defaultValue;
- }
- rule.setAttributeValue(attr, valueToSet, /*explicit=*/ false);
+ // If Attribute#hasComputedDefault is true, Attribute#getDefaultValue returns the computed
+ // default function object. Note that we don't evaluate the computed default function here
+ // because it may depend on other attribute values that are configurable (i.e. they came
+ // from select({..}) expressions in the build language, and they require configuration data
+ // from the analysis phase to be resolved). We're setting the attribute value to a
+ // reference to the computed default function.
+ rule.setAttributeValue(attr, attr.getDefaultValue(rule), /*explicit=*/ false);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
index 4795c7b1ac..3b6cf4c904 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
@@ -20,7 +20,6 @@ import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate.CannotPrecomputeDefaultsException;
import com.google.devtools.build.lib.packages.Package.NameConflictException;
import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
import com.google.devtools.build.lib.syntax.BaseFunction;
@@ -29,8 +28,10 @@ import com.google.devtools.build.lib.syntax.FuncallExpression;
import com.google.devtools.build.lib.syntax.UserDefinedFunction;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.Preconditions;
+
import java.util.Map;
import java.util.Set;
+
import javax.annotation.Nullable;
/**
@@ -134,7 +135,7 @@ public class RuleFactory {
ast,
generator.location,
attributeContainer);
- } catch (LabelSyntaxException | CannotPrecomputeDefaultsException e) {
+ } catch (LabelSyntaxException e) {
throw new RuleFactory.InvalidRuleException(ruleClass + " " + e.getMessage());
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
index 49740a5fe4..3e2665dc97 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
@@ -22,7 +22,7 @@ import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.Attribute.AllowedValueSet;
import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
-import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate;
+import com.google.devtools.build.lib.packages.Attribute.SkylarkLateBound;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.SkylarkAspect;
import com.google.devtools.build.lib.skylarkinterface.Param;
@@ -46,9 +46,11 @@ import com.google.devtools.build.lib.syntax.UserDefinedFunction;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.Preconditions;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+
import javax.annotation.Nullable;
/**
@@ -161,20 +163,10 @@ public final class SkylarkAttr {
Object defaultValue = arguments.get(DEFAULT_ARG);
if (!EvalUtils.isNullOrNone(defaultValue)) {
if (defaultValue instanceof UserDefinedFunction) {
- // Computed attribute. Non label type attributes already caused a type check error.
- UserDefinedFunction callback = (UserDefinedFunction) defaultValue;
-
- // SkylarkComputedDefaultTemplate needs to know the names of all attributes that it depends
- // on. However, this method does not know anything about other attributes.
- // We solve this problem by asking the UserDefinedFunction for the parameter names used in
- // the function definition, which must be the names of attributes used by the callback.
- ImmutableList<String> dependencies = callback.getSignature().getSignature().getNames();
+ // Late bound attribute. Non label type attributes already caused a type check error.
builder.value(
- new SkylarkComputedDefaultTemplate(
- type,
- dependencies,
- new SkylarkCallbackFunction(callback, ast, env),
- ast.getLocation()));
+ new SkylarkLateBound(
+ new SkylarkCallbackFunction((UserDefinedFunction) defaultValue, ast, env)));
} else {
builder.defaultValue(defaultValue);
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
index 60b293bb1a..04ff0ac2e7 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
@@ -26,16 +26,7 @@ import com.google.devtools.build.lib.syntax.compiler.LoopLabels;
import com.google.devtools.build.lib.syntax.compiler.ReflectionUtils;
import com.google.devtools.build.lib.syntax.compiler.VariableScope;
import com.google.devtools.build.lib.vfs.PathFragment;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.ClassVisitorWrapper;
import net.bytebuddy.description.method.MethodDescription;
@@ -51,6 +42,7 @@ import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.matcher.ElementMatchers;
+
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
@@ -58,6 +50,17 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
/**
* The actual function registered in the environment. This function is defined in the
* parsed code using {@link FunctionDefStatement}.