Skip to content

Commit d43737f

Browse files
AlessandroPatticopybara-github
authored andcommitted
Allow multiple matching select branches if they resolve to the same value
Select branches that map to the same value can unambiguosly be resolved even when multiple are true. This is currently not allowed and requires the use of constructs like `bazel-skylib`'s `selects.config_setting_group`. With this change, assuming A and B are true, the following is allowed: ``` select({ "//:A": "Value", "//:B": "Value", }) ``` Closes #14874. PiperOrigin-RevId: 523597091 Change-Id: I751809b1b9e11d20a86ae2af4bbdb0228fd3c670
1 parent 5ab5d80 commit d43737f

File tree

5 files changed

+60
-19
lines changed

5 files changed

+60
-19
lines changed

site/en/configure/attributes.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,13 @@ command line. Specifically, `deps` becomes:
7373
targets. By using `select()` in a configurable attribute, the attribute
7474
effectively adopts different values when different conditions hold.
7575

76-
Matches must be unambiguous: either exactly one condition must match or, if
77-
multiple conditions match, one's `values` must be a strict superset of all
78-
others'. For example, `values = {"cpu": "x86", "compilation_mode": "dbg"}` is an
79-
unambiguous specialization of `values = {"cpu": "x86"}`. The built-in condition
80-
[`//conditions:default`](#default-condition) automatically matches when
76+
Matches must be unambiguous: if multiple conditions match then either
77+
* They all resolve to the same value. For example, when running on linux x86, this is unambiguous
78+
`{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}` because both branches resolve to "hello".
79+
* One's `values` is a strict superset of all others'. For example, `values = {"cpu": "x86", "compilation_mode": "dbg"}`
80+
is an unambiguous specialization of `values = {"cpu": "x86"}`.
81+
82+
The built-in condition [`//conditions:default`](#default-condition) automatically matches when
8183
nothing else does.
8284

8385
While this example uses `deps`, `select()` works just as well on `srcs`,
@@ -518,7 +520,7 @@ Unlike `selects.with_or`, different targets can share `:config1_or_2` across
518520
different attributes.
519521
520522
It's an error for multiple conditions to match unless one is an unambiguous
521-
"specialization" of the others. See [here](#configurable-build-example) for details.
523+
"specialization" of the others or they all resolve to the same value. See [here](#configurable-build-example) for details.
522524

523525
## AND chaining {:#and-chaining}
524526

site/en/docs/configurable-attributes.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,13 @@ command line. Specifically, `deps` becomes:
7373
targets. By using `select()` in a configurable attribute, the attribute
7474
effectively adopts different values when different conditions hold.
7575

76-
Matches must be unambiguous: either exactly one condition must match or, if
77-
multiple conditions match, one's `values` must be a strict superset of all
78-
others'. For example, `values = {"cpu": "x86", "compilation_mode": "dbg"}` is an
79-
unambiguous specialization of `values = {"cpu": "x86"}`. The built-in condition
80-
[`//conditions:default`](#default-condition) automatically matches when
76+
Matches must be unambiguous: if multiple conditions match then either
77+
* They all resolve to the same value. For example, when running on linux x86, this is unambiguous
78+
`{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}` because both branches resolve to "hello".
79+
* One's `values` is a strict superset of all others'. For example, `values = {"cpu": "x86", "compilation_mode": "dbg"}`
80+
is an unambiguous specialization of `values = {"cpu": "x86"}`.
81+
82+
The built-in condition [`//conditions:default`](#default-condition) automatically matches when
8183
nothing else does.
8284

8385
While this example uses `deps`, `select()` works just as well on `srcs`,
@@ -518,7 +520,7 @@ Unlike `selects.with_or`, different targets can share `:config1_or_2` across
518520
different attributes.
519521
520522
It's an error for multiple conditions to match unless one is an unambiguous
521-
"specialization" of the others. See [here](#configurable-build-example) for details.
523+
"specialization" of the others or they all resolve to the same value. See [here](#configurable-build-example) for details.
522524

523525
## AND chaining {:#and-chaining}
524526

src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ sh_binary(
690690
demonstrated in Example 2 below.
691691
</li>
692692
<li>If multiple conditions match and one is not a specialization of all the
693-
others, Bazel fails with an error.
693+
others, Bazel fails with an error, unless all conditions resolve to the same value.
694694
</li>
695695
<li>The special pseudo-label <code>//conditions:default</code> is
696696
considered to match if no other condition matches. If this condition

src/main/java/com/google/devtools/build/lib/packages/ConfiguredAttributeMapper.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,10 @@ private static class ConfigKeyAndValue<T> {
173173

174174
private <T> ConfigKeyAndValue<T> resolveSelector(String attributeName, Selector<T> selector)
175175
throws ValidationException {
176-
Map<Label, ConfigKeyAndValue<T>> matchingConditions = new LinkedHashMap<>();
176+
// Use a LinkedHashMap to guarantee a deterministic branch selection when multiple branches
177+
// matches but they
178+
// resolve to the same value.
179+
LinkedHashMap<Label, ConfigKeyAndValue<T>> matchingConditions = new LinkedHashMap<>();
177180
// Use a LinkedHashSet to guarantee deterministic error message ordering. We use a LinkedHashSet
178181
// vs. a more general SortedSet because the latter supports insertion-order, which should more
179182
// closely match how users see select() structures in BUILD files.
@@ -217,17 +220,19 @@ private <T> ConfigKeyAndValue<T> resolveSelector(String attributeName, Selector<
217220
}
218221
});
219222

220-
if (matchingConditions.size() > 1) {
223+
if (matchingConditions.values().stream().map(s -> s.value).distinct().count() > 1) {
221224
throw new ValidationException(
222225
"Illegal ambiguous match on configurable attribute \""
223226
+ attributeName
224227
+ "\" in "
225228
+ getLabel()
226229
+ ":\n"
227230
+ Joiner.on("\n").join(matchingConditions.keySet())
228-
+ "\nMultiple matches are not allowed unless one is unambiguously more specialized.");
229-
} else if (matchingConditions.size() == 1) {
230-
return Iterables.getOnlyElement(matchingConditions.values());
231+
+ "\nMultiple matches are not allowed unless one is unambiguously "
232+
+ "more specialized or they resolve to the same value. "
233+
+ "See https://bazel.build/reference/be/functions#select.");
234+
} else if (matchingConditions.size() > 0) {
235+
return Iterables.getFirst(matchingConditions.values(), null);
231236
}
232237

233238
// If nothing matched, choose the default condition.

src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,8 @@ public void multipleMatches() throws Exception {
642642
"Illegal ambiguous match on configurable attribute \"srcs\" in //a:gen:\n"
643643
+ "//conditions:dup1\n"
644644
+ "//conditions:dup2\n"
645-
+ "Multiple matches are not allowed unless one is unambiguously more specialized.");
645+
+ "Multiple matches are not allowed unless one is unambiguously more specialized "
646+
+ "or they resolve to the same value.");
646647
}
647648

648649
/**
@@ -688,6 +689,37 @@ public void multipleMatchesConditionAndSubcondition() throws Exception {
688689
"bin java/a/libgeneric.jar", "bin java/a/libprecise.jar"));
689690
}
690691

692+
/** Tests that multiple matches are allowed for conditions where the value is the same. */
693+
@Test
694+
public void multipleMatchesSameValue() throws Exception {
695+
reporter.removeHandler(failFastHandler); // Expect errors.
696+
scratch.file(
697+
"conditions/BUILD",
698+
"config_setting(",
699+
" name = 'dup1',",
700+
" values = {'compilation_mode': 'opt'})",
701+
"config_setting(",
702+
" name = 'dup2',",
703+
" values = {'define': 'foo=bar'})");
704+
scratch.file(
705+
"a/BUILD",
706+
"genrule(",
707+
" name = 'gen',",
708+
" cmd = '',",
709+
" outs = ['gen.out'],",
710+
" srcs = select({",
711+
" '//conditions:dup1': ['a.in'],",
712+
" '//conditions:dup2': ['a.in'],",
713+
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "': [':default.in'],",
714+
" }))");
715+
checkRule(
716+
"//a:gen",
717+
"srcs",
718+
ImmutableList.of("-c", "opt", "--define", "foo=bar"),
719+
/*expected:*/ ImmutableList.of("src a/a.in"),
720+
/*not expected:*/ ImmutableList.of("src a/default.in"));
721+
}
722+
691723
/**
692724
* Tests that when multiple conditions match but one condition is more specialized than the
693725
* others, it is chosen and there is no error.

0 commit comments

Comments
 (0)