Skip to content

Commit 57ccfd7

Browse files
keithcopybara-github
authored andcommitted
Validate conflicting constraints in target_compatible_with
This makes sure a target's target_compatible_with doesn't include impossible to satisfy constraints via 2 constraint_values for the same constraint_setting Fixes #27580 Closes #27583. PiperOrigin-RevId: 839335518 Change-Id: I4d422ad7cb60662745fee176245acdb28a735136
1 parent 44665ec commit 57ccfd7

File tree

4 files changed

+61
-6
lines changed

4 files changed

+61
-6
lines changed

src/main/java/com/google/devtools/build/lib/analysis/constraints/IncompatibleTargetChecker.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
3232
import com.google.devtools.build.lib.analysis.config.ConfigConditions;
3333
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
34+
import com.google.devtools.build.lib.analysis.platform.ConstraintCollection;
3435
import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
3536
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
3637
import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils;
@@ -106,6 +107,8 @@ public static class IncompatibleTargetProducer implements StateMachine, Consumer
106107

107108
private final StateMachine runAfter;
108109

110+
private final ImmutableList.Builder<ConstraintValueInfo> allConstraintValuesBuilder =
111+
new ImmutableList.Builder<>();
109112
private final ImmutableList.Builder<ConstraintValueInfo> invalidConstraintValuesBuilder =
110113
new ImmutableList.Builder<>();
111114

@@ -170,13 +173,26 @@ public StateMachine step(Tasks tasks) {
170173
public void accept(SkyValue value) {
171174
var configuredTarget = ((ConfiguredTargetValue) value).getConfiguredTarget();
172175
@Nullable ConstraintValueInfo info = PlatformProviderUtils.constraintValue(configuredTarget);
173-
if (info == null || platformInfo.constraints().hasConstraintValue(info)) {
176+
if (info == null) {
174177
return;
175178
}
176-
invalidConstraintValuesBuilder.add(info);
179+
allConstraintValuesBuilder.add(info);
180+
if (!platformInfo.constraints().hasConstraintValue(info)) {
181+
invalidConstraintValuesBuilder.add(info);
182+
}
177183
}
178184

179185
private StateMachine processResult(Tasks tasks) {
186+
var allConstraintValues = allConstraintValuesBuilder.build();
187+
188+
// Validate that there are no duplicate constraint values from the same constraint setting
189+
try {
190+
ConstraintCollection.validateConstraints(allConstraintValues);
191+
} catch (ConstraintCollection.DuplicateConstraintException e) {
192+
sink.acceptValidationException(new ValidationException(e.getMessage()));
193+
return runAfter;
194+
}
195+
180196
var invalidConstraintValues = invalidConstraintValuesBuilder.build();
181197
if (!invalidConstraintValues.isEmpty()) {
182198
sink.acceptIncompatibleTarget(

src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintCollection.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,16 @@ public void addToFingerprint(Fingerprint fp) {
284284
constraints().values().forEach(constraintValue -> constraintValue.addTo(fp));
285285
}
286286

287+
/**
288+
* Validates that the given constraints do not contain conflicting values.
289+
*
290+
* <p>Checks that no {@link ConstraintSettingInfo} has multiple different {@link
291+
* ConstraintValueInfo} values. Multiple instances of the same constraint value are allowed.
292+
*
293+
* @param constraintValues the constraints to validate
294+
* @throws DuplicateConstraintException if multiple different constraint values exist for the same
295+
* constraint setting
296+
*/
287297
public static void validateConstraints(Iterable<ConstraintValueInfo> constraintValues)
288298
throws DuplicateConstraintException {
289299
// Collect the constraints by the settings.
@@ -292,12 +302,14 @@ public static void validateConstraints(Iterable<ConstraintValueInfo> constraintV
292302
.collect(
293303
toImmutableListMultimap(ConstraintValueInfo::constraint, Functions.identity()));
294304

295-
// Find settings with duplicate values.
305+
// Find different constraint values targeting the same constraint setting.
306+
// Ignore multiple instances of the same constraint value.
296307
ImmutableListMultimap<ConstraintSettingInfo, ConstraintValueInfo> duplicates =
297308
constraints.asMap().entrySet().stream()
298-
.filter(e -> e.getValue().size() > 1)
309+
.filter(e -> e.getValue().stream().distinct().count() > 1)
299310
.collect(
300-
flatteningToImmutableListMultimap(Map.Entry::getKey, e -> e.getValue().stream()));
311+
flatteningToImmutableListMultimap(
312+
Map.Entry::getKey, e -> e.getValue().stream().distinct()));
301313

302314
if (!duplicates.isEmpty()) {
303315
throw new DuplicateConstraintException(duplicates);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public void validateAttributes() throws ValidationException {
126126

127127
/** ValidationException indicates an error during attribute validation. */
128128
public static final class ValidationException extends Exception {
129-
private ValidationException(String message) {
129+
public ValidationException(String message) {
130130
super(message);
131131
}
132132
}

src/test/shell/integration/target_compatible_with_test.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,33 @@ EOF
289289
expect_log "'//target_skipping:foo3_config_setting' does not have mandatory providers: 'ConstraintValueInfo'"
290290
}
291291

292+
# Validates that we get an error when target_compatible_with contains duplicate
293+
# constraint values from the same constraint setting. This is a regression test
294+
# for https://github.com/bazelbuild/bazel/issues/27580.
295+
function test_duplicate_constraint_values_in_target_compatible_with() {
296+
cat >> target_skipping/BUILD <<'EOF'
297+
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
298+
299+
sh_binary(
300+
name = "target_with_duplicate_constraints",
301+
srcs = ["pass.sh"],
302+
target_compatible_with = [
303+
":foo1",
304+
":foo2",
305+
],
306+
)
307+
EOF
308+
309+
cd target_skipping || fail "couldn't cd into workspace"
310+
311+
bazel build \
312+
//target_skipping:target_with_duplicate_constraints &> "${TEST_log}" \
313+
&& fail "Bazel succeeded unexpectedly."
314+
315+
expect_log "Duplicate constraint values detected: constraint_setting //target_skipping:foo_version has \[//target_skipping:foo1, //target_skipping:foo2\]"
316+
expect_log "ERROR: Analysis of target '//target_skipping:target_with_duplicate_constraints' failed"
317+
}
318+
292319
# Validates that the console log provides useful information to the user for
293320
# builds.
294321
function test_console_log_for_builds() {

0 commit comments

Comments
 (0)