diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/packages/Attribute.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/packages/Attribute.java | 278 |
1 files changed, 172 insertions, 106 deletions
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 5a020be02f..2fedb6a0b5 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 @@ -28,7 +28,6 @@ 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.concurrent.ThreadSafety; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; @@ -676,9 +675,9 @@ public final class Attribute implements Comparable<Attribute> { /** * Sets the attribute default value to be late-bound, i.e., it is derived from the build - * configuration. + * configuration and/or the rule's configured attributes. */ - public Builder<TYPE> value(LateBoundDefault<?> defaultValue) { + public Builder<TYPE> value(LateBoundDefault<?, ? extends TYPE> defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); Preconditions.checkState(name.isEmpty() || isLateBound(name)); value = defaultValue; @@ -1549,137 +1548,204 @@ public final class Attribute implements Comparable<Attribute> { } } + // TODO(b/65746853): Remove documentation about accepting BuildConfiguration when uses are cleaned + // up. /** - * Marker interface for late-bound values. Unfortunately, we can't refer to BuildConfiguration - * right now, since that is in a separate compilation unit. - * - * <p>Implementations of this interface must be immutable. + * Provider of values for late-bound attributes. See + * {@link Attribute#value(LateBoundDefault<?, ? extends TYPE> value)}. * * <p>Use sparingly - having different values for attributes during loading and analysis can * confuse users. + * + * @param <FragmentT> The type of value that is used to compute this value. This is usually a + * subclass of BuildConfiguration.Fragment. It may also be Void to receive null, or + * BuildConfiguration itself to receive the entire configuration. + * @param <ValueT> The type of value returned by this class. */ - public interface LateBoundDefault<T> { + @Immutable + public static final class LateBoundDefault<FragmentT, ValueT> { /** - * Whether to look up the label in the host configuration. This is only here for host - * compilation tools - we usually need to look up labels in the target configuration. + * Functional interface for computing the value of a late-bound attribute. * - * <p>This method only sets the configuration passed to {@link #resolve}. If you want the - * dependency to also be analyzed in the host configuration, use - * {@link ConfigurationTransition#HOST}. + * <p>Implementations of this interface must be immutable. */ - boolean useHostConfiguration(); + @FunctionalInterface + public interface Resolver<FragmentT, ValueT> { + ValueT resolve(Rule rule, AttributeMap attributeMap, FragmentT input); + } - /** - * Returns the set of required configuration fragments, i.e., fragments that will be accessed by - * the code. - */ - Set<Class<?>> getRequiredConfigurationFragments(); + private final boolean useHostConfiguration; + private final ValueT defaultValue; + private final Class<FragmentT> fragmentClass; + private final Resolver<FragmentT, ValueT> resolver; /** - * The default value for the attribute that is set during the loading phase. + * Creates a new LateBoundDefault which uses the rule, its configured attributes, and a fragment + * of the target configuration. + * + * <p>Note that the configuration fragment here does not take into account any transitions that + * are on the attribute with this LateBoundDefault as its value. The configuration will be the + * same as the configuration given to the target bearing the attribute. + * + * <p>Nearly all LateBoundDefaults should use this constructor. There are few situations where + * it isn't the appropriate option. + * + * <p>If you want a late-bound dependency which is configured in the host configuration, just + * use this method with {@link ConfigurationTransition#HOST}. If you also need to decide the + * label of the dependency with information gained from the host configuration - and it's very + * unlikely that you do - you can use {@link #fromHostConfiguration} as well. + * + * <p>If you want to decide an attribute's value based on the value of its other attributes, + * use a subclass of {@link ComputedDefault}. The only time you should need + * {@link #fromRuleAndAttributes} is if you need access to three or more configurable + * attributes, or if you need to match names with a late-bound attribute on another rule. + * + * <p>If you have a constant-valued attribute, but you need it to have the same name as an + * attribute on another rule which is late-bound, use {@link #fromConstant} or + * {@link #alwaysNull}. + * + * @param fragmentClass The fragment to receive from the target configuration. May also be + * BuildConfiguration.class to receive the entire configuration (deprecated) - in this case, + * you must only use methods of BuildConfiguration itself, and not use any fragments. + * @param defaultValue The default value to return at loading time, when the configuration is + * not available. + * @param resolver A function which will compute the actual value with the configuration. */ - Object getDefault(); + public static <FragmentT, ValueT> LateBoundDefault<FragmentT, ValueT> fromTargetConfiguration( + Class<FragmentT> fragmentClass, ValueT defaultValue, Resolver<FragmentT, ValueT> resolver) { + Preconditions.checkArgument( + !fragmentClass.equals(Void.class), + "Use fromRuleAndAttributesOnly to specify a LateBoundDefault which does not use " + + "configuration."); + return new LateBoundDefault<>(false, fragmentClass, defaultValue, resolver); + } /** - * The actual value for the attribute for the analysis phase, which depends on the build - * configuration. Note that configurations transitions are applied after the late-bound - * attribute was evaluated. + * Creates a new LateBoundDefault which uses the rule, its configured attributes, and a fragment + * of the host configuration. * - * @param rule the rule being evaluated - * @param attributes interface for retrieving the values of the rule's other attributes - * @param o the configuration to evaluate with + * <p>This should only be necessary in very specialized cases. In almost all cases, you don't + * need this method, just {@link #fromTargetConfiguration} and + * {@link ConfigurationTransition#HOST}. + * + * <p>This method only affects the configuration fragment passed to {@link #resolve}. You must + * also use {@link ConfigurationTransition#HOST}, so that the dependency will be analyzed in the + * host configuration. + * + * @param fragmentClass The fragment to receive from the host configuration. May also be + * BuildConfiguration.class to receive the entire configuration (deprecated) - in this case, + * you must only use methods of BuildConfiguration itself, and not use any fragments. + * It is very rare that a LateBoundDefault should need a host configuration fragment; use + * {@link #fromTargetConfiguration} in most cases. + * @param defaultValue The default value to return at loading time, when the configuration is + * not available. + * @param resolver A function which will compute the actual value with the configuration. + */ + public static <FragmentT, ValueT> LateBoundDefault<FragmentT, ValueT> fromHostConfiguration( + Class<FragmentT> fragmentClass, ValueT defaultValue, Resolver<FragmentT, ValueT> resolver) { + Preconditions.checkArgument( + !fragmentClass.equals(Void.class), + "Use fromRuleAndAttributesOnly to specify a LateBoundDefault which does not use " + + "configuration."); + return new LateBoundDefault<>(true, fragmentClass, defaultValue, resolver); + } + + /** + * Creates a new LateBoundDefault which uses only the rule and its configured attributes. + * + * <p>This should only be necessary in very specialized cases. In almost all cases, you don't + * need this method, just use {@link ComputedDefault}. + * + * <p>This is used primarily for computing values based on three or more configurable + * attributes and/or matching names with late-bound attributes on other rules. + * + * @param defaultValue The default value to return when the configuration is not available at + * loading time. + * @param resolver A function which will compute the actual value with the configuration. */ - Object resolve(Rule rule, AttributeMap attributes, T o) - throws EvalException, InterruptedException; - } - - /** - * Abstract super class for label-typed {@link LateBoundDefault} implementations that simplifies - * the client code a little and makes it a bit more type-safe. - */ - public abstract static class LateBoundLabel<T> implements LateBoundDefault<T> { - private final Label label; - private final ImmutableSet<Class<?>> requiredConfigurationFragments; - - public LateBoundLabel() { - this((Label) null); - } - - public LateBoundLabel(Class<?>... requiredConfigurationFragments) { - this((Label) null, requiredConfigurationFragments); + public static <ValueT> LateBoundDefault<Void, ValueT> fromRuleAndAttributesOnly( + ValueT defaultValue, Resolver<Void, ValueT> resolver) { + return new LateBoundDefault<>(false, Void.class, defaultValue, resolver); } - public LateBoundLabel(Label label) { - this.label = label; - this.requiredConfigurationFragments = ImmutableSet.of(); + /** + * Creates a new LateBoundDefault which always returns the given value. + * + * <p>This is used primarily for matching names with late-bound attributes on other rules and + * for testing. Use normal default values if the name does not matter. + */ + public static <ValueT> LateBoundDefault<Void, ValueT> fromConstant(final ValueT defaultValue) { + if (defaultValue == null) { + return alwaysNull(); + } + return new LateBoundDefault<>( + false, Void.class, defaultValue, (rule, attributes, unused) -> defaultValue); } - public LateBoundLabel(Label label, Class<?>... requiredConfigurationFragments) { - this.label = label; - this.requiredConfigurationFragments = ImmutableSet.copyOf(requiredConfigurationFragments); + /** + * Creates a new LateBoundDefault which always returns null. + * + * <p>This is used primarily for matching names with late-bound attributes on other rules and + * for testing. Use normal default values if the name does not matter. + */ + @SuppressWarnings("unchecked") // bivariant implementation + public static <ValueT> LateBoundDefault<Void, ValueT> alwaysNull() { + return (LateBoundDefault<Void, ValueT>) ALWAYS_NULL; } - public LateBoundLabel(String label) { - this(Label.parseAbsoluteUnchecked(label)); - } + private static final LateBoundDefault<Void, Void> ALWAYS_NULL = + new LateBoundDefault<>(false, Void.class, null, (rule, attributes, unused) -> null); - public LateBoundLabel(String label, Class<?>... requiredConfigurationFragments) { - this(Label.parseAbsoluteUnchecked(label), requiredConfigurationFragments); + private LateBoundDefault( + boolean useHostConfiguration, + Class<FragmentT> fragmentClass, + ValueT defaultValue, + Resolver<FragmentT, ValueT> resolver) { + this.useHostConfiguration = useHostConfiguration; + this.defaultValue = defaultValue; + this.fragmentClass = fragmentClass; + this.resolver = resolver; } - @Override + /** + * Whether to look up the label in the host configuration. This is only here for host + * compilation tools - we usually need to look up labels in the target configuration. + */ public boolean useHostConfiguration() { - return false; + return useHostConfiguration; } - @Override - public ImmutableSet<Class<?>> getRequiredConfigurationFragments() { - return requiredConfigurationFragments; - } - - @Override - public final Label getDefault() { - return label; - } - - @Override - public abstract Label resolve(Rule rule, AttributeMap attributes, T configuration); - } - - /** - * Abstract super class for label-list-typed {@link LateBoundDefault} implementations that - * simplifies the client code a little and makes it a bit more type-safe. - */ - public abstract static class LateBoundLabelList<T> implements LateBoundDefault<T> { - private final ImmutableList<Label> labels; - private final ImmutableSet<Class<?>> requiredConfigurationFragments; - - public LateBoundLabelList(Class<?>... requiredConfigurationFragments) { - this(ImmutableList.<Label>of(), requiredConfigurationFragments); - } - - public LateBoundLabelList(List<Label> labels, Class<?>... requiredConfigurationFragments) { - this.labels = ImmutableList.copyOf(labels); - this.requiredConfigurationFragments = ImmutableSet.copyOf(requiredConfigurationFragments); - } - - @Override - public boolean useHostConfiguration() { - return false; + /** + * Returns the input type that the attribute expects. This is almost always a configuration + * fragment to be retrieved from the target's configuration (or the host configuration). + * + * <p>It may also be {@link Void} to receive null. This is rarely necessary, but can be used, + * e.g., if the attribute is named to match an attribute in another rule which is late-bound. + * + * <p>It may also be BuildConfiguration to receive the entire configuration. This is deprecated, + * and only necessary when the default is computed from methods of BuildConfiguration itself. + */ + public Class<FragmentT> getFragmentClass() { + return fragmentClass; } - @Override - public ImmutableSet<Class<?>> getRequiredConfigurationFragments() { - return requiredConfigurationFragments; + /** The default value for the attribute that is set during the loading phase. */ + public ValueT getDefault() { + return defaultValue; } - @Override - public final List<Label> getDefault() { - return labels; + /** + * The actual value for the attribute for the analysis phase, which depends on the build + * configuration. Note that configurations transitions are applied after the late-bound + * attribute was evaluated. + * + * @param rule the rule being evaluated + * @param attributes interface for retrieving the values of the rule's other attributes + * @param input the configuration fragment to evaluate with + */ + public ValueT resolve(Rule rule, AttributeMap attributes, FragmentT input) { + return resolver.resolve(rule, attributes, input); } - - @Override - public abstract List<Label> resolve(Rule rule, AttributeMap attributes, T configuration); } private final String name; @@ -1776,7 +1842,7 @@ public final class Attribute implements Comparable<Attribute> { "late bound attributes require a default value that is late bound (and vice versa): %s", name); if (isLateBound(name)) { - LateBoundDefault<?> lateBoundDefault = (LateBoundDefault<?>) defaultValue; + LateBoundDefault<?, ?> lateBoundDefault = (LateBoundDefault<?, ?>) defaultValue; Preconditions.checkArgument(!lateBoundDefault.useHostConfiguration() || (configTransition == ConfigurationTransition.HOST), "a late bound default value using the host configuration must use the host transition"); @@ -2045,8 +2111,8 @@ public final class Attribute implements Comparable<Attribute> { public Object getDefaultValue(Rule rule) { if (!getCondition().apply(rule == null ? null : NonconfigurableAttributeMapper.of(rule))) { return null; - } else if (defaultValue instanceof LateBoundDefault<?>) { - return ((LateBoundDefault<?>) defaultValue).getDefault(); + } else if (defaultValue instanceof LateBoundDefault<?, ?>) { + return ((LateBoundDefault<?, ?>) defaultValue).getDefault(); } else { return defaultValue; } @@ -2061,9 +2127,9 @@ public final class Attribute implements Comparable<Attribute> { return defaultValue; } - public LateBoundDefault<?> getLateBoundDefault() { + public LateBoundDefault<?, ?> getLateBoundDefault() { Preconditions.checkState(isLateBound()); - return (LateBoundDefault<?>) defaultValue; + return (LateBoundDefault<?, ?>) defaultValue; } /** |