private void resolveExplicitAttributes( Rule rule, final BuildConfiguration configuration, AttributeMap attributes, final ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> builder) { attributes.visitLabels( new AttributeMap.AcceptsLabelAttribute() { @Override public void acceptLabelAttribute(Label label, Attribute attribute) { String attributeName = attribute.getName(); if (attributeName.equals("abi_deps")) { // abi_deps is handled specially: we visit only the branch that // needs to be taken based on the configuration. return; } if (attribute.getType() == BuildType.NODEP_LABEL) { return; } if (attribute.isImplicit() || attribute.isLateBound()) { return; } builder.put(attribute, LabelAndConfiguration.of(label, configuration)); } }); // TODO(bazel-team): Remove this in favor of the new configurable attributes. if (attributes.getAttributeDefinition("abi_deps") != null) { Attribute depsAttribute = attributes.getAttributeDefinition("deps"); MakeVariableExpander.Context context = new ConfigurationMakeVariableContext(rule.getPackage(), configuration); String abi = null; try { abi = MakeVariableExpander.expand(attributes.get("abi", Type.STRING), context); } catch (MakeVariableExpander.ExpansionException e) { // Ignore this. It will be handled during the analysis phase. } if (abi != null) { for (Map.Entry<String, List<Label>> entry : attributes.get("abi_deps", BuildType.LABEL_LIST_DICT).entrySet()) { try { if (Pattern.matches(entry.getKey(), abi)) { for (Label label : entry.getValue()) { builder.put(depsAttribute, LabelAndConfiguration.of(label, configuration)); } } } catch (PatternSyntaxException e) { // Ignore this. It will be handled during the analysis phase. } } } } }
private void resolveImplicitAttributes( Rule rule, BuildConfiguration configuration, AttributeMap attributeMap, Iterable<Attribute> attributes, ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> builder) { // Since the attributes that come from aspects do not appear in attributeMap, we have to get // their values from somewhere else. This incidentally means that aspects attributes are not // configurable. It would be nice if that wasn't the case, but we'd have to revamp how // attribute mapping works, which is a large chunk of work. ImmutableSet<String> mappedAttributes = ImmutableSet.copyOf(attributeMap.getAttributeNames()); for (Attribute attribute : attributes) { if (!attribute.isImplicit() || !attribute.getCondition().apply(attributeMap)) { continue; } if (attribute.getType() == BuildType.LABEL) { Label label = mappedAttributes.contains(attribute.getName()) ? attributeMap.get(attribute.getName(), BuildType.LABEL) : BuildType.LABEL.cast(attribute.getDefaultValue(rule)); if (label != null) { builder.put(attribute, LabelAndConfiguration.of(label, configuration)); } } else if (attribute.getType() == BuildType.LABEL_LIST) { List<Label> labelList = mappedAttributes.contains(attribute.getName()) ? attributeMap.get(attribute.getName(), BuildType.LABEL_LIST) : BuildType.LABEL_LIST.cast(attribute.getDefaultValue(rule)); for (Label label : labelList) { builder.put(attribute, LabelAndConfiguration.of(label, configuration)); } } } }
/** * Adds new dependencies to the given rule under the given attribute name * * @param result the builder for the attribute --> dependency labels map * @param rule the rule being evaluated * @param attrName the name of the attribute to add dependency labels to * @param labels the dependencies to add * @param configuration the configuration to apply to those dependencies */ private void addExplicitDeps( ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> result, Rule rule, String attrName, Iterable<Label> labels, BuildConfiguration configuration) { if (!rule.isAttrDefined(attrName, BuildType.LABEL_LIST) && !rule.isAttrDefined(attrName, BuildType.NODEP_LABEL_LIST)) { return; } Attribute attribute = rule.getRuleClassObject().getAttributeByName(attrName); for (Label label : labels) { // The configuration must be the configuration after the first transition step (applying // split configurations). The proper configuration (null) for package groups will be set // later. result.put(attribute, LabelAndConfiguration.of(label, configuration)); } }
private void resolveLateBoundAttributes( Rule rule, BuildConfiguration configuration, BuildConfiguration hostConfiguration, AttributeMap attributeMap, Iterable<Attribute> attributes, ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> builder) throws EvalException, InterruptedException { for (Attribute attribute : attributes) { if (!attribute.isLateBound() || !attribute.getCondition().apply(attributeMap)) { continue; } List<BuildConfiguration> actualConfigurations = ImmutableList.of(configuration); if (attribute.getConfigurationTransition() instanceof SplitTransition<?>) { Preconditions.checkState(attribute.getConfigurator() == null); // TODO(bazel-team): This ends up applying the split transition twice, both here and in the // visitRule method below - this is not currently a problem, because the configuration graph // never contains nested split transitions, so the second application is idempotent. actualConfigurations = configuration.getSplitConfigurations( (SplitTransition<?>) attribute.getConfigurationTransition()); } for (BuildConfiguration actualConfig : actualConfigurations) { @SuppressWarnings("unchecked") LateBoundDefault<BuildConfiguration> lateBoundDefault = (LateBoundDefault<BuildConfiguration>) attribute.getLateBoundDefault(); if (lateBoundDefault.useHostConfiguration()) { actualConfig = hostConfiguration; } // TODO(bazel-team): This might be too expensive - can we cache this somehow? if (!lateBoundDefault.getRequiredConfigurationFragments().isEmpty()) { if (!actualConfig.hasAllFragments(lateBoundDefault.getRequiredConfigurationFragments())) { continue; } } // TODO(bazel-team): We should check if the implementation tries to access an undeclared // fragment. Object actualValue = lateBoundDefault.getDefault(rule, actualConfig); if (EvalUtils.isNullOrNone(actualValue)) { continue; } try { if (attribute.getType() == BuildType.LABEL) { Label label = BuildType.LABEL.cast(actualValue); builder.put(attribute, LabelAndConfiguration.of(label, actualConfig)); } else if (attribute.getType() == BuildType.LABEL_LIST) { for (Label label : BuildType.LABEL_LIST.cast(actualValue)) { builder.put(attribute, LabelAndConfiguration.of(label, actualConfig)); } } else { throw new IllegalStateException( String.format( "Late bound attribute '%s' is not a label or a label list", attribute.getName())); } } catch (ClassCastException e) { throw new EvalException( rule.getLocation(), String.format( "When computing the default value of %s, expected '%s', got '%s'", attribute.getName(), attribute.getType(), EvalUtils.getDataTypeName(actualValue, true))); } } } }
private ListMultimap<Attribute, LabelAndConfiguration> resolveAttributes( Rule rule, AspectDefinition aspect, BuildConfiguration configuration, BuildConfiguration hostConfiguration, Set<ConfigMatchingProvider> configConditions) throws EvalException, InterruptedException { ConfiguredAttributeMapper attributeMap = ConfiguredAttributeMapper.of(rule, configConditions); attributeMap.validateAttributes(); List<Attribute> attributes; if (aspect == null) { attributes = rule.getRuleClassObject().getAttributes(); } else { attributes = new ArrayList<>(); attributes.addAll(rule.getRuleClassObject().getAttributes()); if (aspect != null) { attributes.addAll(aspect.getAttributes().values()); } } ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> result = ImmutableSortedKeyListMultimap.builder(); resolveExplicitAttributes(rule, configuration, attributeMap, result); resolveImplicitAttributes(rule, configuration, attributeMap, attributes, result); resolveLateBoundAttributes( rule, configuration, hostConfiguration, attributeMap, attributes, result); // Add the rule's visibility labels (which may come from the rule or from package defaults). addExplicitDeps( result, rule, "visibility", rule.getVisibility().getDependencyLabels(), configuration); // Add package default constraints when the rule doesn't explicitly declare them. // // Note that this can have subtle implications for constraint semantics. For example: say that // package defaults declare compatibility with ':foo' and rule R declares compatibility with // ':bar'. Does that mean that R is compatible with [':foo', ':bar'] or just [':bar']? In other // words, did R's author intend to add additional compatibility to the package defaults or to // override them? More severely, what if package defaults "restrict" support to just [':baz']? // Should R's declaration signify [':baz'] + ['bar'], [ORIGINAL_DEFAULTS] + ['bar'], or // something else? // // Rather than try to answer these questions with possibly confusing logic, we take the // simple approach of assigning the rule's "restriction" attribute to the rule-declared value if // it exists, else the package defaults value (and likewise for "compatibility"). This may not // always provide what users want, but it makes it easy for them to understand how rule // declarations and package defaults intermix (and how to refactor them to get what they want). // // An alternative model would be to apply the "rule declaration" / "rule class defaults" // relationship, i.e. the rule class' "compatibility" and "restriction" declarations are merged // to generate a set of default environments, then the rule's declarations are independently // processed on top of that. This protects against obscure coupling behavior between // declarations from wildly different places (e.g. it offers clear answers to the examples posed // above). But within the scope of a single package it seems better to keep the model simple and // make the user responsible for resolving ambiguities. if (!rule.isAttributeValueExplicitlySpecified(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR)) { addExplicitDeps( result, rule, RuleClass.COMPATIBLE_ENVIRONMENT_ATTR, rule.getPackage().getDefaultCompatibleWith(), configuration); } if (!rule.isAttributeValueExplicitlySpecified(RuleClass.RESTRICTED_ENVIRONMENT_ATTR)) { addExplicitDeps( result, rule, RuleClass.RESTRICTED_ENVIRONMENT_ATTR, rule.getPackage().getDefaultRestrictedTo(), configuration); } return result.build(); }