// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.packages; import static com.google.common.collect.Sets.newEnumSet; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Verify; 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.analysis.config.transitions.ConfigurationTransition; import com.google.devtools.build.lib.analysis.config.transitions.NoTransition; import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition; 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; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; import com.google.devtools.build.lib.skylarkbuildapi.SplitTransitionProviderApi; 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; import com.google.devtools.build.lib.syntax.Type.LabelClass; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.FileTypeSet; import com.google.devtools.build.lib.util.StringUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** * Metadata of a rule attribute. Contains the attribute name and type, and an default value to be * used if none is provided in a rule declaration in a BUILD file. Attributes are immutable, and may * be shared by more than one rule (for example, foo_binary and foo_library * may share many attributes in common). */ @Immutable @AutoCodec public final class Attribute implements Comparable { public static final RuleClassNamePredicate ANY_RULE = RuleClassNamePredicate.unspecified(); public static final RuleClassNamePredicate NO_RULE = RuleClassNamePredicate.only(); /** Wraps the information necessary to construct an Aspect. */ @VisibleForSerialization abstract static class RuleAspect { protected final C aspectClass; protected final Function parametersExtractor; protected RuleAspect(C aspectClass, Function parametersExtractor) { this.aspectClass = aspectClass; this.parametersExtractor = parametersExtractor; } public String getName() { return this.aspectClass.getName(); } public ImmutableSet getRequiredParameters() { return ImmutableSet.of(); } public abstract Aspect getAspect(Rule rule); public C getAspectClass() { return aspectClass; } } private static class NativeRuleAspect extends RuleAspect { public NativeRuleAspect(NativeAspectClass aspectClass, Function parametersExtractor) { super(aspectClass, parametersExtractor); } @Override public Aspect getAspect(Rule rule) { AspectParameters params = parametersExtractor.apply(rule); return params == null ? null : Aspect.forNative(aspectClass, params); } } @AutoCodec static class SkylarkRuleAspect extends RuleAspect { private final SkylarkDefinedAspect aspect; public SkylarkRuleAspect(SkylarkDefinedAspect aspect) { super(aspect.getAspectClass(), aspect.getDefaultParametersExtractor()); this.aspect = aspect; } @Override public ImmutableSet getRequiredParameters() { return aspect.getParamAttributes(); } @Override public Aspect getAspect(Rule rule) { AspectParameters parameters = parametersExtractor.apply(rule); return Aspect.forSkylark(aspectClass, aspect.getDefinition(parameters), parameters); } } /** A RuleAspect that just wraps a pre-existing Aspect that doesn't vary with the Rule. */ private static class PredefinedRuleAspect extends RuleAspect { private final Aspect aspect; public PredefinedRuleAspect(Aspect aspect) { super(aspect.getAspectClass(), null); this.aspect = aspect; } @Override public Aspect getAspect(Rule rule) { return aspect; } } @VisibleForSerialization enum PropertyFlag { MANDATORY, EXECUTABLE, UNDOCUMENTED, TAGGABLE, /** * Whether the list attribute is order-independent and can be sorted. */ ORDER_INDEPENDENT, /** * Whether the allowedRuleClassesForLabels or allowedFileTypesForLabels are * set to custom values. If so, and the attribute is called "deps", the * legacy deps checking is skipped, and the new stricter checks are used * instead. For non-"deps" attributes, this allows skipping the check if it * would pass anyway, as the default setting allows any rule classes and * file types. */ STRICT_LABEL_CHECKING, /** * Set for things that would cause the a compile or lint-like action to * be executed when the input changes. Used by compile_one_dependency. * Set for attributes like hdrs and srcs on cc_ rules or srcs on java_ * or py_rules. Generally not set on data/resource attributes. */ DIRECT_COMPILE_TIME_INPUT, /** * Whether the value of the list type attribute must not be an empty list. */ NON_EMPTY, /** * Verifies that the referenced rule produces a single artifact. Note that this check happens * on a per label basis, i.e. the check happens separately for every label in a label list. */ SINGLE_ARTIFACT, /** * Whether we perform silent ruleclass filtering of the dependencies of the label type * attribute according to their rule classes. I.e. elements of the list which don't match the * allowedRuleClasses predicate or not rules will be filtered out without throwing any errors. * This flag is introduced to handle plugins, do not use it in other cases. */ SILENT_RULECLASS_FILTER, // TODO(bazel-team): This is a hack introduced because of the bad design of the original rules. // Depot cleanup would be too expensive, but don't migrate this to Skylark. /** * Whether to perform analysis time filetype check on this label-type attribute or not. * If the flag is set, we skip the check that applies the allowedFileTypes filter * to generated files. Do not use this if avoidable. */ SKIP_ANALYSIS_TIME_FILETYPE_CHECK, /** * Whether the value of the attribute should come from a given set of values. */ CHECK_ALLOWED_VALUES, /** * Whether this attribute is opted out of "configurability", i.e. the ability to determine * its value based on properties of the build configuration. */ NONCONFIGURABLE, /** * Whether we should skip dependency validation checks done by * {@link com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.PrerequisiteValidator} * (for visibility, etc.). */ SKIP_PREREQ_VALIDATOR_CHECKS, /** * Whether we should check constraints on this attribute even if default enforcement policy * would skip it. See * {@link com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics} for more on * constraints. */ CHECK_CONSTRAINTS_OVERRIDE, /** * Whether we should skip constraints checking on this attribute even if default enforcement * policy would check it. */ SKIP_CONSTRAINTS_OVERRIDE, /** * Whether we should use output_licenses to check the licences on this attribute. */ OUTPUT_LICENSES, } // TODO(bazel-team): modify this interface to extend Predicate and have an extra error // message function like AllowedValues does /** * A predicate-like class that determines whether an edge between two rules is valid or not. */ public interface ValidityPredicate { /** * This method should return null if the edge is valid, or a suitable error message * if it is not. Note that warnings are not supported. */ String checkValid(Rule from, Rule to); } @AutoCodec public static final ValidityPredicate ANY_EDGE = new ValidityPredicate() { @Override public String checkValid(Rule from, Rule to) { return null; } }; /** * Provides a {@link SplitTransition} given the originating target {@link Rule}. The split * transition may be constant for all instances of the originating rule, or it may differ * based on attributes of that rule. For instance, a split transition on a rule's deps may differ * depending on the 'platform' attribute of the rule. */ public interface SplitTransitionProvider extends SplitTransitionProviderApi { /** * Returns the {@link SplitTransition} given the attribute mapper of the originating rule. */ SplitTransition apply(AttributeMap attributeMap); } /** * Implementation of {@link SplitTransitionProvider} that returns a single {@link SplitTransition} * regardless of the originating rule. */ @AutoCodec.VisibleForSerialization @AutoCodec static class BasicSplitTransitionProvider implements SplitTransitionProvider { private final SplitTransition splitTransition; @AutoCodec.VisibleForSerialization BasicSplitTransitionProvider(SplitTransition splitTransition) { this.splitTransition = splitTransition; } @Override public SplitTransition apply(AttributeMap attributeMap) { return splitTransition; } } /** A predicate class to check if the value of the attribute comes from a predefined set. */ @AutoCodec public static class AllowedValueSet implements PredicateWithMessage { private final Set allowedValues; public AllowedValueSet(T... values) { this(Arrays.asList(values)); } public AllowedValueSet(Iterable values) { Preconditions.checkNotNull(values); Preconditions.checkArgument(!Iterables.isEmpty(values)); allowedValues = ImmutableSet.copyOf(values); } @AutoCodec.Instantiator @VisibleForSerialization AllowedValueSet(Set allowedValues) { this.allowedValues = allowedValues; } @Override public boolean apply(Object input) { return allowedValues.contains(input); } @Override public String getErrorReason(Object value) { return String.format("has to be one of %s instead of '%s'", StringUtil.joinEnglishList(allowedValues, "or", "'"), value); } @VisibleForTesting public Collection getAllowedValues() { return allowedValues; } } public ImmutableMap> getRequiredAspectParameters() { ImmutableMap.Builder> paramBuilder = ImmutableMap.builder(); for (RuleAspect aspect : aspects) { paramBuilder.put(aspect.getName(), aspect.getRequiredParameters()); } return paramBuilder.build(); } /** * Creates a new attribute builder. * * @param name attribute name * @param type attribute type * @return attribute builder * * @param attribute type class */ public static Attribute.Builder attr(String name, Type type) { return new Builder<>(name, type); } /** A factory to generate {@link Attribute} instances. */ @AutoCodec public static class ImmutableAttributeFactory { private final Type type; private final ConfigurationTransition configTransition; private final RuleClassNamePredicate allowedRuleClassesForLabels; private final RuleClassNamePredicate allowedRuleClassesForLabelsWarning; private final SplitTransitionProvider splitTransitionProvider; private final FileTypeSet allowedFileTypesForLabels; private final ValidityPredicate validityPredicate; private final Object value; private final AttributeValueSource valueSource; private final boolean valueSet; private final Predicate condition; private final ImmutableSet propertyFlags; private final PredicateWithMessage allowedValues; private final RequiredProviders requiredProviders; private final ImmutableList> aspects; @AutoCodec.VisibleForSerialization ImmutableAttributeFactory( Type type, ImmutableSet propertyFlags, Object value, ConfigurationTransition configTransition, RuleClassNamePredicate allowedRuleClassesForLabels, RuleClassNamePredicate allowedRuleClassesForLabelsWarning, SplitTransitionProvider splitTransitionProvider, FileTypeSet allowedFileTypesForLabels, ValidityPredicate validityPredicate, AttributeValueSource valueSource, boolean valueSet, Predicate condition, PredicateWithMessage allowedValues, RequiredProviders requiredProviders, ImmutableList> aspects) { this.type = type; this.configTransition = configTransition; this.allowedRuleClassesForLabels = allowedRuleClassesForLabels; this.allowedRuleClassesForLabelsWarning = allowedRuleClassesForLabelsWarning; this.splitTransitionProvider = splitTransitionProvider; this.allowedFileTypesForLabels = allowedFileTypesForLabels; this.validityPredicate = validityPredicate; this.value = value; this.valueSource = valueSource; this.valueSet = valueSet; this.condition = condition; this.propertyFlags = propertyFlags; this.allowedValues = allowedValues; this.requiredProviders = requiredProviders; this.aspects = aspects; } public AttributeValueSource getValueSource() { return valueSource; } public boolean isValueSet() { return valueSet; } public Attribute build(String name) { Preconditions.checkState(!name.isEmpty(), "name has not been set"); if (valueSource == AttributeValueSource.LATE_BOUND) { Preconditions.checkState(isLateBound(name)); } // TODO(bazel-team): Set the default to be no file type, then remove this check, and also // remove all allowedFileTypes() calls without parameters. // do not modify this.allowedFileTypesForLabels, instead create a copy. FileTypeSet allowedFileTypesForLabels = this.allowedFileTypesForLabels; if (type.getLabelClass() == LabelClass.DEPENDENCY) { if (isPrivateAttribute(name) && allowedFileTypesForLabels == null) { allowedFileTypesForLabels = FileTypeSet.ANY_FILE; } Preconditions.checkNotNull( allowedFileTypesForLabels, "allowedFileTypesForLabels not set for %s", name); } else if (type.getLabelClass() == LabelClass.OUTPUT) { // TODO(bazel-team): Set the default to no file type and make explicit calls instead. if (allowedFileTypesForLabels == null) { allowedFileTypesForLabels = FileTypeSet.ANY_FILE; } } return new Attribute( name, type, propertyFlags, value, configTransition, splitTransitionProvider, allowedRuleClassesForLabels, allowedRuleClassesForLabelsWarning, allowedFileTypesForLabels, validityPredicate, condition, allowedValues, requiredProviders, aspects); } } /** * A fluent builder for the {@code Attribute} instances. * *

All methods could be called only once per builder. The attribute * already undocumented based on its name cannot be marked as undocumented. */ public static class Builder { private final String name; private final Type type; private ConfigurationTransition configTransition = NoTransition.INSTANCE; private RuleClassNamePredicate allowedRuleClassesForLabels = ANY_RULE; private RuleClassNamePredicate allowedRuleClassesForLabelsWarning = NO_RULE; private SplitTransitionProvider splitTransitionProvider; private FileTypeSet allowedFileTypesForLabels; private ValidityPredicate validityPredicate = ANY_EDGE; private Object value; private AttributeValueSource valueSource = AttributeValueSource.DIRECT; private boolean valueSet; private Predicate condition; private Set propertyFlags = EnumSet.noneOf(PropertyFlag.class); private PredicateWithMessage allowedValues = null; private RequiredProviders.Builder requiredProvidersBuilder = RequiredProviders.acceptAnyBuilder(); private HashMap> aspects = new LinkedHashMap<>(); /** * Creates an attribute builder with given name and type. This attribute is optional, uses * target configuration and has a default value the same as its type default value. This * attribute will be marked as undocumented if its name starts with the dollar sign ({@code $}) * or colon ({@code :}). * * @param name attribute name * @param type attribute type */ public Builder(String name, Type type) { this.name = Preconditions.checkNotNull(name); this.type = Preconditions.checkNotNull(type); if (isImplicit(name) || isLateBound(name)) { setPropertyFlag(PropertyFlag.UNDOCUMENTED, "undocumented"); } } private Builder setPropertyFlag(PropertyFlag flag, String propertyName) { Preconditions.checkState( !propertyFlags.contains(flag), "%s flag is already set", propertyName); propertyFlags.add(flag); return this; } /** * Sets the property flag of the corresponding name if exists, otherwise throws an Exception. * Only meant to use from Skylark, do not use from Java. */ public Builder setPropertyFlag(String propertyName) { PropertyFlag flag = null; try { flag = PropertyFlag.valueOf(propertyName); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("unknown attribute flag " + propertyName); } setPropertyFlag(flag, propertyName); return this; } /** * Makes the built attribute mandatory. */ public Builder mandatory() { return setPropertyFlag(PropertyFlag.MANDATORY, "mandatory"); } /** * Makes the built attribute non empty, meaning the attribute cannot have an empty list value. * Only applicable for list type attributes. */ public Builder nonEmpty() { Preconditions.checkNotNull(type.getListElementType(), "attribute '%s' must be a list", name); return setPropertyFlag(PropertyFlag.NON_EMPTY, "non_empty"); } /** * Makes the built attribute producing a single artifact. */ public Builder singleArtifact() { Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "attribute '%s' must be a label-valued type", name); return setPropertyFlag(PropertyFlag.SINGLE_ARTIFACT, "single_artifact"); } /** * Forces silent ruleclass filtering on the label type attribute. * This flag is introduced to handle plugins, do not use it in other cases. */ public Builder silentRuleClassFilter() { Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "must be a label-valued type"); return setPropertyFlag(PropertyFlag.SILENT_RULECLASS_FILTER, "silent_ruleclass_filter"); } /** * Skip analysis time filetype check. Don't use it if avoidable. */ public Builder skipAnalysisTimeFileTypeCheck() { Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "must be a label-valued type"); return setPropertyFlag(PropertyFlag.SKIP_ANALYSIS_TIME_FILETYPE_CHECK, "skip_analysis_time_filetype_check"); } /** * Mark the built attribute as order-independent. */ public Builder orderIndependent() { Preconditions.checkNotNull(type.getListElementType(), "attribute '%s' must be a list", name); return setPropertyFlag(PropertyFlag.ORDER_INDEPENDENT, "order-independent"); } /** * Mark the built attribute as to use output_licenses for license checking. */ public Builder useOutputLicenses() { Preconditions.checkState(BuildType.isLabelType(type), "must be a label type"); return setPropertyFlag(PropertyFlag.OUTPUT_LICENSES, "output_license"); } /** * Defines the configuration transition for this attribute. */ public Builder cfg(SplitTransitionProvider splitTransitionProvider) { Preconditions.checkState(this.configTransition == NoTransition.INSTANCE, "the configuration transition is already set"); this.splitTransitionProvider = Preconditions.checkNotNull(splitTransitionProvider); return this; } /** * Defines the configuration transition for this attribute (e.g. a {@link PatchTransition} or * {@link SplitTransition}). Defaults to {@code NONE}. */ public Builder cfg(ConfigurationTransition configTransition) { Preconditions.checkNotNull(configTransition); Preconditions.checkState(this.configTransition == NoTransition.INSTANCE, "the configuration transition is already set"); if (configTransition instanceof SplitTransition) { return cfg(new BasicSplitTransitionProvider((SplitTransition) configTransition)); } else { this.configTransition = configTransition; return this; } } /** * Requires the attribute target to be executable; only for label or label * list attributes. Defaults to {@code false}. */ public Builder exec() { return setPropertyFlag(PropertyFlag.EXECUTABLE, "executable"); } /** * Indicates that the attribute (like srcs or hdrs) should be used as an input when calculating * compile_one_dependency. */ public Builder direct_compile_time_input() { return setPropertyFlag(PropertyFlag.DIRECT_COMPILE_TIME_INPUT, "direct_compile_time_input"); } /** * Makes the built attribute undocumented. * * @param reason explanation why the attribute is undocumented. This is not * used but required for documentation */ public Builder undocumented(String reason) { return setPropertyFlag(PropertyFlag.UNDOCUMENTED, "undocumented"); } /** * Sets the attribute default value. The type of the default value must * match the type parameter. (e.g. list=[], integer=0, string="", * label=null). The {@code defaultValue} must be immutable. * *

If defaultValue is of type Label and is a target, that target will * become an implicit dependency of the 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(TYPE defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); value = defaultValue; valueSet = true; return this; } /** * See value(TYPE) above. This method is only meant for Skylark usage. * *

The parameter {@code context} is relevant iff the default value is a Label string. In this * case, {@code context} must point to the parent Label in order to be able to convert the * default value string to a proper Label. * * @param parameterName The name of the attribute to use in error messages */ public Builder defaultValue( Object defaultValue, Object context, @Nullable String parameterName) throws ConversionException { Preconditions.checkState(!valueSet, "the default value is already set"); value = type.convert( defaultValue, ((parameterName == null) ? "" : String.format("parameter '%s' of ", parameterName)) + String.format("attribute '%s'", name), context); valueSet = true; return this; } /** See value(TYPE) above. This method is only meant for Skylark usage. */ public Builder defaultValue(Object defaultValue) throws ConversionException { return defaultValue(defaultValue, null, null); } public boolean isValueSet() { return valueSet; } /** * 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. */ public Builder value(ComputedDefault defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); value = defaultValue; valueSource = AttributeValueSource.COMPUTED_DEFAULT; valueSet = true; 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 and/or the rule's configured attributes. */ public Builder value(LateBoundDefault defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); value = defaultValue; valueSource = AttributeValueSource.LATE_BOUND; valueSet = true; return this; } /** * Returns where the value of this attribute comes from. Useful only for Skylark. */ public AttributeValueSource getValueSource() { return valueSource; } /** * Sets a condition predicate. The default value of the attribute only applies if the condition * evaluates to true. If the value is explicitly provided, then this condition is ignored. * *

The condition is only evaluated if the attribute is not explicitly set, and after all * explicit attributes have been set. It can generally not access default values of other * attributes. */ public Builder condition(Predicate condition) { Preconditions.checkState(this.condition == null, "the condition is already set"); this.condition = condition; return this; } /** * Switches on the capability of an attribute to be published to the rule's * tag set. */ public Builder taggable() { return setPropertyFlag(PropertyFlag.TAGGABLE, "taggable"); } /** * Disables dependency checks done by * {@link com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.PrerequisiteValidator}. */ public Builder skipPrereqValidatorCheck() { return setPropertyFlag(PropertyFlag.SKIP_PREREQ_VALIDATOR_CHECKS, "skip_prereq_validator_checks"); } /** * Enforces constraint checking on this attribute even if default enforcement policy would skip * it. If default policy checks the attribute, this is a no-op. * *

Most attributes are enforced by default, so in the common case this call is unnecessary. * *

See {@link com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics#getConstraintCheckedDependencies} * for enforcement policy details. */ public Builder checkConstraints() { Verify.verify(!propertyFlags.contains(PropertyFlag.SKIP_CONSTRAINTS_OVERRIDE), "constraint checking is already overridden to be skipped"); return setPropertyFlag(PropertyFlag.CHECK_CONSTRAINTS_OVERRIDE, "check_constraints"); } /** * Skips constraint checking on this attribute even if default enforcement policy would check * it. If default policy skips the attribute, this is a no-op. * *

See {@link com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics#getConstraintCheckedDependencies} * for enforcement policy details. */ public Builder dontCheckConstraints() { Verify.verify(!propertyFlags.contains(PropertyFlag.CHECK_CONSTRAINTS_OVERRIDE), "constraint checking is already overridden to be checked"); return setPropertyFlag(PropertyFlag.SKIP_CONSTRAINTS_OVERRIDE, "dont_check_constraints"); } /** * If this is a label or label-list attribute, then this sets the allowed rule types for the * labels occurring in the attribute. * *

If the attribute contains Labels of any other rule type, then if they're in * {@link #allowedRuleClassesForLabelsWarning}, the build continues with a warning. Else if * they fulfill {@link #getMandatoryNativeProvidersList()}, the build continues without error. * Else the build fails during analysis. * *

If neither this nor {@link #allowedRuleClassesForLabelsWarning} is set, only rules that * fulfill {@link #getMandatoryNativeProvidersList()} build without error. * *

This only works on a per-target basis, not on a per-file basis; with other words, it * works for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedRuleClasses(Iterable allowedRuleClasses) { return allowedRuleClasses( RuleClassNamePredicate.only(allowedRuleClasses)); } /** * If this is a label or label-list attribute, then this sets the allowed rule types for the * labels occurring in the attribute. * *

If the attribute contains Labels of any other rule type, then if they're in * {@link #allowedRuleClassesForLabelsWarning}, the build continues with a warning. Else if * they fulfill {@link #getMandatoryNativeProvidersList()}, the build continues without error. * Else the build fails during analysis. * *

If neither this nor {@link #allowedRuleClassesForLabelsWarning} is set, only rules that * fulfill {@link #getMandatoryNativeProvidersList()} build without error. * *

This only works on a per-target basis, not on a per-file basis; with other words, it * works for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedRuleClasses(RuleClassNamePredicate allowedRuleClasses) { Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "must be a label-valued type"); propertyFlags.add(PropertyFlag.STRICT_LABEL_CHECKING); allowedRuleClassesForLabels = allowedRuleClasses; return this; } /** * If this is a label or label-list attribute, then this sets the allowed rule types for the * labels occurring in the attribute. * *

If the attribute contains Labels of any other rule type, then if they're in * {@link #allowedRuleClassesForLabelsWarning}, the build continues with a warning. Else if * they fulfill {@link #getMandatoryNativeProvidersList()}, the build continues without error. * Else the build fails during analysis. * *

If neither this nor {@link #allowedRuleClassesForLabelsWarning} is set, only rules that * fulfill {@link #getMandatoryNativeProvidersList()} build without error. * *

This only works on a per-target basis, not on a per-file basis; with other words, it * works for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedRuleClasses(String... allowedRuleClasses) { return allowedRuleClasses(ImmutableSet.copyOf(allowedRuleClasses)); } /** * If this is a label or label-list attribute, then this sets the allowed * file types for file labels occurring in the attribute. If the attribute * contains labels that correspond to files of any other type, then an error * is produced during the analysis phase. * *

This only works on a per-target basis, not on a per-file basis; with * other words, it works for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedFileTypes(FileTypeSet allowedFileTypes) { Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "must be a label-valued type"); propertyFlags.add(PropertyFlag.STRICT_LABEL_CHECKING); allowedFileTypesForLabels = Preconditions.checkNotNull(allowedFileTypes); return this; } /** * Allow all files for legacy compatibility. All uses of this method should be audited and then * removed. In some cases, it's correct to allow any file, but mostly the set of files should be * restricted to a reasonable set. */ public Builder legacyAllowAnyFileType() { return allowedFileTypes(FileTypeSet.ANY_FILE); } /** * If this is a label or label-list attribute, then this sets the allowed * file types for file labels occurring in the attribute. If the attribute * contains labels that correspond to files of any other type, then an error * is produced during the analysis phase. * *

This only works on a per-target basis, not on a per-file basis; with * other words, it works for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedFileTypes(FileType... allowedFileTypes) { return allowedFileTypes(FileTypeSet.of(allowedFileTypes)); } /** * If this is a label or label-list attribute, then this sets the allowed rule types with * warning for the labels occurring in the attribute. This must be a disjoint set from * {@link #allowedRuleClasses}. * *

If the attribute contains Labels of any other rule type (other than this or those set in * allowedRuleClasses()) and they fulfill {@link #getMandatoryNativeProvidersList()}}, the build * continues without error. Else the build fails during analysis. * *

If neither this nor {@link #allowedRuleClassesForLabels} is set, only rules that * fulfill {@link #getMandatoryNativeProvidersList()} build without error. * *

This only works on a per-target basis, not on a per-file basis; with other words, it * works for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedRuleClassesWithWarning(Collection allowedRuleClasses) { return allowedRuleClassesWithWarning( RuleClassNamePredicate.only(allowedRuleClasses)); } /** * If this is a label or label-list attribute, then this sets the allowed rule types with * warning for the labels occurring in the attribute. This must be a disjoint set from * {@link #allowedRuleClasses}. * *

If the attribute contains Labels of any other rule type (other than this or those set in * allowedRuleClasses()) and they fulfill {@link #getMandatoryNativeProvidersList()}}, the build * continues without error. Else the build fails during analysis. * *

If neither this nor {@link #allowedRuleClassesForLabels} is set, only rules that * fulfill {@link #getMandatoryNativeProvidersList()} build without error. * *

This only works on a per-target basis, not on a per-file basis; with other words, it * works for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedRuleClassesWithWarning(RuleClassNamePredicate allowedRuleClasses) { Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "must be a label-valued type"); propertyFlags.add(PropertyFlag.STRICT_LABEL_CHECKING); allowedRuleClassesForLabelsWarning = allowedRuleClasses; return this; } /** * If this is a label or label-list attribute, then this sets the allowed rule types with * warning for the labels occurring in the attribute. This must be a disjoint set from {@link * #allowedRuleClasses}. * *

If the attribute contains Labels of any other rule type (other than this or those set in * allowedRuleClasses()) and they fulfill {@link #getRequiredProviders()}}, the build continues * without error. Else the build fails during analysis. * *

If neither this nor {@link #allowedRuleClassesForLabels} is set, only rules that fulfill * {@link #getRequiredProviders()} build without error. * *

This only works on a per-target basis, not on a per-file basis; with other words, it works * for 'deps' attributes, but not 'srcs' attributes. */ public Builder allowedRuleClassesWithWarning(String... allowedRuleClasses) { return allowedRuleClassesWithWarning(ImmutableSet.copyOf(allowedRuleClasses)); } /** * Sets a list of lists of mandatory native providers. Every configured target occurring in this * label type attribute has to provide all the providers from one of those lists, otherwise an * error is produced during the analysis phase. */ public final Builder mandatoryNativeProvidersList( Iterable>> providersList) { Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "must be a label-valued type"); for (Iterable> providers : providersList) { this.requiredProvidersBuilder.addNativeSet(ImmutableSet.copyOf(providers)); } return this; } public Builder mandatoryNativeProviders( Iterable> providers) { if (providers.iterator().hasNext()) { mandatoryNativeProvidersList(ImmutableList.of(providers)); } return this; } /** * Sets a list of sets of mandatory Skylark providers. Every configured target occurring in * this label type attribute has to provide all the providers from one of those sets, * or be one of {@link #allowedRuleClasses}, otherwise an error is produced during * the analysis phase. */ public Builder mandatoryProvidersList( Iterable> providersList){ Preconditions.checkState(type.getLabelClass() == LabelClass.DEPENDENCY, "must be a label-valued type"); for (Iterable providers : providersList) { this.requiredProvidersBuilder.addSkylarkSet(ImmutableSet.copyOf(providers)); } return this; } public Builder legacyMandatoryProviders(String... ids) { return mandatoryProviders( Iterables.transform( Arrays.asList(ids), s -> { Preconditions.checkNotNull(s); return SkylarkProviderIdentifier.forLegacy(s); })); } public Builder mandatoryProviders(Iterable providers) { if (providers.iterator().hasNext()) { mandatoryProvidersList(ImmutableList.of(providers)); } return this; } public Builder mandatoryProviders(SkylarkProviderIdentifier... providers) { mandatoryProviders(Arrays.asList(providers)); return this; } /** * Asserts that a particular parameterized aspect probably needs to be computed for all direct * dependencies through this attribute. * * @param evaluator function that extracts aspect parameters from rule. If it returns null, * then the aspect will not be attached. */ public Builder aspect( NativeAspectClass aspect, Function evaluator) { NativeRuleAspect nativeRuleAspect = new NativeRuleAspect(aspect, evaluator); RuleAspect oldAspect = this.aspects.put(nativeRuleAspect.getName(), nativeRuleAspect); if (oldAspect != null) { throw new AssertionError( String.format("Aspect %s has already been added", oldAspect.getName())); } return this; } /** * Asserts that a particular parameterized aspect probably needs to be computed for all direct * dependencies through this attribute. */ public Builder aspect(NativeAspectClass aspect) { return this.aspect(aspect, EMPTY_FUNCTION); } @AutoCodec @AutoCodec.VisibleForSerialization static final Function EMPTY_FUNCTION = input -> AspectParameters.EMPTY; public Builder aspect(SkylarkDefinedAspect skylarkAspect, Location location) throws EvalException { SkylarkRuleAspect skylarkRuleAspect = new SkylarkRuleAspect(skylarkAspect); RuleAspect oldAspect = this.aspects.put(skylarkAspect.getName(), skylarkRuleAspect); if (oldAspect != null) { throw new EvalException( location, String.format("aspect %s added more than once", skylarkAspect.getName())); } return this; } /** * Should only be used for deserialization. */ public Builder aspect(final Aspect aspect) { PredefinedRuleAspect predefinedRuleAspect = new PredefinedRuleAspect(aspect); RuleAspect oldAspect = this.aspects.put(predefinedRuleAspect.getName(), predefinedRuleAspect); if (oldAspect != null) { throw new AssertionError( String.format("Aspect %s has already been added", oldAspect.getName())); } return this; } /** * Sets the predicate-like edge validity checker. */ public Builder validityPredicate(ValidityPredicate validityPredicate) { propertyFlags.add(PropertyFlag.STRICT_LABEL_CHECKING); this.validityPredicate = validityPredicate; return this; } /** * The value of the attribute must be one of allowedValues. */ public Builder allowedValues(PredicateWithMessage allowedValues) { this.allowedValues = allowedValues; propertyFlags.add(PropertyFlag.CHECK_ALLOWED_VALUES); return this; } /** * Makes the built attribute "non-configurable", i.e. its value cannot be influenced by * the build configuration. Attributes are "configurable" unless explicitly opted out here. * *

Non-configurability indicates an exceptional state: there exists Blaze logic that needs * the attribute's value, has no access to configurations, and can't apply a workaround * through an appropriate {@link AbstractAttributeMapper} implementation. Scenarios like * this should be as uncommon as possible, so it's important we maintain clear documentation * on what causes them and why users consequently can't configure certain attributes. * * @param reason why this attribute can't be configurable. This isn't used by Blaze - it's * solely a documentation mechanism. */ public Builder nonconfigurable(String reason) { Preconditions.checkState(!reason.isEmpty()); return setPropertyFlag(PropertyFlag.NONCONFIGURABLE, "nonconfigurable"); } /** Returns an {@link ImmutableAttributeFactory} that can be invoked to create attributes. */ public ImmutableAttributeFactory buildPartial() { Preconditions.checkState( !allowedRuleClassesForLabels.consideredOverlapping(allowedRuleClassesForLabelsWarning), "allowedRuleClasses %s and allowedRuleClassesWithWarning %s " + "may not contain the same rule classes", allowedRuleClassesForLabels, allowedRuleClassesForLabelsWarning); return new ImmutableAttributeFactory( type, Sets.immutableEnumSet(propertyFlags), valueSet ? value : type.getDefaultValue(), configTransition, allowedRuleClassesForLabels, allowedRuleClassesForLabelsWarning, splitTransitionProvider, allowedFileTypesForLabels, validityPredicate, valueSource, valueSet, condition, allowedValues, requiredProvidersBuilder.build(), ImmutableList.copyOf(aspects.values())); } /** * Creates the attribute. Uses name, type, optionality, configuration type and the default value * configured by the builder. */ public Attribute build() { return build(this.name); } /** * Creates the attribute. Uses type, optionality, configuration type and the default value * configured by the builder. Use the name passed as an argument. This function is used by * Skylark where the name is provided only when we build. We don't want to modify the builder, * as it is shared in a multithreaded environment. */ public Attribute build(String name) { return buildPartial().build(name); } } /** * A strategy for dealing with too many computations, used when creating lookup tables for {@link * ComputedDefault}s. * * @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. * *

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

    *
  • 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) *
* *

to: * *

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

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<>(depMaps.size()); 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: * *

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

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. */ public abstract static class ComputedDefault { private final ImmutableList dependencies; /** * Create a computed default that can read all non-configurable attribute values and no * configurable attribute values. */ public ComputedDefault() { this(ImmutableList.of()); } /** * Create a computed default that can read all non-configurable attributes values and one * explicitly specified configurable attribute value */ public ComputedDefault(String depAttribute) { this(ImmutableList.of(depAttribute)); } /** * Create a computed default that can read all non-configurable attributes values and two * 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()); } /** The list of configurable attributes this ComputedDefault declares it may read. */ public ImmutableList dependencies() { 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. */ @AutoCodec 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); // Order is important for #createDependencyAssignmentTuple. this.dependencies = Ordering.natural().immutableSortedCopy(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.getPublicName(), rule.getLabel()); final AtomicReference caughtEvalExceptionIfAny = new AtomicReference<>(); ComputationStrategy strategy = new ComputationStrategy() { @Override public Object compute(AttributeMap map) throws InterruptedException { try { return owner.computeValue(eventHandler, 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(EventHandler eventHandler, 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(eventHandler, attrValues); } private Object invokeCallback(EventHandler eventHandler, Map attrValues) throws EvalException, InterruptedException { ClassObject attrs = StructProvider.STRUCT.create( attrValues, "No such regular (non computed) attribute '%s'."); Object result = callback.call(eventHandler, 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. */ @AutoCodec static final class SkylarkComputedDefault extends ComputedDefault { private final List> dependencyTypes; private final Map, Object> lookupTable; /** * Creates a new SkylarkComputedDefault containing a lookup table. * * @param dependencies 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 dependencies, ImmutableList> dependencyTypes, Map, Object> lookupTable) { super(Preconditions.checkNotNull(dependencies)); 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. Available keys: %s.", rule.getLabel(), Iterables.toString(key), Iterables.toString(lookupTable.keySet())); 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; } } @AutoCodec.VisibleForSerialization static class SimpleLateBoundDefault extends LateBoundDefault { @AutoCodec.VisibleForSerialization protected final Resolver resolver; private SimpleLateBoundDefault(boolean useHostConfiguration, Class fragmentClass, ValueT defaultValue, Resolver resolver) { super(useHostConfiguration, fragmentClass, defaultValue); this.resolver = resolver; } @Override public ValueT resolve(Rule rule, AttributeMap attributes, FragmentT input) { return resolver.resolve(rule, attributes, input); } } // 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 value)}. * *

Use sparingly - having different values for attributes during loading and analysis can * confuse users. * * @param 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 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 { /** * Functional interface for computing the value of a late-bound attribute. * *

Implementations of this interface must be immutable. */ @FunctionalInterface public interface Resolver { ValueT resolve(Rule rule, AttributeMap attributeMap, FragmentT input); } private final boolean useHostConfiguration; private final ValueT defaultValue; private final Class fragmentClass; /** * Creates a new LateBoundDefault which always returns the given value. * *

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. */ @VisibleForTesting public static LabelLateBoundDefault fromConstantForTesting(Label defaultValue) { return new LabelLateBoundDefault( false, Void.class, Preconditions.checkNotNull(defaultValue), (rule, attributes, unused) -> defaultValue) {}; } /** * Creates a new LateBoundDefault which always returns null. * *

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 LateBoundDefault alwaysNull() { return (LateBoundDefault) AlwaysNullLateBoundDefault.INSTANCE; } protected LateBoundDefault( boolean useHostConfiguration, Class fragmentClass, ValueT defaultValue) { this.useHostConfiguration = useHostConfiguration; this.defaultValue = defaultValue; this.fragmentClass = fragmentClass; } /** * 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 final boolean useHostConfiguration() { return useHostConfiguration; } /** * 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). * *

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

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 final Class getFragmentClass() { return fragmentClass; } /** The default value for the attribute that is set during the loading phase. */ public final ValueT getDefault() { return defaultValue; } /** * 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 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 extends LateBoundDefault { protected AbstractLabelLateBoundDefault( boolean useHostConfiguration, Class fragmentClass, Label defaultValue) { super(useHostConfiguration, fragmentClass, defaultValue); } } private static class AlwaysNullLateBoundDefault extends SimpleLateBoundDefault { static final AlwaysNullLateBoundDefault INSTANCE = new AlwaysNullLateBoundDefault(); private AlwaysNullLateBoundDefault() { super(false, Void.class, null, (rule, attributes, unused) -> null); } } /** A {@link LateBoundDefault} for a {@link Label}. */ @AutoCodec public static class LabelLateBoundDefault extends SimpleLateBoundDefault { @AutoCodec.VisibleForSerialization LabelLateBoundDefault( boolean useHostConfiguration, Class fragmentClass, Label defaultValue, Resolver resolver) { super(useHostConfiguration, fragmentClass, defaultValue, resolver); } /** * Creates a new LabelLateBoundDefault which uses the rule, its configured attributes, and a * fragment of the target configuration. * *

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

Nearly all LateBoundDefaults should use this constructor or {@link * LabelListLateBoundDefault#fromTargetConfiguration}. There are few situations where it isn't * the appropriate option. * *

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

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

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 LabelLateBoundDefault fromTargetConfiguration( Class fragmentClass, Label defaultValue, Resolver 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. * *

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

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 LabelLateBoundDefault fromHostConfiguration( Class fragmentClass, Label defaultValue, Resolver 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. */ @AutoCodec public static class LabelListLateBoundDefault extends SimpleLateBoundDefault> { @AutoCodec.VisibleForSerialization LabelListLateBoundDefault( boolean useHostConfiguration, Class fragmentClass, Resolver> resolver) { super(useHostConfiguration, fragmentClass, ImmutableList.of(), resolver); } public static LabelListLateBoundDefault fromTargetConfiguration( Class fragmentClass, Resolver> 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. * *

This should only be necessary in very specialized cases. In almost all cases, you don't * need this method, just use {@link ComputedDefault}. * *

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 fromRuleAndAttributesOnly( Resolver> resolver) { return new LabelListLateBoundDefault<>(false, Void.class, resolver); } } private final String name; private final Type type; private final Set propertyFlags; // Exactly one of these conditions is true: // 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 && // type.isValid(defaultValue.getDefault(configuration)) // (We assume a hypothetical Type.isValid(Object) predicate.) private final Object defaultValue; private final ConfigurationTransition configTransition; private final SplitTransitionProvider splitTransitionProvider; /** * For label or label-list attributes, this predicate returns which rule * classes are allowed for the targets in the attribute. */ private final RuleClassNamePredicate allowedRuleClassesForLabels; /** * For label or label-list attributes, this predicate returns which rule * classes are allowed for the targets in the attribute with warning. */ private final RuleClassNamePredicate allowedRuleClassesForLabelsWarning; /** * For label or label-list attributes, this predicate returns which file * types are allowed for targets in the attribute that happen to be file * targets (rather than rules). */ private final FileTypeSet allowedFileTypesForLabels; /** * This predicate-like object checks * if the edge between two rules using this attribute is valid * in the dependency graph. Returns null if valid, otherwise an error message. */ private final ValidityPredicate validityPredicate; private final Predicate condition; private final PredicateWithMessage allowedValues; private final RequiredProviders requiredProviders; private final ImmutableList> aspects; private final int hashCode; /** * Constructs a rule attribute with the specified name, type and default value. * * @param name the name of the attribute * @param type the type of the attribute * @param defaultValue the default value to use for this attribute if none is specified in rule * declaration in the BUILD file. Must be null, or of type "type". May be an instance of * ComputedDefault, in which case its getDefault() method must return an instance of "type", * or null. Must be immutable. * @param configTransition the configuration transition for this attribute (which must be of type * LABEL, LABEL_LIST, NODEP_LABEL or NODEP_LABEL_LIST). */ @VisibleForSerialization Attribute( String name, Type type, Set propertyFlags, Object defaultValue, ConfigurationTransition configTransition, SplitTransitionProvider splitTransitionProvider, RuleClassNamePredicate allowedRuleClassesForLabels, RuleClassNamePredicate allowedRuleClassesForLabelsWarning, FileTypeSet allowedFileTypesForLabels, ValidityPredicate validityPredicate, Predicate condition, PredicateWithMessage allowedValues, RequiredProviders requiredProviders, ImmutableList> aspects) { Preconditions.checkNotNull(configTransition); Preconditions.checkArgument( (configTransition == NoTransition.INSTANCE) || type.getLabelClass() == LabelClass.DEPENDENCY || type.getLabelClass() == LabelClass.NONDEP_REFERENCE, "Configuration transitions can only be specified for label or label list attributes"); Preconditions.checkArgument( isLateBound(name) == (defaultValue instanceof LateBoundDefault), "late bound attributes require a default value that is late bound (and vice versa): %s", name); if (isLateBound(name)) { LateBoundDefault lateBoundDefault = (LateBoundDefault) defaultValue; Preconditions.checkArgument(!lateBoundDefault.useHostConfiguration() || (configTransition.isHostTransition()), "a late bound default value using the host configuration must use the host transition"); } this.name = name; this.type = type; this.propertyFlags = propertyFlags; this.defaultValue = defaultValue; this.configTransition = configTransition; this.splitTransitionProvider = splitTransitionProvider; this.allowedRuleClassesForLabels = allowedRuleClassesForLabels; this.allowedRuleClassesForLabelsWarning = allowedRuleClassesForLabelsWarning; this.allowedFileTypesForLabels = allowedFileTypesForLabels; this.validityPredicate = validityPredicate; this.condition = condition; this.allowedValues = allowedValues; this.requiredProviders = requiredProviders; this.aspects = aspects; this.hashCode = Objects.hash( name, type, propertyFlags, defaultValue, configTransition, splitTransitionProvider, allowedRuleClassesForLabels, allowedRuleClassesForLabelsWarning, allowedFileTypesForLabels, validityPredicate, condition, allowedValues, requiredProviders, aspects); } /** * Returns the name of this attribute. */ public String getName() { return name; } /** * Returns the public name of this attribute. This is the name we use in Skylark code * and we can use it to display to the end-user. * Implicit and late-bound attributes start with '_' (instead of '$' or ':'). */ public String getPublicName() { return getSkylarkName(getName()); } /** * Returns the logical type of this attribute. (May differ from the actual * representation as a value in the build interpreter; for example, an * attribute may logically be a list of labels, but be represented as a list * of strings.) */ public Type getType() { return type; } private boolean getPropertyFlag(PropertyFlag flag) { return propertyFlags.contains(flag); } /** * Returns true if this parameter is mandatory. */ public boolean isMandatory() { return getPropertyFlag(PropertyFlag.MANDATORY); } /** * Returns true if this list parameter cannot have an empty list as a value. */ public boolean isNonEmpty() { return getPropertyFlag(PropertyFlag.NON_EMPTY); } /** * Returns true if this label parameter must produce a single artifact. */ public boolean isSingleArtifact() { return getPropertyFlag(PropertyFlag.SINGLE_ARTIFACT); } /** * Returns true if this label type parameter is checked by silent ruleclass filtering. */ public boolean isSilentRuleClassFilter() { return getPropertyFlag(PropertyFlag.SILENT_RULECLASS_FILTER); } /** * Returns true if this label type parameter skips the analysis time filetype check. */ public boolean isSkipAnalysisTimeFileTypeCheck() { return getPropertyFlag(PropertyFlag.SKIP_ANALYSIS_TIME_FILETYPE_CHECK); } /** * Returns true if this parameter is order-independent. */ public boolean isOrderIndependent() { return getPropertyFlag(PropertyFlag.ORDER_INDEPENDENT); } /** * Returns true if output_licenses should be used for checking licensing. */ public boolean useOutputLicenses() { return getPropertyFlag(PropertyFlag.OUTPUT_LICENSES); } /** * Returns the configuration transition for this attribute for label or label * list attributes. For other attributes it will always return {@code NONE}. */ public ConfigurationTransition getConfigurationTransition() { return configTransition; } /** * Returns the split configuration transition for this attribute. * * @param attributeMapper the attribute mapper of the current {@link Rule} * @return a SplitTransition object * @throws IllegalStateException if {@link #hasSplitConfigurationTransition} is not true */ public SplitTransition getSplitTransition(AttributeMap attributeMapper) { Preconditions.checkState(hasSplitConfigurationTransition()); return splitTransitionProvider.apply(attributeMapper); } /** * Returns true if this attribute transitions on a split transition. * See {@link SplitTransition}. */ public boolean hasSplitConfigurationTransition() { return (splitTransitionProvider != null); } /** * Returns whether the target is required to be executable for label or label * list attributes. For other attributes it always returns {@code false}. */ public boolean isExecutable() { return getPropertyFlag(PropertyFlag.EXECUTABLE); } /** * Returns {@code true} iff the rule is a direct input for an action. */ public boolean isDirectCompileTimeInput() { return getPropertyFlag(PropertyFlag.DIRECT_COMPILE_TIME_INPUT); } /** * Returns {@code true} iff this attribute requires documentation. */ public boolean isDocumented() { return !getPropertyFlag(PropertyFlag.UNDOCUMENTED); } /** * Returns {@code true} iff this attribute should be published to the rule's * tag set. Note that not all Type classes support tag conversion. */ public boolean isTaggable() { return getPropertyFlag(PropertyFlag.TAGGABLE); } public boolean isStrictLabelCheckingEnabled() { return getPropertyFlag(PropertyFlag.STRICT_LABEL_CHECKING); } /** * Returns true if the value of this attribute should be a part of a given set. */ public boolean checkAllowedValues() { return getPropertyFlag(PropertyFlag.CHECK_ALLOWED_VALUES); } public boolean performPrereqValidatorCheck() { return !getPropertyFlag(PropertyFlag.SKIP_PREREQ_VALIDATOR_CHECKS); } public boolean checkConstraintsOverride() { return getPropertyFlag(PropertyFlag.CHECK_CONSTRAINTS_OVERRIDE); } public boolean skipConstraintsOverride() { return getPropertyFlag(PropertyFlag.SKIP_CONSTRAINTS_OVERRIDE); } /** * Returns true if this attribute's value can be influenced by the build configuration. */ public boolean isConfigurable() { // Output types are excluded because of Rule#populateExplicitOutputFiles. return !(type.getLabelClass() == LabelClass.OUTPUT || getPropertyFlag(PropertyFlag.NONCONFIGURABLE)); } /** * Returns a predicate that evaluates to true for rule classes that are allowed labels in this * attribute. If this is not a label or label-list attribute, the returned predicate always * evaluates to true. * *

NOTE: This may return Predicates.alwaysTrue() as a sentinel meaning "do the right * thing", rather than actually allowing all rule classes in that attribute. Others parts of bazel * code check for that specific instance. */ public Predicate getAllowedRuleClassesPredicate() { return allowedRuleClassesForLabels.asPredicateOfRuleClass(); } /** * Returns a predicate that evaluates to true for rule classes that are * allowed labels in this attribute. If this is not a label or label-list * attribute, the returned predicate always evaluates to true. */ // TODO(b/69917891) Remove these methods once checkbuilddeps no longer depends on them. public Predicate getAllowedRuleClassNamesPredicate() { return allowedRuleClassesForLabels.asPredicateOfRuleClassName(); } /** * Returns a predicate that evaluates to true for rule classes that are * allowed labels in this attribute with warning. If this is not a label or label-list * attribute, the returned predicate always evaluates to true. */ public Predicate getAllowedRuleClassesWarningPredicate() { return allowedRuleClassesForLabelsWarning.asPredicateOfRuleClass(); } public RequiredProviders getRequiredProviders() { return requiredProviders; } public FileTypeSet getAllowedFileTypesPredicate() { return allowedFileTypesForLabels; } public ValidityPredicate getValidityPredicate() { return validityPredicate; } public Predicate getCondition() { return condition == null ? Predicates.alwaysTrue() : condition; } public PredicateWithMessage getAllowedValues() { return allowedValues; } /** * Returns the list of aspects required for dependencies through this attribute. */ public ImmutableList getAspects(Rule rule) { ImmutableList.Builder builder = null; for (RuleAspect aspect : aspects) { Aspect a = aspect.getAspect(rule); if (a != null) { if (builder == null) { builder = ImmutableList.builder(); } builder.add(a); } } return builder == null ? ImmutableList.of() : builder.build(); } public ImmutableList getAspectClasses() { ImmutableList.Builder result = ImmutableList.builder(); for (RuleAspect aspect : aspects) { result.add(aspect.getAspectClass()); } return result.build(); } /** * Returns the default value of this attribute in the context of the * specified Rule. For attributes with a computed default, i.e. {@code * hasComputedDefault()}, {@code rule} must be non-null since the result may * depend on the values of its other attributes. * *

The result may be null (although this is not a value in the build * language). * *

During population of the rule's attribute dictionary, all non-computed * defaults must be set before all computed ones. * * @param rule the rule to which this attribute belongs; non-null if * {@code hasComputedDefault()}; ignored otherwise. */ 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 { return defaultValue; } } /** * Returns the default value of this attribute, even if it has a condition, is a computed default, * or a late-bound default. */ @VisibleForTesting public Object getDefaultValueForTesting() { return defaultValue; } public LateBoundDefault getLateBoundDefault() { Preconditions.checkState(isLateBound()); return (LateBoundDefault) defaultValue; } /** * Returns true iff this attribute has a computed default or a condition. * * @see #getDefaultValue(Rule) */ boolean hasComputedDefault() { return (defaultValue instanceof ComputedDefault) || (defaultValue instanceof SkylarkComputedDefaultTemplate) || (condition != null); } /** * Returns if this attribute is an implicit dependency according to the naming policy that * designates implicit attributes. */ public boolean isImplicit() { return isImplicit(getName()); } /** * Returns if an attribute with the given name is an implicit dependency according to the * naming policy that designates implicit attributes. */ public static boolean isImplicit(String name) { return name.startsWith("$"); } /** * Returns if this attribute is late-bound according to the naming policy that designates * late-bound attributes. */ public boolean isLateBound() { return isLateBound(getName()); } /** * Returns if an attribute with the given name is late-bound according to the naming policy * that designates late-bound attributes. */ public static boolean isLateBound(String name) { return name.startsWith(":"); } /** Returns whether this attribute is considered private in Skylark. */ private static boolean isPrivateAttribute(String nativeAttrName) { return isLateBound(nativeAttrName) || isImplicit(nativeAttrName); } /** * Returns the Skylark-usable name of this attribute. * * Implicit and late-bound attributes start with '_' (instead of '$' or ':'). */ public static String getSkylarkName(String nativeAttrName) { if (isPrivateAttribute(nativeAttrName)) { return "_" + nativeAttrName.substring(1); } return nativeAttrName; } @Override public String toString() { return "Attribute(" + name + ", " + type + ")"; } @Override public int compareTo(Attribute other) { return name.compareTo(other.name); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Attribute attribute = (Attribute) o; return Objects.equals(hashCode, attribute.hashCode) && Objects.equals(name, attribute.name) && Objects.equals(type, attribute.type) && Objects.equals(propertyFlags, attribute.propertyFlags) && Objects.equals(defaultValue, attribute.defaultValue) && Objects.equals(configTransition, attribute.configTransition) && Objects.equals(splitTransitionProvider, attribute.splitTransitionProvider) && Objects.equals(allowedRuleClassesForLabels, attribute.allowedRuleClassesForLabels) && Objects.equals( allowedRuleClassesForLabelsWarning, attribute.allowedRuleClassesForLabelsWarning) && Objects.equals(allowedFileTypesForLabels, attribute.allowedFileTypesForLabels) && Objects.equals(validityPredicate, attribute.validityPredicate) && Objects.equals(condition, attribute.condition) && Objects.equals(allowedValues, attribute.allowedValues) && Objects.equals(requiredProviders, attribute.requiredProviders) && Objects.equals(aspects, attribute.aspects); } @Override public int hashCode() { return hashCode; } /** * Returns a replica builder of this Attribute. */ public Attribute.Builder cloneBuilder(Type tp) { Preconditions.checkArgument(tp == this.type); Builder builder = new Builder<>(name, tp); builder.allowedFileTypesForLabels = allowedFileTypesForLabels; builder.allowedRuleClassesForLabels = allowedRuleClassesForLabels; builder.allowedRuleClassesForLabelsWarning = allowedRuleClassesForLabelsWarning; builder.requiredProvidersBuilder = requiredProviders.copyAsBuilder(); builder.validityPredicate = validityPredicate; builder.condition = condition; builder.configTransition = configTransition; builder.splitTransitionProvider = splitTransitionProvider; builder.propertyFlags = newEnumSet(propertyFlags, PropertyFlag.class); builder.value = defaultValue; builder.valueSet = false; builder.allowedValues = allowedValues; builder.aspects = new LinkedHashMap<>(); for (RuleAspect aspect : aspects) { builder.aspects.put(aspect.getName(), aspect); } return builder; } public Attribute.Builder cloneBuilder() { return cloneBuilder(this.type); } }