Properly parse build setting strings in flag_values attr for config_settings
PiperOrigin-RevId: 249047560
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptionConverters.java b/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptionConverters.java
index d0e8ff5..31341ef 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptionConverters.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptionConverters.java
@@ -14,12 +14,24 @@
package com.google.devtools.build.lib.analysis.config;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
+import static com.google.devtools.build.lib.syntax.Type.INTEGER;
+import static com.google.devtools.build.lib.syntax.Type.STRING;
+import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
+
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.common.options.Converter;
+import com.google.devtools.common.options.Converters.BooleanConverter;
+import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
+import com.google.devtools.common.options.Converters.IntegerConverter;
+import com.google.devtools.common.options.Converters.StringConverter;
import com.google.devtools.common.options.EnumConverter;
import com.google.devtools.common.options.OptionsParsingException;
import java.util.Collections;
@@ -32,6 +44,21 @@
* domain-specific (i.e. aren't consumed within a single {@link FragmentOptions}).
*/
public class CoreOptionConverters {
+
+ /**
+ * The set of converters used for {@link com.google.devtools.build.lib.packages.BuildSetting}
+ * value parsing.
+ */
+ public static final ImmutableMap<Type<?>, Converter<?>> BUILD_SETTING_CONVERTERS =
+ new ImmutableMap.Builder<Type<?>, Converter<?>>()
+ .put(INTEGER, new IntegerConverter())
+ .put(BOOLEAN, new BooleanConverter())
+ .put(STRING, new StringConverter())
+ .put(STRING_LIST, new CommaSeparatedOptionListConverter())
+ .put(LABEL, new LabelConverter())
+ .put(LABEL_LIST, new LabelListConverter())
+ .build();
+
/** A converter from strings to Labels. */
public static class LabelConverter implements Converter<Label> {
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRuleClasses.java
index 828f0a7..7522324 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRuleClasses.java
@@ -234,6 +234,13 @@
.add(
attr(DEFINE_SETTINGS_ATTRIBUTE, STRING_DICT)
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
+ // Originally this attribute was a map of feature flags targets -> feature flag values,
+ // the latter of which are always strings. Now it also includes starlark build setting
+ // targets -> starlark build setting values. In other places in the starlark configuration
+ // API, starlark setting values are passed as their actual object instead of a string
+ // representation. It would be more consistent to be able to pass starlark setting values
+ // as objects to this attribute as well. But attributes are strongly-typed so
+ // label->object dict is not an option for attribute types right now.
.add(
attr(FLAG_SETTINGS_ATTRIBUTE, LABEL_KEYED_STRING_DICT)
.undocumented("the feature flag feature has not yet been launched")
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 5d16409..67bdfc5 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,6 +14,8 @@
package com.google.devtools.build.lib.rules.config;
+import static com.google.devtools.build.lib.analysis.config.CoreOptionConverters.BUILD_SETTING_CONVERTERS;
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
@@ -58,7 +60,6 @@
import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.rules.config.ConfigRuleClasses.ConfigSettingRule;
import com.google.devtools.build.lib.syntax.Type;
-import com.google.devtools.build.lib.syntax.Type.ConversionException;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.OptionsParsingException;
@@ -403,6 +404,16 @@
return targetsToAliases.build();
}
+ /**
+ * The 'flag_values' attribute takes a label->string dictionary of feature flags and
+ * starlark-defined settings to their values in string form.
+ *
+ * @param attributeValue map of user-defined flag labels to their values as set in the
+ * 'flag_values' attribute
+ * @param prerequisites targets that match the keyset of the attributeValue map
+ * @param optionDetails information about the configuration to match against
+ * @param errors error consumer
+ */
static UserDefinedFlagMatch fromAttributeValueAndPrerequisites(
Map<Label, String> attributeValue,
Iterable<? extends TransitiveInfoCollection> prerequisites,
@@ -446,8 +457,9 @@
: provider.getDefaultValue();
Object convertedSpecifiedValue;
try {
- convertedSpecifiedValue = provider.getType().convert(specifiedValue, specifiedLabel);
- } catch (ConversionException e) {
+ convertedSpecifiedValue =
+ BUILD_SETTING_CONVERTERS.get(provider.getType()).convert(specifiedValue);
+ } catch (OptionsParsingException e) {
errors.attributeError(
ConfigSettingRule.FLAG_SETTINGS_ATTRIBUTE,
String.format(
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java b/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
index 2fa39b5..aad40d2 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
@@ -14,20 +14,14 @@
package com.google.devtools.build.lib.runtime;
-import static com.google.devtools.build.lib.packages.BuildType.LABEL;
-import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.analysis.config.CoreOptionConverters.BUILD_SETTING_CONVERTERS;
import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
-import static com.google.devtools.build.lib.syntax.Type.INTEGER;
-import static com.google.devtools.build.lib.syntax.Type.STRING;
-import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
-import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.LabelConverter;
-import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.LabelListConverter;
import com.google.devtools.build.lib.cmdline.TargetParsingException;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.events.Reporter;
@@ -43,10 +37,6 @@
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.common.options.Converter;
-import com.google.devtools.common.options.Converters.BooleanConverter;
-import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
-import com.google.devtools.common.options.Converters.IntegerConverter;
-import com.google.devtools.common.options.Converters.StringConverter;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.OptionsParsingException;
import java.util.Collections;
@@ -66,16 +56,6 @@
private final Reporter reporter;
private final OptionsParser nativeOptionsParser;
- private final ImmutableMap<Type<?>, Converter<?>> converters =
- new ImmutableMap.Builder<Type<?>, Converter<?>>()
- .put(INTEGER, new IntegerConverter())
- .put(BOOLEAN, new BooleanConverter())
- .put(STRING, new StringConverter())
- .put(STRING_LIST, new CommaSeparatedOptionListConverter())
- .put(LABEL, new LabelConverter())
- .put(LABEL_LIST, new LabelListConverter())
- .build();
-
private StarlarkOptionsParser(
SkyframeExecutor skyframeExecutor,
PathFragment relativeWorkingDirectory,
@@ -146,7 +126,7 @@
String.format("Unrecognized option: %s=%s", loadedFlag, unparsedValue));
}
Type<?> type = buildSetting.getType();
- Converter<?> converter = converters.get(type);
+ Converter<?> converter = BUILD_SETTING_CONVERTERS.get(type);
Object value;
try {
value = converter.convert(unparsedValue);
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 b40c670..c250a6a 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
@@ -1320,6 +1320,32 @@
assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue();
}
+ /**
+ * Regression test to ensure that non-String typed build setting values are being properly
+ * converted from Strings to their real type.
+ */
+ @Test
+ public void buildsettings_convertedType() throws Exception {
+ setSkylarkSemanticsOptions("--experimental_build_setting_api=true");
+
+ scratch.file(
+ "test/build_settings.bzl",
+ "def _impl(ctx):",
+ " return []",
+ "bool_flag = rule(implementation = _impl, build_setting = config.bool(flag = True))");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:build_settings.bzl', 'bool_flag')",
+ "config_setting(",
+ " name = 'match',",
+ " flag_values = {",
+ " ':cheese': 'True',",
+ " },",
+ ")",
+ "bool_flag(name = 'cheese', build_setting_default = True)");
+ assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue();
+ }
+
@Test
public void buildsettings_doesntMatch() throws Exception {
setSkylarkSemanticsOptions("--experimental_build_setting_api=true");