Skip to content

Commit 8482c7c

Browse files
fmeumcopybara-github
authored andcommitted
Check target incompatibility before toolchain resolution
Known missing toolchains are a valid reason for declaring a target incompatible and should not result in the target failing to build rather than being skipped as incompatible. This is realized by moving the check for target incompatibility to the beginning of `PrerequisiteProducer#evaluate`, which requires fetching the target platform and config conditions. Closes #17947. PiperOrigin-RevId: 522534102 Change-Id: I9870edbf9cdb9873641f63610c86eb642f5c1d29
1 parent 153c2bc commit 8482c7c

File tree

8 files changed

+136
-14
lines changed

8 files changed

+136
-14
lines changed

src/main/java/com/google/devtools/build/lib/analysis/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ java_library(
331331
":make_variable_supplier",
332332
":options_diff_predicate",
333333
":package_specification_provider",
334+
":platform_configuration",
334335
":platform_options",
335336
":provider_collection",
336337
":repo_mapping_manifest_action",

src/main/java/com/google/devtools/build/lib/analysis/Util.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ public static ImmutableSet<ConfiguredTargetKey> findImplicitDeps(RuleContext rul
9696
}
9797
}
9898

99+
if (ruleContext.getRule().useToolchainResolution()) {
100+
// Rules that participate in toolchain resolution implicitly depend on the target platform to
101+
// check whether it matches the constraints in the target_compatible_with attribute.
102+
if (ruleContext.getConfiguration().hasFragment(PlatformConfiguration.class)) {
103+
PlatformConfiguration platformConfiguration =
104+
ruleContext.getConfiguration().getFragment(PlatformConfiguration.class);
105+
maybeImplicitDeps.add(
106+
ConfiguredTargetKey.builder()
107+
.setLabel(platformConfiguration.getTargetPlatform())
108+
.setConfiguration(ruleContext.getConfiguration())
109+
.build());
110+
}
111+
}
112+
99113
ToolchainCollection<ResolvedToolchainContext> toolchainContexts =
100114
ruleContext.getToolchainContexts();
101115
if (toolchainContexts != null) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,12 @@ public class IncompatibleTargetChecker {
9898
* </ul>
9999
*/
100100
public static class IncompatibleTargetProducer implements StateMachine, Consumer<SkyValue> {
101+
101102
private final Target target;
102103
@Nullable // Non-null when the target has an associated rule.
103104
private final BuildConfigurationValue configuration;
104105
private final ConfigConditions configConditions;
106+
// Non-null when the target has an associated rule and does not opt out of toolchain resolution.
105107
@Nullable private final PlatformInfo platformInfo;
106108
@Nullable private final NestedSetBuilder<Package> transitivePackages;
107109

@@ -133,7 +135,7 @@ public IncompatibleTargetProducer(
133135
@Override
134136
public StateMachine step(Tasks tasks, ExtendedEventHandler listener) {
135137
Rule rule = target.getAssociatedRule();
136-
if (rule == null || rule.getRuleClass().equals("toolchain") || platformInfo == null) {
138+
if (rule == null || !rule.useToolchainResolution() || platformInfo == null) {
137139
sink.accept(Optional.empty());
138140
return DONE;
139141
}

src/main/java/com/google/devtools/build/lib/skyframe/PrerequisiteProducer.java

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,41 @@ public boolean evaluate(
318318
// would exit this SkyFunction and restart it when permits were available.
319319
semaphoreLocker.acquireSemaphore();
320320
try {
321+
// Check target compatibility before requesting toolchains - known missing toolchains are a
322+
// valid reason for declaring a target incompatible and should not result in the target
323+
// failing to build rather than being skipped as incompatible.
324+
// Non-rule targets and those that are part of the toolchain resolution system do not support
325+
// target compatibility checking anyway.
326+
if (targetAndConfiguration.getTarget() instanceof Rule
327+
&& ((Rule) targetAndConfiguration.getTarget()).useToolchainResolution()) {
328+
platformInfo = loadTargetPlatformInfo(env);
329+
// loadTargetPlatformInfo may return null even when no deps are missing.
330+
if (env.valuesMissing()) {
331+
return false;
332+
}
333+
334+
configConditions =
335+
computeConfigConditions(
336+
env,
337+
targetAndConfiguration,
338+
transitivePackages,
339+
platformInfo,
340+
transitiveRootCauses);
341+
if (configConditions == null) {
342+
return false;
343+
}
344+
345+
if (!checkForIncompatibleTarget(
346+
env,
347+
state,
348+
targetAndConfiguration,
349+
configConditions,
350+
platformInfo,
351+
transitivePackages)) {
352+
return false;
353+
}
354+
}
355+
321356
// Determine what toolchains are needed by this target.
322357
ComputedToolchainContexts result =
323358
computeUnloadedToolchainContexts(
@@ -335,9 +370,17 @@ public boolean evaluate(
335370
unloadedToolchainContexts != null ? unloadedToolchainContexts.getTargetPlatform() : null;
336371

337372
// Get the configuration targets that trigger this rule's configurable attributes.
338-
configConditions =
339-
computeConfigConditions(
340-
env, targetAndConfiguration, transitivePackages, platformInfo, transitiveRootCauses);
373+
// Has been computed as part of checking for incompatible targets at the beginning of this
374+
// function unless the current target is not a rule participating in toolchain resolution.
375+
if (configConditions == null) {
376+
configConditions =
377+
computeConfigConditions(
378+
env,
379+
targetAndConfiguration,
380+
transitivePackages,
381+
platformInfo,
382+
transitiveRootCauses);
383+
}
341384
if (configConditions == null) {
342385
return false;
343386
}
@@ -361,10 +404,6 @@ public boolean evaluate(
361404
getPrioritizedDetailedExitCode(causes)));
362405
}
363406

364-
if (!checkForIncompatibleTarget(env, state, transitivePackages)) {
365-
return false;
366-
}
367-
368407
// Calculate the dependencies of this target.
369408
depValueMap =
370409
computeDependencies(
@@ -405,13 +444,44 @@ public boolean evaluate(
405444
return true;
406445
}
407446

447+
// May return null even when no deps are missing, use env.valuesMissing() to check.
448+
@Nullable
449+
private PlatformInfo loadTargetPlatformInfo(Environment env)
450+
throws InterruptedException, ToolchainException {
451+
PlatformConfiguration platformConfiguration =
452+
targetAndConfiguration.getConfiguration().getFragment(PlatformConfiguration.class);
453+
if (platformConfiguration == null) {
454+
// No restart required in this case.
455+
return null;
456+
}
457+
Label targetPlatformLabel = platformConfiguration.getTargetPlatform();
458+
ConfiguredTargetKey targetPlatformKey =
459+
ConfiguredTargetKey.builder()
460+
.setLabel(targetPlatformLabel)
461+
.setConfiguration(targetAndConfiguration.getConfiguration())
462+
.build();
463+
464+
Map<ConfiguredTargetKey, PlatformInfo> platformInfoMap =
465+
PlatformLookupUtil.getPlatformInfo(ImmutableList.of(targetPlatformKey), env);
466+
if (platformInfoMap == null) {
467+
return null;
468+
}
469+
470+
return platformInfoMap.get(targetPlatformKey);
471+
}
472+
408473
/**
409474
* Checks if a target is incompatible because of its "target_compatible_with" attribute.
410475
*
411476
* @return false if a {@code Skyframe} restart is needed.
412477
*/
413-
private boolean checkForIncompatibleTarget(
414-
Environment env, State state, @Nullable NestedSetBuilder<Package> transitivePackages)
478+
private static boolean checkForIncompatibleTarget(
479+
Environment env,
480+
State state,
481+
TargetAndConfiguration targetAndConfiguration,
482+
@Nullable ConfigConditions configConditions,
483+
@Nullable PlatformInfo targetPlatformInfo,
484+
@Nullable NestedSetBuilder<Package> transitivePackages)
415485
throws InterruptedException, IncompatibleTargetException {
416486
if (state.incompatibleTarget == null) {
417487
if (state.incompatibleTargetProducer == null) {
@@ -421,7 +491,7 @@ private boolean checkForIncompatibleTarget(
421491
targetAndConfiguration.getTarget(),
422492
targetAndConfiguration.getConfiguration(),
423493
configConditions,
424-
platformInfo,
494+
targetPlatformInfo,
425495
transitivePackages,
426496
state));
427497
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.google.devtools.build.lib.pkgcache.LoadingFailureEvent;
4747
import com.google.devtools.build.lib.skyframe.ActionLookupConflictFindingFunction;
4848
import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
49+
import com.google.devtools.build.lib.testutil.TestConstants;
4950
import com.google.devtools.build.lib.testutil.TestConstants.InternalTestExecutionMode;
5051
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
5152
import com.google.devtools.build.lib.util.Pair;
@@ -502,7 +503,9 @@ public void testGetDirectPrerequisites() throws Exception {
502503
Iterable<Label> labels = Iterables.transform(targets, TransitiveInfoCollection::getLabel);
503504
assertThat(labels)
504505
.containsExactly(
505-
Label.parseCanonical("//package:inner"), Label.parseCanonical("//package:file"));
506+
Label.parseCanonical("//package:inner"),
507+
Label.parseCanonical("//package:file"),
508+
Label.parseCanonical(TestConstants.PLATFORM_LABEL));
506509
}
507510

508511
@Test

src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenRuleConfiguredTargetTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,9 @@ public void testToolsAreExecConfiguration() throws Exception {
424424
assertThat(getConfiguration(prereq)).isNull();
425425
foundSetup = true;
426426
break;
427+
case "host":
428+
// Ignore the dependency on the target platform.
429+
break;
427430
default:
428431
fail("unexpected prerequisite " + prereq + " (name: " + name + ")");
429432
}

src/test/java/com/google/devtools/build/lib/metrics/MetricsCollectorTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -619,8 +619,8 @@ public void skymeldNullIncrementalBuild_buildGraphMetricsCollected() throws Exce
619619
addOptions("--experimental_merged_skyframe_analysis_execution");
620620
BuildGraphMetrics expected =
621621
BuildGraphMetrics.newBuilder()
622-
.setActionLookupValueCount(3)
623-
.setActionLookupValueCountNotIncludingAspects(3)
622+
.setActionLookupValueCount(8)
623+
.setActionLookupValueCountNotIncludingAspects(8)
624624
.setActionCount(2)
625625
.setActionCountNotIncludingAspects(2)
626626
.setInputFileConfiguredTargetCount(1)

src/test/shell/integration/target_compatible_with_test.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,4 +1609,33 @@ EOF
16091609
expect_not_log "${debug_message5}"
16101610
}
16111611

1612+
function test_skipping_with_missing_toolchain() {
1613+
mkdir -p missing_toolchain
1614+
1615+
cat > missing_toolchain/BUILD <<'EOF'
1616+
load(":rule.bzl", "my_rule")
1617+
1618+
toolchain_type(name = "my_toolchain_type")
1619+
1620+
my_rule(
1621+
name = "my_rule",
1622+
target_compatible_with = ["@platforms//:incompatible"],
1623+
)
1624+
EOF
1625+
1626+
cat > missing_toolchain/rule.bzl <<'EOF'
1627+
def _my_rule_impl(ctx):
1628+
pass
1629+
1630+
my_rule = rule(
1631+
_my_rule_impl,
1632+
toolchains = ["//missing_toolchain:my_toolchain_type"],
1633+
)
1634+
EOF
1635+
1636+
bazel build --show_result=10 //missing_toolchain:all &> "${TEST_log}" \
1637+
|| fail "Bazel failed unexpectedly."
1638+
expect_log "Target //missing_toolchain:my_rule was skipped"
1639+
}
1640+
16121641
run_suite "target_compatible_with tests"

0 commit comments

Comments
 (0)