/** * Returns the configurable attribute conditions necessary to evaluate the given configured * target, or null if not all dependencies have yet been SkyFrame-evaluated. */ @Nullable private Set<ConfigMatchingProvider> getConfigurableAttributeConditions( TargetAndConfiguration ctg, Environment env) { if (!(ctg.getTarget() instanceof Rule)) { return ImmutableSet.of(); } Rule rule = (Rule) ctg.getTarget(); RawAttributeMapper mapper = RawAttributeMapper.of(rule); Set<SkyKey> depKeys = new LinkedHashSet<>(); for (Attribute attribute : rule.getAttributes()) { for (Label label : mapper.getConfigurabilityKeys(attribute.getName(), attribute.getType())) { if (!BuildType.Selector.isReservedLabel(label)) { depKeys.add(ConfiguredTargetValue.key(label, ctg.getConfiguration())); } } } Map<SkyKey, SkyValue> cts = env.getValues(depKeys); if (env.valuesMissing()) { return null; } ImmutableSet.Builder<ConfigMatchingProvider> conditions = ImmutableSet.builder(); for (SkyValue ctValue : cts.values()) { ConfiguredTarget ct = ((ConfiguredTargetValue) ctValue).getConfiguredTarget(); conditions.add(Preconditions.checkNotNull(ct.getProvider(ConfigMatchingProvider.class))); } return conditions.build(); }
private static Set<AspectWithParameters> extractAspectCandidates( AspectDefinition aspectDefinition, AspectParameters aspectParameters, Attribute attribute, Rule originalRule) { // The order of this set will be deterministic. This is necessary because this order eventually // influences the order in which aspects are merged into the main configured target, which in // turn influences which aspect takes precedence if two emit the same provider (maybe this // should be an error) Set<AspectWithParameters> aspectCandidates = new LinkedHashSet<>(); for (Map.Entry<Class<? extends AspectFactory<?, ?, ?>>, AspectParameters> aspectWithParameters : attribute.getAspectsWithParameters(originalRule).entrySet()) { aspectCandidates.add( new AspectWithParameters( aspectWithParameters.getKey().asSubclass(ConfiguredAspectFactory.class), aspectWithParameters.getValue())); } if (aspectDefinition != null) { for (Class<? extends AspectFactory<?, ?, ?>> aspect : aspectDefinition.getAttributeAspects().get(attribute.getName())) { aspectCandidates.add( new AspectWithParameters( aspect.asSubclass(ConfiguredAspectFactory.class), aspectParameters)); } } return aspectCandidates; }
@Test public void testNonEmpty() throws Exception { Attribute attr = attr("foo", Type.LABEL_LIST).nonEmpty().legacyAllowAnyFileType().build(); assertEquals("foo", attr.getName()); assertEquals(Type.LABEL_LIST, attr.getType()); assertTrue(attr.isNonEmpty()); }
private void visitRule( Rule rule, AspectDefinition aspect, AspectParameters aspectParameters, ListMultimap<Attribute, LabelAndConfiguration> labelMap, ListMultimap<Attribute, Dependency> outgoingEdges) { Preconditions.checkNotNull(labelMap); for (Map.Entry<Attribute, Collection<LabelAndConfiguration>> entry : labelMap.asMap().entrySet()) { Attribute attribute = entry.getKey(); for (LabelAndConfiguration dep : entry.getValue()) { Label label = dep.getLabel(); BuildConfiguration config = dep.getConfiguration(); Target toTarget; try { toTarget = getTarget(label); } catch (NoSuchThingException e) { throw new IllegalStateException( "not found: " + label + " from " + rule + " in " + attribute.getName()); } if (toTarget == null) { continue; } BuildConfiguration.TransitionApplier transitionApplier = config.getTransitionApplier(); if (config.useDynamicConfigurations() && config.isHostConfiguration() && !BuildConfiguration.usesNullConfiguration(toTarget)) { // This condition is needed because resolveLateBoundAttributes may switch config to // the host configuration, which is the only case DependencyResolver applies a // configuration transition outside of this method. We need to reflect that // transition in the results of this method, but config.evaluateTransition is hard-set // to return a NONE transition when the input is a host config. Since the outside // caller originally passed the *original* value of config (before the possible // switch), it can mistakenly interpret the result as a NONE transition from the // original value of config. This condition fixes that. Another fix would be to have // config.evaluateTransition return a HOST transition when the input config is a host, // but since this blemish is specific to DependencyResolver it seems best to keep the // fix here. // TODO(bazel-team): eliminate this special case by passing transitionApplier to // resolveLateBoundAttributes, so that method uses the same interface for transitions. transitionApplier.applyTransition(Attribute.ConfigurationTransition.HOST); } else { config.evaluateTransition(rule, attribute, toTarget, transitionApplier); } for (Dependency dependency : transitionApplier.getDependencies( label, requiredAspects(aspect, aspectParameters, attribute, toTarget, rule))) { outgoingEdges.put(entry.getKey(), dependency); } } } }
/** Returns all dependencies that should be constraint-checked against the current rule. */ private static Iterable<TransitiveInfoCollection> getConstraintCheckedDependencies( RuleContext ruleContext) { Set<TransitiveInfoCollection> depsToCheck = new LinkedHashSet<>(); AttributeMap attributes = ruleContext.attributes(); for (String attr : attributes.getAttributeNames()) { Attribute attrDef = attributes.getAttributeDefinition(attr); Type<?> attrType = attributes.getAttributeType(attr); // TODO(bazel-team): support a user-definable API for choosing which attributes are checked if (!attrDef.checkConstraintsOverride()) { if ((attrType != BuildType.LABEL && attrType != BuildType.LABEL_LIST) || RuleClass.isConstraintAttribute(attr) || attr.equals("visibility") // Use the same implicit deps check that query uses. This facilitates running queries to // determine exactly which rules need to be constraint-annotated for depot migrations. || !Rule.NO_IMPLICIT_DEPS.apply(ruleContext.getRule(), attrDef) // We can't identify host deps by calling BuildConfiguration.isHostConfiguration() // because --nodistinct_host_configuration subverts that call. || attrDef.getConfigurationTransition() == Attribute.ConfigurationTransition.HOST) { continue; } } for (TransitiveInfoCollection dep : ruleContext.getPrerequisites(attr, RuleConfiguredTarget.Mode.DONT_CHECK)) { // Output files inherit the environment spec of their generating rule. if (dep instanceof OutputFileConfiguredTarget) { // Note this reassignment means constraint violation errors reference the generating // rule, not the file. This makes the source of the environmental mismatch more clear. dep = ((OutputFileConfiguredTarget) dep).getGeneratingRule(); } // Input files don't support environments. We may subsequently opt them into constraint // checking, but for now just pass them by. if (dep.getProvider(SupportedEnvironmentsProvider.class) != null) { depsToCheck.add(dep); } } } return depsToCheck; }
@Override public ImmutableMultimap<Attribute, Label> computeAspectDependencies(Target target) throws InterruptedException { if (!(target instanceof Rule)) { return ImmutableMultimap.of(); } Multimap<Attribute, Label> result = LinkedHashMultimap.create(); for (Attribute attribute : ((Rule) target).getAttributes()) { for (Class<? extends AspectFactory<?, ?, ?>> aspectFactory : attribute.getAspects()) { AspectDefinition.addAllAttributesOfAspect( (Rule) target, result, AspectFactory.Util.create(aspectFactory).getDefinition(), Rule.ALL_DEPS); } } return ImmutableMultimap.copyOf(result); }
@Test public void testBasics() throws Exception { Attribute attr = attr("foo", Type.INTEGER).mandatory().value(3).build(); assertEquals("foo", attr.getName()); assertEquals(3, attr.getDefaultValue(null)); assertEquals(Type.INTEGER, attr.getType()); assertTrue(attr.isMandatory()); assertTrue(attr.isDocumented()); attr = attr("$foo", Type.INTEGER).build(); assertFalse(attr.isDocumented()); }
@Override public Iterable<String> getAttrAsString(Target target, String attrName) { Preconditions.checkArgument(target instanceof Rule); List<String> values = new ArrayList<>(); // May hold null values. Attribute attribute = ((Rule) target).getAttributeDefinition(attrName); if (attribute != null) { Type<?> attributeType = attribute.getType(); for (Object attrValue : AggregatingAttributeMapper.of((Rule) target) .visitAttribute(attribute.getName(), attributeType)) { // Ugly hack to maintain backward 'attr' query compatibility for BOOLEAN and TRISTATE // attributes. These are internally stored as actual Boolean or TriState objects but were // historically queried as integers. To maintain compatibility, we inspect their actual // value and return the integer equivalent represented as a String. This code is the // opposite of the code in BooleanType and TriStateType respectively. if (attributeType == BOOLEAN) { values.add(Type.BOOLEAN.cast(attrValue) ? "1" : "0"); } else if (attributeType == TRISTATE) { switch (BuildType.TRISTATE.cast(attrValue)) { case AUTO: values.add("-1"); break; case NO: values.add("0"); break; case YES: values.add("1"); break; default: throw new AssertionError("This can't happen!"); } } else { values.add(attrValue == null ? null : attrValue.toString()); } } } return values; }
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 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)); } } } }
private void assertType(Type<?> expectedType, Attribute attr) { assertEquals(expectedType, attr.getType()); }
private void assertDefaultValue(Object expected, Attribute attr) { assertEquals(expected, attr.getDefaultValue(null)); }
@Test public void testCloneBuilder() { FileTypeSet txtFiles = FileTypeSet.of(FileType.of("txt")); RuleClass.Builder.RuleClassNamePredicate ruleClasses = new RuleClass.Builder.RuleClassNamePredicate("mock_rule"); Attribute parentAttr = attr("x", LABEL_LIST).allowedFileTypes(txtFiles).mandatory().build(); Attribute childAttr1 = parentAttr.cloneBuilder().build(); assertEquals("x", childAttr1.getName()); assertEquals(txtFiles, childAttr1.getAllowedFileTypesPredicate()); assertEquals(Predicates.alwaysTrue(), childAttr1.getAllowedRuleClassesPredicate()); assertTrue(childAttr1.isMandatory()); assertFalse(childAttr1.isNonEmpty()); Attribute childAttr2 = parentAttr.cloneBuilder().nonEmpty().allowedRuleClasses(ruleClasses).build(); assertEquals("x", childAttr2.getName()); assertEquals(txtFiles, childAttr2.getAllowedFileTypesPredicate()); assertEquals(ruleClasses, childAttr2.getAllowedRuleClassesPredicate()); assertTrue(childAttr2.isMandatory()); assertTrue(childAttr2.isNonEmpty()); // Check if the parent attribute is unchanged assertFalse(parentAttr.isNonEmpty()); assertEquals(Predicates.alwaysTrue(), parentAttr.getAllowedRuleClassesPredicate()); }
@Override public void acceptLabelAttribute(Label label, Attribute attribute) { if (attribute.getName().equals("srcs")) { labelsVisited.add(label.toString()); } }