aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java122
1 files changed, 104 insertions, 18 deletions
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 bdc40fb8cc..1da66e0cb1 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
@@ -14,20 +14,26 @@
package com.google.devtools.build.lib.rules.config;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.LicensesProviderImpl;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.RunfilesProvider;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationOptionDetails;
import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
import com.google.devtools.build.lib.analysis.config.TransitiveOptionDetails;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.Preconditions;
@@ -53,26 +59,44 @@ public class ConfigSetting implements RuleConfiguredTargetFactory {
// Get the required flag=value settings for this rule.
Map<String, String> settings = NonconfigurableAttributeMapper.of(ruleContext.getRule())
.get(ConfigRuleClasses.ConfigSettingRule.SETTINGS_ATTRIBUTE, Type.STRING_DICT);
- if (settings.isEmpty()) {
- ruleContext.attributeError(ConfigRuleClasses.ConfigSettingRule.SETTINGS_ATTRIBUTE,
- "no settings specified");
+ Map<Label, String> flagSettings =
+ NonconfigurableAttributeMapper.of(ruleContext.getRule())
+ .get(
+ ConfigRuleClasses.ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE,
+ BuildType.LABEL_KEYED_STRING_DICT);
+
+ List<? extends TransitiveInfoCollection> flagValues =
+ ruleContext.getPrerequisites(
+ ConfigRuleClasses.ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE, Mode.TARGET);
+
+ ImmutableMap<Label, ConfigFeatureFlagProvider> configProviders =
+ buildConfigFeatureFlagMap(flagValues);
+
+ if (settings.isEmpty() && flagSettings.isEmpty()) {
+ ruleContext.ruleError(
+ String.format(
+ "Either %s or %s must be specified and non-empty",
+ ConfigRuleClasses.ConfigSettingRule.SETTINGS_ATTRIBUTE,
+ ConfigRuleClasses.ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE));
return null;
}
- ConfigMatchingProvider configMatcher;
- try {
- configMatcher =
- new ConfigMatchingProvider(
- ruleContext.getLabel(),
- settings,
- matchesConfig(
- settings, BuildConfigurationOptionDetails.get(ruleContext.getConfiguration())));
- } catch (OptionsParsingException e) {
- ruleContext.attributeError(ConfigRuleClasses.ConfigSettingRule.SETTINGS_ATTRIBUTE,
- "error while parsing configuration settings: " + e.getMessage());
+ boolean flagSettingsMatch = matchesUserConfig(flagSettings, configProviders, ruleContext);
+
+ boolean settingsMatch =
+ matchesConfig(
+ settings,
+ BuildConfigurationOptionDetails.get(ruleContext.getConfiguration()),
+ ruleContext);
+
+ if (ruleContext.hasErrors()) {
return null;
}
+ ConfigMatchingProvider configMatcher =
+ new ConfigMatchingProvider(
+ ruleContext.getLabel(), settings, flagSettings, settingsMatch && flagSettingsMatch);
+
return new RuleConfiguredTargetBuilder(ruleContext)
.addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY)
.addProvider(FileProvider.class, FileProvider.EMPTY)
@@ -82,13 +106,60 @@ public class ConfigSetting implements RuleConfiguredTargetFactory {
.build();
}
+ /** Maps the labels of the given prerequisites to their {@link ConfigFeatureFlagProvider}s. */
+ private static ImmutableMap<Label, ConfigFeatureFlagProvider> buildConfigFeatureFlagMap(
+ Iterable<? extends TransitiveInfoCollection> prerequisites) {
+ ImmutableMap.Builder<Label, ConfigFeatureFlagProvider> output = new ImmutableMap.Builder<>();
+
+ for (TransitiveInfoCollection target : prerequisites) {
+ ConfigFeatureFlagProvider provider = ConfigFeatureFlagProvider.fromTarget(target);
+ // We know the provider exists because only labels with ConfigFeatureFlagProvider can be added
+ // to this attribute.
+ assert provider != null;
+ output.put(target.getLabel(), provider);
+ }
+
+ return output.build();
+ }
+
+ /** Returns whether the actual user-defined flags are set to the specified values. */
+ private static boolean matchesUserConfig(
+ Map<Label, String> specifiedFlags,
+ Map<Label, ConfigFeatureFlagProvider> actualFlags,
+ RuleErrorConsumer errors) {
+ boolean foundMismatch = false;
+ for (Map.Entry<Label, String> specifiedFlag : specifiedFlags.entrySet()) {
+ Label label = specifiedFlag.getKey();
+ String specifiedValue = specifiedFlag.getValue();
+ ConfigFeatureFlagProvider provider = actualFlags.get(label);
+ // Both specifiedFlags and actualFlags are built from the same set of keys; therefore, the
+ // provider will always be present.
+ assert provider != null;
+ if (!provider.isValidValue(specifiedValue)) {
+ errors.attributeError(
+ ConfigRuleClasses.ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE,
+ String.format(
+ "error while parsing user-defined configuration values: "
+ + "'%s' is not a valid value for '%s'",
+ specifiedValue, label));
+ foundMismatch = true;
+ continue;
+ }
+ if (!provider.getValue().equals(specifiedValue)) {
+ foundMismatch = true;
+ }
+ }
+ return !foundMismatch;
+ }
+
/**
* Given a list of [flagName, flagValue] pairs, returns true if flagName == flagValue for every
* item in the list under this configuration, false otherwise.
*/
private boolean matchesConfig(
- Map<String, String> expectedSettings, TransitiveOptionDetails options)
- throws OptionsParsingException {
+ Map<String, String> expectedSettings,
+ TransitiveOptionDetails options,
+ RuleErrorConsumer errors) {
// Rather than returning fast when we find a mismatch, continue looking at the other flags
// to check that they're indeed valid flag specifications.
boolean foundMismatch = false;
@@ -102,7 +173,12 @@ public class ConfigSetting implements RuleConfiguredTargetFactory {
Class<? extends OptionsBase> optionClass = options.getOptionClass(optionName);
if (optionClass == null) {
- throw new OptionsParsingException("unknown option: '" + optionName + "'");
+ errors.attributeError(
+ ConfigRuleClasses.ConfigSettingRule.SETTINGS_ATTRIBUTE,
+ String.format(
+ "error while parsing configuration settings: unknown option: '%s'", optionName));
+ foundMismatch = true;
+ continue;
}
OptionsParser parser = parserCache.get(optionClass);
@@ -110,7 +186,17 @@ public class ConfigSetting implements RuleConfiguredTargetFactory {
parser = OptionsParser.newOptionsParser(optionClass);
parserCache.put(optionClass, parser);
}
- parser.parse("--" + optionName + "=" + expectedRawValue);
+
+ try {
+ parser.parse("--" + optionName + "=" + expectedRawValue);
+ } catch (OptionsParsingException ex) {
+ errors.attributeError(
+ ConfigRuleClasses.ConfigSettingRule.SETTINGS_ATTRIBUTE,
+ "error while parsing configuration settings: " + ex.getMessage());
+ foundMismatch = true;
+ continue;
+ }
+
Object expectedParsedValue = parser.getOptions(optionClass).asMap().get(optionName);
if (!optionMatches(options, optionName, expectedParsedValue)) {