Use RequiredProviders to validate rule prerequisites in RuleContext.
We now use a unified way to check provider requirements everywhere.
Reland of https://github.com/bazelbuild/bazel/commit/c32e1b1efcd703b3780de47fba62974123593d71.
RELNOTES: None.
PiperOrigin-RevId: 164038621
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 771189a..de978e0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -70,18 +70,17 @@
import com.google.devtools.build.lib.packages.OutputFile;
import com.google.devtools.build.lib.packages.PackageSpecification;
import com.google.devtools.build.lib.packages.RawAttributeMapper;
+import com.google.devtools.build.lib.packages.RequiredProviders;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.packages.RuleErrorConsumer;
-import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.rules.AliasProvider;
import com.google.devtools.build.lib.rules.MakeVariableProvider;
import com.google.devtools.build.lib.rules.fileset.FilesetProvider;
import com.google.devtools.build.lib.shell.ShellUtils;
-import com.google.devtools.build.lib.syntax.ClassObject;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.syntax.Type.LabelClass;
@@ -1975,154 +1974,101 @@
}
}
- private String getMissingMandatoryProviders(ConfiguredTarget prerequisite, Attribute attribute){
- ImmutableList<ImmutableSet<SkylarkProviderIdentifier>> mandatoryProvidersList =
- attribute.getMandatoryProvidersList();
- if (mandatoryProvidersList.isEmpty()) {
- return null;
- }
- List<List<String>> missingProvidersList = new ArrayList<>();
- for (ImmutableSet<SkylarkProviderIdentifier> providers : mandatoryProvidersList) {
- List<String> missing = new ArrayList<>();
- for (SkylarkProviderIdentifier provider : providers) {
- // A rule may require a built-in provider that is always implicitly provided, e.g. "files"
- ImmutableSet<String> availableKeys =
- ImmutableSet.copyOf(((ClassObject) prerequisite).getKeys());
- if ((prerequisite.get(provider) == null)
- && !(provider.isLegacy() && availableKeys.contains(provider.getLegacyId()))) {
- missing.add(provider.toString());
- }
- }
- if (missing.isEmpty()) {
- return null;
- } else {
- missingProvidersList.add(missing);
- }
- }
- StringBuilder missingProviders = new StringBuilder();
- Joiner joinProvider = Joiner.on("', '");
- for (List<String> providers : missingProvidersList) {
- if (missingProviders.length() > 0) {
- missingProviders.append(" or ");
- }
- missingProviders.append((providers.size() > 1) ? "[" : "")
- .append("'");
- joinProvider.appendTo(missingProviders, providers);
- missingProviders.append("'")
- .append((providers.size() > 1) ? "]" : "");
- }
- return missingProviders.toString();
- }
-
- private String getMissingMandatoryNativeProviders(
- ConfiguredTarget prerequisite, Attribute attribute) {
- List<ImmutableList<Class<? extends TransitiveInfoProvider>>> mandatoryProvidersList =
- attribute.getMandatoryNativeProvidersList();
- if (mandatoryProvidersList.isEmpty()) {
- return null;
- }
- List<List<String>> missingProvidersList = new ArrayList<>();
- for (ImmutableList<Class<? extends TransitiveInfoProvider>> providers
- : mandatoryProvidersList) {
- List<String> missing = new ArrayList<>();
- for (Class<? extends TransitiveInfoProvider> provider : providers) {
- if (prerequisite.getProvider(provider) == null) {
- missing.add(provider.getSimpleName());
- }
- }
- if (missing.isEmpty()) {
- return null;
- } else {
- missingProvidersList.add(missing);
- }
- }
- StringBuilder missingProviders = new StringBuilder();
- Joiner joinProvider = Joiner.on(", ");
- for (List<String> providers : missingProvidersList) {
- if (missingProviders.length() > 0) {
- missingProviders.append(" or ");
- }
- missingProviders.append((providers.size() > 1) ? "[" : "");
- joinProvider.appendTo(missingProviders, providers);
- missingProviders.append((providers.size() > 1) ? "]" : "");
- }
- return missingProviders.toString();
- }
-
/**
- * Because some rules still have to use allowedRuleClasses to do rule dependency validation.
- * A dependency is valid if it is from a rule in allowedRuleClasses, OR if all of the providers
- * in mandatoryNativeProviders AND mandatoryProvidersList are provided by the target.
+ * Because some rules still have to use allowedRuleClasses to do rule dependency validation. A
+ * dependency is valid if it is from a rule in allowedRuledClasses, OR if all of the providers
+ * in requiredProviders are provided by the target.
*/
private void validateRuleDependency(ConfiguredTarget prerequisite, Attribute attribute) {
- Target prerequisiteTarget = prerequisite.getTarget();
- RuleClass ruleClass = ((Rule) prerequisiteTarget).getRuleClassObject();
- String notAllowedRuleClassesMessage = null;
+ Set<String> unfulfilledRequirements = new LinkedHashSet<>();
+ if (checkRuleDependencyClass(prerequisite, attribute, unfulfilledRequirements)) {
+ return;
+ }
+
+ if (checkRuleDependencyClassWarnings(prerequisite, attribute)) {
+ return;
+ }
+
+ if (checkRuleDependencyMandatoryProviders(prerequisite, attribute, unfulfilledRequirements)) {
+ return;
+ }
+
+ // not allowed rule class and some mandatory providers missing => reject.
+ if (!unfulfilledRequirements.isEmpty()) {
+ attributeError(
+ attribute.getName(), StringUtil.joinEnglishList(unfulfilledRequirements, "and"));
+ }
+ }
+
+ /** Check if prerequisite should be allowed based on its rule class. */
+ private boolean checkRuleDependencyClass(
+ ConfiguredTarget prerequisite, Attribute attribute, Set<String> unfulfilledRequirements) {
if (attribute.getAllowedRuleClassesPredicate() != Predicates.<RuleClass>alwaysTrue()) {
- if (attribute.getAllowedRuleClassesPredicate().apply(ruleClass)) {
+ if (attribute
+ .getAllowedRuleClassesPredicate()
+ .apply(((Rule) prerequisite.getTarget()).getRuleClassObject())) {
// prerequisite has an allowed rule class => accept.
- return;
+ return true;
}
// remember that the rule class that was not allowed;
// but maybe prerequisite provides required providers? do not reject yet.
- notAllowedRuleClassesMessage =
+ unfulfilledRequirements.add(
badPrerequisiteMessage(
- prerequisiteTarget.getTargetKind(),
+ prerequisite.getTarget().getTargetKind(),
prerequisite,
"expected " + attribute.getAllowedRuleClassesPredicate(),
- false);
+ false));
}
+ return false;
+ }
- if (attribute.getAllowedRuleClassesWarningPredicate().apply(ruleClass)) {
+ /**
+ * Check if prerequisite should be allowed with warning based on its rule class.
+ *
+ * <p>If yes, also issues said warning.
+ */
+ private boolean checkRuleDependencyClassWarnings(
+ ConfiguredTarget prerequisite, Attribute attribute) {
+ if (attribute
+ .getAllowedRuleClassesWarningPredicate()
+ .apply(((Rule) prerequisite.getTarget()).getRuleClassObject())) {
Predicate<RuleClass> allowedRuleClasses = attribute.getAllowedRuleClassesPredicate();
- reportBadPrerequisite(attribute, prerequisiteTarget.getTargetKind(), prerequisite,
+ reportBadPrerequisite(
+ attribute,
+ prerequisite.getTarget().getTargetKind(),
+ prerequisite,
allowedRuleClasses == Predicates.<RuleClass>alwaysTrue()
? null
: "expected " + allowedRuleClasses,
true);
// prerequisite has a rule class allowed with a warning => accept, emitting a warning.
- return;
+ return true;
+ }
+ return false;
+ }
+
+ /** Check if prerequisite should be allowed based on required providers on the attribute. */
+ private boolean checkRuleDependencyMandatoryProviders(
+ ConfiguredTarget prerequisite, Attribute attribute, Set<String> unfulfilledRequirements) {
+ RequiredProviders requiredProviders = attribute.getRequiredProviders();
+
+ if (requiredProviders.acceptsAny()) {
+ // If no required providers specified, we do not know if we should accept.
+ return false;
}
- // If we get here, we have no allowed rule class.
- // If mandatory providers are specified, try them.
- Set<String> unfulfilledRequirements = new LinkedHashSet<>();
- if (!attribute.getMandatoryNativeProvidersList().isEmpty()
- || !attribute.getMandatoryProvidersList().isEmpty()) {
- boolean hadAllMandatoryProviders = true;
-
- String missingNativeProviders = getMissingMandatoryNativeProviders(prerequisite, attribute);
- if (missingNativeProviders != null) {
- unfulfilledRequirements.add(
- "'" + prerequisite.getLabel() + "' does not have mandatory providers: "
- + missingNativeProviders);
- hadAllMandatoryProviders = false;
- }
-
- String missingProviders = getMissingMandatoryProviders(prerequisite, attribute);
- if (missingProviders != null) {
- unfulfilledRequirements.add(
- "'" + prerequisite.getLabel() + "' does not have mandatory providers: "
- + missingProviders);
- hadAllMandatoryProviders = false;
- }
-
- if (hadAllMandatoryProviders) {
- // all mandatory providers are present => accept.
- return;
- }
+ if (prerequisite.satisfies(requiredProviders)) {
+ return true;
}
- // not allowed rule class and some mandatory providers missing => reject.
- if (notAllowedRuleClassesMessage != null) {
- unfulfilledRequirements.add(notAllowedRuleClassesMessage);
- }
+ unfulfilledRequirements.add(
+ String.format(
+ "'%s' does not have mandatory providers: %s",
+ prerequisite.getLabel(),
+ prerequisite.missingProviders(requiredProviders).getDescription()));
- if (!unfulfilledRequirements.isEmpty()) {
- attributeError(
- attribute.getName(), StringUtil.joinEnglishList(unfulfilledRequirements, "and"));
- }
+ return false;
}
private void validateDirectPrerequisite(Attribute attribute, ConfiguredTarget prerequisite) {