diff options
author | 2018-02-02 15:52:22 -0800 | |
---|---|---|
committer | 2018-02-02 15:53:55 -0800 | |
commit | a56a6adf7c5d2829ea99f393f1a2d2d3d4488e0f (patch) | |
tree | 2a7ec0955afd09a9fb1d39801b85dca6c2567bba /src/main/java/com/google/devtools/build/lib/packages/Attribute.java | |
parent | 64d9a4d6dcd720a3b7a60ff550a17a7707dd41d0 (diff) |
Stop allowing generic LateBoundDefault value types. Such types are always either a Label or a List<Label>. We can easily enforce this through static type checking, so do it.
This will help with LateBoundDefault serialization, since we don't have to serialize an arbitrary object.
PiperOrigin-RevId: 184347100
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 | 261 |
1 files changed, 157 insertions, 104 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 d6b1cebef1..75ff0f3332 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 @@ -32,6 +32,7 @@ import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition; import com.google.devtools.build.lib.analysis.config.transitions.NoTransition; import com.google.devtools.build.lib.analysis.config.transitions.SplitTransition; +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.packages.RuleClass.Builder.RuleClassNamePredicate; @@ -1501,7 +1502,7 @@ public final class Attribute implements Comparable<Attribute> { } } - private static final class SimpleLateBoundDefault<FragmentT, ValueT> + private static class SimpleLateBoundDefault<FragmentT, ValueT> extends LateBoundDefault<FragmentT, ValueT> { private final Resolver<FragmentT, ValueT> resolver; @@ -1523,8 +1524,8 @@ public final class Attribute implements Comparable<Attribute> { // TODO(b/65746853): Remove documentation about accepting BuildConfiguration when uses are cleaned // up. /** - * Provider of values for late-bound attributes. See - * {@link Attribute#value(LateBoundDefault<?, ? extends TYPE> value)}. + * 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. @@ -1532,7 +1533,8 @@ public final class Attribute implements Comparable<Attribute> { * @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. + * @param <ValueT> The type of value returned by this class. Must be either {@link Void}, a {@link + * Label}, or a {@link List} of {@link Label} objects. */ @Immutable public abstract static class LateBoundDefault<FragmentT, ValueT> { @@ -1551,107 +1553,18 @@ public final class Attribute implements Comparable<Attribute> { private final Class<FragmentT> fragmentClass; /** - * 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 com.google.devtools.build.lib.analysis.config.HostTransition}. - * 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. - */ - 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 SimpleLateBoundDefault<>(false, fragmentClass, defaultValue, resolver); - } - - /** - * Creates a new LateBoundDefault which uses the rule, its configured attributes, and a fragment - * of the host configuration. - * - * <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 com.google.devtools.build.lib.analysis.config.HostTransition}. - * - * <p>This method only affects the configuration fragment passed to {@link #resolve}. You must - * also use {@link com.google.devtools.build.lib.analysis.config.HostTransition}, 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 SimpleLateBoundDefault<>(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. - */ - public static <ValueT> LateBoundDefault<Void, ValueT> fromRuleAndAttributesOnly( - ValueT defaultValue, Resolver<Void, ValueT> resolver) { - return new SimpleLateBoundDefault<>(false, Void.class, defaultValue, resolver); - } - - /** * 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 SimpleLateBoundDefault<>( - false, Void.class, defaultValue, (rule, attributes, unused) -> defaultValue); + @VisibleForTesting + public static LabelLateBoundDefault<Void> fromConstantForTesting(Label defaultValue) { + return new LabelLateBoundDefault<Void>( + false, + Void.class, + Preconditions.checkNotNull(defaultValue), + (rule, attributes, unused) -> defaultValue) {}; } /** @@ -1662,12 +1575,9 @@ public final class Attribute implements Comparable<Attribute> { */ @SuppressWarnings("unchecked") // bivariant implementation public static <ValueT> LateBoundDefault<Void, ValueT> alwaysNull() { - return (LateBoundDefault<Void, ValueT>) ALWAYS_NULL; + return (LateBoundDefault<Void, ValueT>) AlwaysNullLateBoundDefault.INSTANCE; } - private static final LateBoundDefault<Void, Void> ALWAYS_NULL = - new SimpleLateBoundDefault<>(false, Void.class, null, (rule, attributes, unused) -> null); - protected LateBoundDefault( boolean useHostConfiguration, Class<FragmentT> fragmentClass, @@ -1716,6 +1626,149 @@ public final class Attribute implements Comparable<Attribute> { public abstract ValueT resolve(Rule rule, AttributeMap attributes, FragmentT input); } + /** + * An abstract {@link LateBoundDefault} class so that {@code SkylarkLateBoundDefault} can derive + * from {@link LateBoundDefault} without compromising the type-safety of the second generic + * parameter to {@link LateBoundDefault}. + */ + public abstract static class AbstractLabelLateBoundDefault<FragmentT> + extends LateBoundDefault<FragmentT, Label> { + protected AbstractLabelLateBoundDefault( + boolean useHostConfiguration, Class<FragmentT> fragmentClass, Label defaultValue) { + super(useHostConfiguration, fragmentClass, defaultValue); + } + } + + private static class AlwaysNullLateBoundDefault extends SimpleLateBoundDefault<Void, Void> { + static final AlwaysNullLateBoundDefault INSTANCE = new AlwaysNullLateBoundDefault(); + + private AlwaysNullLateBoundDefault() { + super(false, Void.class, null, (rule, attributes, unused) -> null); + } + } + + /** A {@link LateBoundDefault} for a {@link Label}. */ + public static class LabelLateBoundDefault<FragmentT> + extends SimpleLateBoundDefault<FragmentT, Label> { + private LabelLateBoundDefault( + boolean useHostConfiguration, + Class<FragmentT> fragmentClass, + Label defaultValue, + Resolver<FragmentT, Label> resolver) { + super(useHostConfiguration, fragmentClass, defaultValue, resolver); + } + + /** + * Creates a new LabelLateBoundDefault 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 LabelLateBoundDefault 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 or {@link + * LabelListLateBoundDefault#fromTargetConfiguration}. 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 com.google.devtools.build.lib.analysis.config.HostTransition}. 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 + * LabelLateBoundDefault#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 + * LabelListLateBoundDefault#fromRuleAndAttributesOnly} 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 #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 {@link Label} 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> LabelLateBoundDefault<FragmentT> fromTargetConfiguration( + Class<FragmentT> fragmentClass, Label defaultValue, Resolver<FragmentT, Label> resolver) { + Preconditions.checkArgument( + !fragmentClass.equals(Void.class), + "Use fromRuleAndAttributesOnly to specify a LateBoundDefault which does not use " + + "configuration."); + return new LabelLateBoundDefault<>(false, fragmentClass, defaultValue, resolver); + } + + /** + * Creates a new LateBoundDefault which uses the rule, its configured attributes, and a fragment + * of the host configuration. + * + * <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 + * com.google.devtools.build.lib.analysis.config.HostTransition}. + * + * <p>This method only affects the configuration fragment passed to {@link #resolve}. You must + * also use {@link com.google.devtools.build.lib.analysis.config.HostTransition}, 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 {@link Label} 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> LabelLateBoundDefault<FragmentT> fromHostConfiguration( + Class<FragmentT> fragmentClass, Label defaultValue, Resolver<FragmentT, Label> resolver) { + Preconditions.checkArgument( + !fragmentClass.equals(Void.class), + "Use fromRuleAndAttributesOnly to specify a LateBoundDefault which does not use " + + "configuration."); + return new LabelLateBoundDefault<>(true, fragmentClass, defaultValue, resolver); + } + } + + /** A {@link LateBoundDefault} for a {@link List} of {@link Label} objects. */ + public static class LabelListLateBoundDefault<FragmentT> + extends SimpleLateBoundDefault<FragmentT, List<Label>> { + private LabelListLateBoundDefault( + boolean useHostConfiguration, + Class<FragmentT> fragmentClass, + Resolver<FragmentT, List<Label>> resolver) { + super(useHostConfiguration, fragmentClass, ImmutableList.of(), resolver); + } + + public static <FragmentT> LabelListLateBoundDefault<FragmentT> fromTargetConfiguration( + Class<FragmentT> fragmentClass, Resolver<FragmentT, List<Label>> resolver) { + Preconditions.checkArgument( + !fragmentClass.equals(Void.class), + "Use fromRuleAndAttributesOnly to specify a LateBoundDefault which does not use " + + "configuration."); + return new LabelListLateBoundDefault<>(false, fragmentClass, resolver); + } + + /** + * Creates a new LabelListLateBoundDefault 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 resolver A function which will compute the actual value with the configuration. + */ + public static LabelListLateBoundDefault<Void> fromRuleAndAttributesOnly( + Resolver<Void, List<Label>> resolver) { + return new LabelListLateBoundDefault<>(false, Void.class, resolver); + } + } + private final String name; private final Type<?> type; |