Fix config_setting NullPointerException on bad constraint_values reference. Also clean up ConfigSetting.java a little bit. Fixes https://github.com/bazelbuild/bazel/issues/7941 PiperOrigin-RevId: 242180529
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformProviderUtils.java b/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformProviderUtils.java index 68122a4..21ec846 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformProviderUtils.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformProviderUtils.java
@@ -59,6 +59,11 @@ return target.get(ConstraintValueInfo.PROVIDER); } + /** Returns if a target provides {@link ConstraintValueInfo}. * */ + public static boolean hasConstraintValue(ProviderCollection target) { + return target.get(ConstraintValueInfo.PROVIDER) != null; + } + /** Retrieves and casts {@link ConstraintValueInfo} providers from the given targets. */ public static Iterable<ConstraintValueInfo> constraintValues( Iterable<? extends ProviderCollection> targets) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java index 2963dea..51b44d3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java +++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
@@ -46,7 +46,6 @@ import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.platform.ConstraintCollection; import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo; -import com.google.devtools.build.lib.analysis.platform.PlatformInfo; import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; @@ -61,6 +60,7 @@ import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.OptionsParsingException; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; @@ -79,6 +79,7 @@ public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException, RuleErrorException, ActionConflictException { AttributeMap attributes = NonconfigurableAttributeMapper.of(ruleContext.getRule()); + // Get the built-in Blaze flag settings that match this rule. ImmutableMultimap<String, String> nativeFlagSettings = ImmutableMultimap.<String, String>builder() @@ -93,28 +94,17 @@ // Get the user-defined flag settings that match this rule. Map<Label, String> userDefinedFlagSettings = - NonconfigurableAttributeMapper.of(ruleContext.getRule()) - .get( - ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE, - BuildType.LABEL_KEYED_STRING_DICT); + attributes.get( + ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE, BuildType.LABEL_KEYED_STRING_DICT); - List<? extends TransitiveInfoCollection> flagValues = - ruleContext.getPrerequisites( - ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE, Mode.TARGET); - - // Get the constraint values that match this rule - Iterable<ConstraintValueInfo> constraintValues = - PlatformProviderUtils.constraintValues( - ruleContext.getPrerequisites( - ConfigSettingRule.CONSTRAINT_VALUES_ATTRIBUTE, Mode.DONT_CHECK)); - - // Get the target platform - PlatformInfo targetPlatform = ruleContext.getToolchainContext().targetPlatform(); + // Get the platform constraint settings that match this rule. + List<Label> constraintValueSettings = + attributes.get(ConfigSettingRule.CONSTRAINT_VALUES_ATTRIBUTE, BuildType.LABEL_LIST); // Check that this config_setting contains at least one of {values, define_values, // constraint_values} - if (!checkValidConditions( - nativeFlagSettings, userDefinedFlagSettings, constraintValues, ruleContext)) { + if (!valuesAreSet( + nativeFlagSettings, userDefinedFlagSettings, constraintValueSettings, ruleContext)) { return null; } @@ -126,9 +116,11 @@ ConfigFeatureFlagMatch featureFlags = ConfigFeatureFlagMatch.fromAttributeValueAndPrerequisites( - userDefinedFlagSettings, flagValues, ruleContext); + userDefinedFlagSettings, + ruleContext.getPrerequisites(ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE, Mode.TARGET), + ruleContext); - boolean constraintValuesMatch = targetPlatform.constraints().containsAll(constraintValues); + boolean constraintValuesMatch = constraintValuesMatch(ruleContext); if (ruleContext.hasErrors()) { return null; @@ -150,12 +142,26 @@ .build(); } - private boolean checkValidConditions( - ImmutableMultimap<String, String> nativeFlagSettings, - Map<Label, String> userDefinedFlagSettings, - Iterable<ConstraintValueInfo> constraintValues, - RuleErrorConsumer errors) { - if (!valuesAreSet(nativeFlagSettings, userDefinedFlagSettings, constraintValues, errors)) { + /** + * Returns true if all <code>constraint_values</code> settings are valid and match this + * configuration, false otherwise. + * + * <p>May generate rule errors on bad settings (e.g. wrong target types). + */ + boolean constraintValuesMatch(RuleContext ruleContext) { + List<ConstraintValueInfo> constraintValues = new ArrayList<>(); + for (TransitiveInfoCollection dep : + ruleContext.getPrerequisites( + ConfigSettingRule.CONSTRAINT_VALUES_ATTRIBUTE, Mode.DONT_CHECK)) { + if (!PlatformProviderUtils.hasConstraintValue(dep)) { + ruleContext.attributeError( + ConfigSettingRule.CONSTRAINT_VALUES_ATTRIBUTE, + String.format(dep.getLabel() + " is not a constraint_value")); + } else { + constraintValues.add(PlatformProviderUtils.constraintValue(dep)); + } + } + if (ruleContext.hasErrors()) { return false; } @@ -165,12 +171,16 @@ try { ConstraintCollection.validateConstraints(constraintValues); } catch (ConstraintCollection.DuplicateConstraintException e) { - errors.ruleError( + ruleContext.ruleError( ConstraintCollection.DuplicateConstraintException.formatError(e.duplicateConstraints())); return false; } - return true; + return ruleContext + .getToolchainContext() + .targetPlatform() + .constraints() + .containsAll(constraintValues); } private static RepositoryName getToolsRepository(RuleContext ruleContext) { @@ -212,7 +222,7 @@ private boolean valuesAreSet( ImmutableMultimap<String, String> nativeFlagSettings, Map<Label, String> userDefinedFlagSettings, - Iterable<ConstraintValueInfo> constraintValues, + Iterable<Label> constraintValues, RuleErrorConsumer errors) { if (nativeFlagSettings.isEmpty() && userDefinedFlagSettings.isEmpty()
diff --git a/src/test/java/com/google/devtools/build/lib/rules/config/ConfigSettingTest.java b/src/test/java/com/google/devtools/build/lib/rules/config/ConfigSettingTest.java index 3c2c31d..b7e3ddc 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/config/ConfigSettingTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/config/ConfigSettingTest.java
@@ -1429,6 +1429,23 @@ ");"); } + @Test + public void notAConstraintValue() throws Exception { + checkError( + "test", + "match", + "//test:what_am_i is not a constraint_value", + "genrule(", + " name = 'what_am_i',", + " srcs = [],", + " outs = ['the_answer'],", + " cmd = 'echo an eternal enigma > $@')", + "config_setting(", + " name = 'match',", + " constraint_values = [':what_am_i'],", + ")"); + } + private Set<LicenseType> getLicenses(String label) throws Exception { return getTarget(label).getLicense().getLicenseTypes(); }