// Copyright 2014 Google Inc. 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 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.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.Sets; import com.google.devtools.build.lib.syntax.ClassObject; import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.Label; import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction; import com.google.devtools.build.lib.syntax.SkylarkModule; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.syntax.Type.ConversionException; 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.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; 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 public final class Attribute implements Comparable { public static final Predicate ANY_RULE = Predicates.alwaysTrue(); public static final Predicate NO_RULE = Predicates.alwaysFalse(); private static final class RuleAspect { private final Class> aspectFactory; private final Function parametersExtractor; RuleAspect( Class> aspectFactory, Function parametersExtractor) { this.aspectFactory = aspectFactory; this.parametersExtractor = parametersExtractor; } Class> getAspectFactory() { return aspectFactory; } Function getParametersExtractor() { return parametersExtractor; } } /** * A configuration transition. */ public interface Transition { /** * Usually, a non-existent entry in the configuration transition table indicates an error. * Unfortunately, that means that we need to always build the full table. This method allows a * transition to indicate that a non-existent entry indicates a self transition, i.e., that the * resulting configuration is the same as the current configuration. This can simplify the code * needed to set up the transition table. */ boolean defaultsToSelf(); } /** * A configuration split transition; this should be used to transition to multiple configurations * simultaneously. Note that the corresponding rule implementations must have special support to * handle this. */ // TODO(bazel-team): Serializability constraints? public interface SplitTransition extends Transition { /** * Return the list of {@code BuildOptions} after splitting; empty if not applicable. */ List split(T buildOptions); } /** * Declaration how the configuration should change when following a label or * label list attribute. */ @SkylarkModule(name = "ConfigurationTransition", doc = "Declares how the configuration should change when following a dependency. " + "It can be either DATA_CFG or " + "HOST_CFG.") public enum ConfigurationTransition implements Transition { /** No transition, i.e., the same configuration as the current. */ NONE, /** Transition to the host configuration. */ HOST, /** Transition to a null configuration (applies to, e.g., input files). */ NULL, /** Transition from the target configuration to the data configuration. */ // TODO(bazel-team): Move this elsewhere. DATA; @Override public boolean defaultsToSelf() { return false; } } private 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 constraints checks for licenses, visibility, etc. */ SKIP_CONSTRAINTS_CHECKS, } // 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); } public static final ValidityPredicate ANY_EDGE = new ValidityPredicate() { @Override public String checkValid(Rule from, Rule to) { return null; } }; /** * Using this callback function, rules can set the configuration of their dependencies during the * analysis phase. */ public interface Configurator { TConfig apply(TRule fromRule, TConfig fromConfiguration, Attribute attribute, Target toTarget); } /** * A predicate class to check if the value of the attribute comes from a predefined set. */ public static class AllowedValueSet implements PredicateWithMessage { private final Set allowedValues; public AllowedValueSet(Iterable values) { Preconditions.checkNotNull(values); Preconditions.checkArgument(!Iterables.isEmpty(values)); allowedValues = ImmutableSet.copyOf(values); } @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; } } /** * 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 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 String name; private final Type type; private Transition configTransition = ConfigurationTransition.NONE; private Predicate allowedRuleClassesForLabels = Predicates.alwaysTrue(); private Predicate allowedRuleClassesForLabelsWarning = Predicates.alwaysFalse(); private Configurator configurator; private FileTypeSet allowedFileTypesForLabels; private ValidityPredicate validityPredicate = ANY_EDGE; private Object value; private boolean valueSet; private Predicate condition; private Set propertyFlags = EnumSet.noneOf(PropertyFlag.class); private PredicateWithMessage allowedValues = null; private ImmutableSet mandatoryProviders = ImmutableSet.of(); private Set aspects = new LinkedHashSet<>(); /** * 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 == BuildType.LABEL) || (type == BuildType.LABEL_LIST), "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 == BuildType.LABEL) || (type == BuildType.LABEL_LIST), "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 == BuildType.LABEL) || (type == BuildType.LABEL_LIST), "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"); } /** * Defines the configuration transition for this attribute. Defaults to * {@code NONE}. */ public Builder cfg(Transition configTransition) { Preconditions.checkState(this.configTransition == ConfigurationTransition.NONE, "the configuration transition is already set"); this.configTransition = configTransition; return this; } public Builder cfg(Configurator configurator) { this.configurator = configurator; 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. */ public Builder defaultValue(Object defaultValue) throws ConversionException { Preconditions.checkState(!valueSet, "the default value is already set"); value = type.convert(defaultValue, "attribute " + name); valueSet = true; return this; } /** * 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 computedDefault returns a Label that is a target, that target will * become an implicit dependency of this Rule; we will load the target * (and its dependencies) if it encounters the Rule and build the target if * needs to apply the Rule. */ public Builder value(ComputedDefault defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); value = defaultValue; valueSet = true; return this; } /** * Sets the attribute default value to be late-bound, i.e., it is derived from the build * configuration. */ public Builder value(LateBoundDefault defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); Preconditions.checkState(name.isEmpty() || isLateBound(name)); value = defaultValue; valueSet = true; return this; } /** * Returns true if a late-bound value has been set. Useful only for Skylark. */ public boolean hasLateBoundValue() { return value instanceof LateBoundDefault; } /** * 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 constraints and visibility checks. */ public Builder skipConstraintsCheck() { return setPropertyFlag(PropertyFlag.SKIP_CONSTRAINTS_CHECKS, "skip_constraints_checks"); } /** * 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 an error is produced during * the analysis phase. Defaults to allow any types. * *

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( new RuleClass.Builder.RuleClassNamePredicate(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 an error is produced during * the analysis phase. Defaults to allow any types. * *

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(Predicate allowedRuleClasses) { Preconditions.checkState((type == BuildType.LABEL) || (type == BuildType.LABEL_LIST), "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 an error is produced during * the analysis phase. Defaults to allow any types. * *

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 == BuildType.LABEL) || (type == BuildType.LABEL_LIST), "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. If the attribute * contains Labels of any other rule type (other than this or those set in * allowedRuleClasses()), then a warning is produced during * the analysis phase. Defaults to deny any types. * *

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( new RuleClass.Builder.RuleClassNamePredicate(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 (other than this or those set in * allowedRuleClasses()), then a warning is produced during * the analysis phase. Defaults to deny any types. * *

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(Predicate allowedRuleClasses) { Preconditions.checkState((type == BuildType.LABEL) || (type == BuildType.LABEL_LIST), "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 for the labels occurring in the attribute. If the attribute * contains Labels of any other rule type (other than this or those set in * allowedRuleClasses()), then a warning is produced during * the analysis phase. Defaults to deny any types. * *

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 set of mandatory Skylark providers. Every configured target occurring in * this label type attribute has to provide all of these providers, otherwise an * error is produces during the analysis phase for every missing provider. */ public Builder mandatoryProviders(Iterable providers) { Preconditions.checkState((type == BuildType.LABEL) || (type == BuildType.LABEL_LIST), "must be a label-valued type"); this.mandatoryProviders = ImmutableSet.copyOf(providers); return this; } /** * Asserts that a particular aspect probably needs to be computed for all direct dependencies * through this attribute. */ public Builder aspect(Class> aspect) { Function noParameters = new Function() { @Override public AspectParameters apply(Rule input) { return AspectParameters.EMPTY; } }; return this.aspect(aspect, noParameters); } /** * 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. */ public Builder aspect(Class> aspect, Function evaluator) { this.aspects.add(new RuleAspect(aspect, evaluator)); 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"); } /** * 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) { Preconditions.checkState(!name.isEmpty(), "name has not been set"); // TODO(bazel-team): Set the default to be no file type, then remove this check, and also // remove all allowedFileTypes() calls without parameters. if ((type == BuildType.LABEL) || (type == BuildType.LABEL_LIST)) { if ((name.startsWith("$") || name.startsWith(":")) && allowedFileTypesForLabels == null) { allowedFileTypesForLabels = FileTypeSet.ANY_FILE; } if (allowedFileTypesForLabels == null) { throw new IllegalStateException(name); } } else if ((type == BuildType.OUTPUT) || (type == BuildType.OUTPUT_LIST)) { // 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, Sets.immutableEnumSet(propertyFlags), valueSet ? value : type.getDefaultValue(), configTransition, configurator, allowedRuleClassesForLabels, allowedRuleClassesForLabelsWarning, allowedFileTypesForLabels, validityPredicate, condition, allowedValues, mandatoryProviders, ImmutableSet.copyOf(aspects)); } } /** * 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. *
  3. The other attribute must be non-configurable ({@link Builder#nonconfigurable}
  4. *
* *

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 List dependencies; List dependencies() { return dependencies; } /** * Create a computed default that can read all non-configurable attribute values and no * configurable attribute values. */ public ComputedDefault() { dependencies = 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) { dependencies = 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) { dependencies = ImmutableList.of(depAttribute1, depAttribute2); } public abstract Object getDefault(AttributeMap rule); } /** * Marker interface for late-bound values. Unfortunately, we can't refer to BuildConfiguration * right now, since that is in a separate compilation unit. * *

Implementations of this interface must be immutable. * *

Use sparingly - having different values for attributes during loading and analysis can * confuse users. */ public interface LateBoundDefault { /** * Whether to look up the label in the host configuration. This is only here for the host JDK - * we usually need to look up labels in the target configuration. */ boolean useHostConfiguration(); /** * Returns the set of required configuration fragments, i.e., fragments that will be accessed by * the code. */ Set> getRequiredConfigurationFragments(); /** * The default value for the attribute that is set during the loading phase. */ Object getDefault(); /** * 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. */ Object getDefault(Rule rule, T o) throws EvalException, InterruptedException; } /** * Abstract super class for label-typed {@link LateBoundDefault} implementations that simplifies * the client code a little and makes it a bit more type-safe. */ public abstract static class LateBoundLabel implements LateBoundDefault { private final Label label; private final ImmutableSet> requiredConfigurationFragments; public LateBoundLabel() { this((Label) null); } public LateBoundLabel(Class... requiredConfigurationFragments) { this((Label) null, requiredConfigurationFragments); } public LateBoundLabel(Label label) { this.label = label; this.requiredConfigurationFragments = ImmutableSet.of(); } public LateBoundLabel(Label label, Class... requiredConfigurationFragments) { this.label = label; this.requiredConfigurationFragments = ImmutableSet.copyOf(requiredConfigurationFragments); } public LateBoundLabel(String label) { this(Label.parseAbsoluteUnchecked(label)); } public LateBoundLabel(String label, Class... requiredConfigurationFragments) { this(Label.parseAbsoluteUnchecked(label), requiredConfigurationFragments); } @Override public boolean useHostConfiguration() { return false; } @Override public ImmutableSet> getRequiredConfigurationFragments() { return requiredConfigurationFragments; } @Override public final Label getDefault() { return label; } @Override public abstract Label getDefault(Rule rule, T configuration); } /** * Abstract super class for label-list-typed {@link LateBoundDefault} implementations that * simplifies the client code a little and makes it a bit more type-safe. */ public abstract static class LateBoundLabelList implements LateBoundDefault { private final ImmutableList