aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
diff options
context:
space:
mode:
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.java278
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;
}
/**