From 0cfa5d6d89f66cc6be9668dedb61042acd841268 Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Fri, 2 Sep 2016 13:56:25 +0000 Subject: Rollback of commit 19db71413329da3f5d22b5fc7681471f3d971d88. -- MOS_MIGRATED_REVID=132058819 --- .../devtools/build/lib/packages/Attribute.java | 493 ++++----------------- 1 file changed, 75 insertions(+), 418 deletions(-) (limited to 'src/main/java/com/google/devtools/build/lib/packages/Attribute.java') 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 { } /** - * 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. * - *

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. + *

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 value(ComputedDefault defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); @@ -650,28 +646,6 @@ public final class Attribute implements Comparable { return this; } - /** - * 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. - * - *

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. - * - *

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 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 { } /** - * 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 The type of exception this strategy throws if too many computations are - * attempted. - */ - interface ComputationLimiter { - 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 NULL_COMPUTATION_LIMITER = - new ComputationLimiter() { - @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 { - - /** 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. + *

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. * - *

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 { - abstract Object compute(AttributeMap map) throws TComputeException; - - /** - * Returns a lookup table mapping from: - * - *

- * - *

to: - * - *

- * - *

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. - * - *

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. - */ - Map, T> computeValuesForAllCombinations( - List dependencies, - Type type, - Rule rule, - ComputationLimiter limiter) - throws TComputeException, TLimitException { - // This will hold every (value1, value2, ..) combination of the declared dependencies. - // Collect those combinations. - AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule); - List> depMaps = mapper.visitAttributes(dependencies, limiter); - // For each combination, call compute() on a specialized AttributeMap providing those - // values. - Map, T> valueMap = new HashMap<>(); - for (Map depMap : depMaps) { - AttributeMap attrMap = mapper.createMapBackedAttributeMap(depMap); - Object value = compute(attrMap); - List 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 createDependencyAssignmentTuple( - List dependencies, AttributeMap attrMap) { - ArrayList 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. - * - *

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. - * - *

If a computed default reads the value of another attribute, at least one of the following - * must be true: + *

If a computed default reads the value of another attribute, at least one of + * the following must be true: * *

    - *
  1. The other attribute must be declared in the computed default's constructor - *
  2. The other attribute must be non-configurable ({@link Builder#nonconfigurable} + *
  3. The other attribute must be declared in the computed default's constructor
  4. + *
  5. The other attribute must be non-configurable ({@link Builder#nonconfigurable}
  6. *
* - *

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. + *

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. * *

Implementations of this interface must be immutable. */ @@ -1206,7 +1054,7 @@ public final class Attribute implements Comparable { * configurable attribute values. */ public ComputedDefault() { - this(ImmutableList.of()); + dependencies = ImmutableList.of(); } /** @@ -1214,7 +1062,7 @@ public final class Attribute implements Comparable { * 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 { * 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. - * - *

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 dependencies) { - // Order is important for #createDependencyAssignmentTuple. - this.dependencies = Ordering.natural().immutableSortedCopy(dependencies); - } - - Iterable getPossibleValues(Type type, Rule rule) { - final ComputedDefault owner = ComputedDefault.this; - ComputationStrategy strategy = - new ComputationStrategy() { - @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,210 +1078,9 @@ public final class Attribute implements Comparable { 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 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 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}. - * - *

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}. - * - *

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 caughtEvalExceptionIfAny = new AtomicReference<>(); - ComputationStrategy strategy = - new ComputationStrategy() { - @Override - public Object compute(AttributeMap map) throws InterruptedException { - try { - return owner.computeValue(map); - } catch (EvalException ex) { - caughtEvalExceptionIfAny.compareAndSet(null, ex); - return null; - } - } - }; - - ImmutableList.Builder> dependencyTypesBuilder = ImmutableList.builder(); - Map, 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 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 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. - * - *

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> dependencyTypes; - private final Map, 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 requiredAttributes, - ImmutableList> dependencyTypes, - Map, Object> lookupTable) { - super(Preconditions.checkNotNull(requiredAttributes)); - this.dependencyTypes = Preconditions.checkNotNull(dependencyTypes); - this.lookupTable = Preconditions.checkNotNull(lookupTable); - } - - List> getDependencyTypes() { - return dependencyTypes; - } - - Map, Object> getLookupTable() { - return lookupTable; - } - - @Override - public Object getDefault(AttributeMap rule) { - List 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 - Iterable getPossibleValues(Type type, Rule rule) { - List 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 { public abstract List