Make config_setting work with list-typed Starlark flags.
Before this change, no list-typed Starlark flag match
any config_setting, period.
After this change, a config_setting expecting value "foo"
matches any list that contains "foo" in any of its entries.
This is consistent with config_setting's existing semantics
for multi-value native flags (like "--define foo --define bar").
Fixes https://github.com/bazelbuild/bazel/issues/8939.
PiperOrigin-RevId: 259370619
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 25aedb6..ebdf33d 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
@@ -154,9 +154,6 @@
/* <!-- #BLAZE_RULE(config_setting).ATTRIBUTE(values) -->
The set of configuration values that match this rule (expressed as Bazel flags)
- <i>(Dictionary mapping flags to expected values, both expressed as strings;
- mandatory)</i>
-
<p>This rule inherits the configuration of the configured target that
references it in a <code>select</code> statement. It is considered to
"match" a Bazel invocation if, for every entry in the dictionary, its
@@ -193,14 +190,9 @@
The same as <a href="${link config_setting.values}"><code>values</code></a> but
specifically for the <code>--define</code> flag.
- <p><code>--define</code> is special for two reasons:
-
- <ol>
- <li>It's the primary interface Bazel has today for declaring user-definable settings.
- </li>
- <li>Its syntax (<code>--define KEY=VAL</code>) means <code>KEY=VAL</code> is
- a <i>value</i> from a Bazel flag perspective.</li>
- </ol>
+ <p><code>--define</code> is special because its syntax (<code>--define KEY=VAL</code>)
+ means <code>KEY=VAL</code> is a <i>value</i> from a Bazel flag perspective.
+ </p>
<p>That means:
@@ -225,7 +217,7 @@
})
</pre>
- <p>corrrectly matches <code>bazel build //foo --define a=1 --define b=2</code>.
+ <p>correctly matches <code>bazel build //foo --define a=1 --define b=2</code>.
<p><code>--define</code> can still appear in
<a href="${link config_setting.values}"><code>values</code></a> with normal flag syntax,
@@ -234,6 +226,13 @@
.add(
attr(DEFINE_SETTINGS_ATTRIBUTE, STRING_DICT)
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
+
+ /* <!-- #BLAZE_RULE(config_setting).ATTRIBUTE(flag_values) -->
+ The same as <a href="${link config_setting.values}"><code>values</code></a> but
+ for <a href="https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings">
+ Starlark-defined flags</a>.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+
// 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
@@ -243,7 +242,6 @@
// 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")
.allowedFileTypes()
.nonconfigurable(NONCONFIGURABLE_ATTRIBUTE_REASON))
/* <!-- #BLAZE_RULE(config_setting).ATTRIBUTE(constraint_values) -->
@@ -349,11 +347,18 @@
match invocations using either form.
</p>
- <p>The currently endorsed method for creating custom conditions that can't be expressed through
- dedicated build flags is through the <code>--define</code> flag. Use this flag with caution:
- it's not ideal and only endorsed for lack of a currently better workaround. See the
- <a href="${link common-definitions#configurable-attributes}">
- Configurable attributes</a> section for more discussion.
+ <p>
+ If a flag takes multiple values (like <code>--define a=b --define c=d</code> or a list-typed
+ <a href="https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings">
+ Starlark flag</a>), a <code>config_setting</code> that expects <code>"foo"</code> matches if
+ <code>"foo"</code> is present <i>anywhere</i> in the actual list.
+ </p>
+
+ <p>If you need to define conditions that aren't modeled by built-in Bazel flags, use
+ <a href="https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings">
+ Starlark-defined flags</a>. You can also use <code>--define</code>, but this offers weaker
+ support and is not recommended. See <a href="${link common-definitions#configurable-attributes}">
+ for more discussion.
</p>
<p>Avoid repeating identical <code>config_setting</code> definitions in different packages.
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 38d2bd9..83d27d5 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
@@ -469,7 +469,21 @@
matches = false;
continue;
}
- if (!configurationValue.equals(convertedSpecifiedValue)) {
+
+ if (configurationValue instanceof List) {
+ // If the build_setting is a list, we use the same semantics as for multi-value native
+ // flags: if *any* entry in the list matches the config_setting's expected entry, it's
+ // a match. In other words, config_setting(flag_values {"//foo": "bar"} matches
+ // //foo=["bar", "baz"].
+
+ // If the config_setting expects "foo", convertedSpecifiedValue converts it to the
+ // flag's native type, which produces ["foo"]. So unpack that again.
+ Object specifiedUnpacked =
+ Iterables.getOnlyElement((Iterable<?>) convertedSpecifiedValue);
+ if (!((List<?>) configurationValue).contains(specifiedUnpacked)) {
+ matches = false;
+ }
+ } else if (!configurationValue.equals(convertedSpecifiedValue)) {
matches = false;
}
} else {
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 c250a6a..4262a6e 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
@@ -1739,4 +1739,101 @@
useConfiguration("--copt", "-Dfoo");
assertThat(getLicenses("//test:match")).containsExactly(LicenseType.NONE);
}
+
+ @Test
+ public void simpleStarlarkFlag() throws Exception {
+ scratch.file(
+ "test/flagdef.bzl",
+ "def _impl(ctx):",
+ " return []",
+ "my_flag = rule(",
+ " implementation = _impl,",
+ " build_setting = config.string(flag = True))");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:flagdef.bzl', 'my_flag')",
+ "my_flag(",
+ " name = 'flag',",
+ " build_setting_default = 'actual_flag_value')",
+ "config_setting(",
+ " name = 'matches',",
+ " flag_values = {",
+ " ':flag': 'actual_flag_value',",
+ " })",
+ "config_setting(",
+ " name = 'doesntmatch',",
+ " flag_values = {",
+ " ':flag': 'other_flag_value',",
+ " })");
+ assertThat(getConfigMatchingProvider("//test:matches").matches()).isTrue();
+ assertThat(getConfigMatchingProvider("//test:doesntmatch").matches()).isFalse();
+ }
+
+ @Test
+ public void starlarkListFlagSingleValue() throws Exception {
+ // When a list-typed Starlark flag has value ["foo"], the config_setting's expected value "foo"
+ // must match exactly.
+ scratch.file(
+ "test/flagdef.bzl",
+ "def _impl(ctx):",
+ " return []",
+ "my_flag = rule(",
+ " implementation = _impl,",
+ " build_setting = config.string_list(flag = True))");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:flagdef.bzl', 'my_flag')",
+ "my_flag(",
+ " name = 'one_value_flag',",
+ " build_setting_default = ['one'])",
+ "config_setting(",
+ " name = 'matches',",
+ " flag_values = {",
+ " ':one_value_flag': 'one',",
+ " })",
+ "config_setting(",
+ " name = 'doesntmatch',",
+ " flag_values = {",
+ " ':one_value_flag': 'other',",
+ " })");
+ assertThat(getConfigMatchingProvider("//test:matches").matches()).isTrue();
+ assertThat(getConfigMatchingProvider("//test:doesntmatch").matches()).isFalse();
+ }
+
+ @Test
+ public void starlarkListFlagMultiValue() throws Exception {
+ // When a list-typed Starlark flag has value ["foo", "bar"], the config_setting's expected
+ // value "foo" must match *any* entry in the list.
+ scratch.file(
+ "test/flagdef.bzl",
+ "def _impl(ctx):",
+ " return []",
+ "my_flag = rule(",
+ " implementation = _impl,",
+ " build_setting = config.string_list(flag = True))");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:flagdef.bzl', 'my_flag')",
+ "my_flag(",
+ " name = 'two_value_flag',",
+ " build_setting_default = ['one', 'two'])",
+ "config_setting(",
+ " name = 'matches_one',",
+ " flag_values = {",
+ " ':two_value_flag': 'one',",
+ " })",
+ "config_setting(",
+ " name = 'matches_two',",
+ " flag_values = {",
+ " ':two_value_flag': 'two',",
+ " })",
+ "config_setting(",
+ " name = 'doesntmatch',",
+ " flag_values = {",
+ " ':two_value_flag': 'other',",
+ " })");
+ assertThat(getConfigMatchingProvider("//test:matches_one").matches()).isTrue();
+ assertThat(getConfigMatchingProvider("//test:matches_two").matches()).isTrue();
+ assertThat(getConfigMatchingProvider("//test:doesntmatch").matches()).isFalse();
+ }
}