Skip to content

Commit 4ae8538

Browse files
philscShreeM01
andauthored
Prevent aspects from executing on incompatible targets (#15984)
* Prevent aspects from executing on incompatible targets This patch prevents aspect functions from being evaluated when their associated targets are incompatible. Otherwise aspects can generate errors that should never be printed at all. For example, an aspect evaluating an incompatible target may generate errors about missing providers. This is not the desired behaviour. The implementation in this patch short-circuits the `AspectValue` creation if the associated target is incompatible. I had tried to add an `IncompatiblePlatformProvider` to the corresponding `ConfiguredAspect` instance, but then Bazel complained about having duplicate `IncompatiblePlatformProvider` instances. Fixes #15271 (cherry picked from commit 6d71850) * Fix build and test Co-authored-by: kshyanashree <[email protected]>
1 parent a24d1bc commit 4ae8538

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.google.devtools.build.lib.analysis.DependencyKind;
3737
import com.google.devtools.build.lib.analysis.DuplicateException;
3838
import com.google.devtools.build.lib.analysis.ExecGroupCollection.InvalidExecGroupException;
39+
import com.google.devtools.build.lib.analysis.IncompatiblePlatformProvider;
3940
import com.google.devtools.build.lib.analysis.InconsistentAspectOrderException;
4041
import com.google.devtools.build.lib.analysis.PlatformOptions;
4142
import com.google.devtools.build.lib.analysis.ResolvedToolchainContext;
@@ -292,6 +293,20 @@ public SkyValue compute(SkyKey skyKey, Environment env)
292293
throw new IllegalStateException("Name already verified", e);
293294
}
294295

296+
// If the target is incompatible, then there's not much to do. The intent here is to create an
297+
// AspectValue that doesn't trigger any of the associated target's dependencies to be evaluated
298+
// against this aspect.
299+
if (associatedTarget.get(IncompatiblePlatformProvider.PROVIDER) != null) {
300+
return new AspectValue(
301+
key,
302+
aspect,
303+
target.getLocation(),
304+
ConfiguredAspect.forNonapplicableTarget(),
305+
/*transitivePackagesForPackageRootResolution=*/ NestedSetBuilder
306+
.<Package>stableOrder()
307+
.build());
308+
}
309+
295310
if (AliasProvider.isAlias(associatedTarget)) {
296311
return createAliasAspect(
297312
env,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ java_library(
258258
"//src/main/java/com/google/devtools/build/lib/analysis:duplicate_exception",
259259
"//src/main/java/com/google/devtools/build/lib/analysis:exec_group_collection",
260260
"//src/main/java/com/google/devtools/build/lib/analysis:extra_action_artifacts_provider",
261+
"//src/main/java/com/google/devtools/build/lib/analysis:incompatible_platform_provider",
261262
"//src/main/java/com/google/devtools/build/lib/analysis:inconsistent_aspect_order_exception",
262263
"//src/main/java/com/google/devtools/build/lib/analysis:platform_configuration",
263264
"//src/main/java/com/google/devtools/build/lib/analysis:platform_options",

src/test/shell/integration/target_compatible_with_test.sh

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,4 +1066,163 @@ function test_aquery_incompatible_target() {
10661066
expect_log "target platform (//target_skipping:foo3_platform) didn't satisfy constraint //target_skipping:foo1"
10671067
}
10681068
1069+
# Use aspects to interact with incompatible targets and validate the behaviour.
1070+
function test_aspect_skipping() {
1071+
cat >> target_skipping/BUILD <<EOF
1072+
load(":defs.bzl", "basic_rule", "rule_with_aspect")
1073+
# This target is compatible with all platforms and configurations. This target
1074+
# exists to validate the behaviour of aspects running against incompatible
1075+
# targets. The expectation is that the aspect should _not_ propagate to this
1076+
# compatible target from an incomaptible target. I.e. an aspect should _not_
1077+
# evaluate this target if "basic_foo3_target" is incompatible.
1078+
basic_rule(
1079+
name = "basic_universal_target",
1080+
)
1081+
# An alias to validate that incompatible target skipping works as expected with
1082+
# aliases and aspects.
1083+
alias(
1084+
name = "aliased_basic_universal_target",
1085+
actual = ":basic_universal_target",
1086+
)
1087+
basic_rule(
1088+
name = "basic_foo3_target",
1089+
deps = [
1090+
":aliased_basic_universal_target",
1091+
],
1092+
target_compatible_with = [
1093+
":foo3",
1094+
],
1095+
)
1096+
# This target is only compatible when "basic_foo3_target" is compatible. This
1097+
# target exists to validate the behaviour of aspects running against
1098+
# incompatible targets. The expectation is that the aspect should _not_
1099+
# evaluate this target when "basic_foo3_target" is incompatible.
1100+
basic_rule(
1101+
name = "other_basic_target",
1102+
deps = [
1103+
":basic_foo3_target",
1104+
],
1105+
)
1106+
alias(
1107+
name = "aliased_other_basic_target",
1108+
actual = ":other_basic_target",
1109+
)
1110+
rule_with_aspect(
1111+
name = "inspected_foo3_target",
1112+
inspect = ":aliased_other_basic_target",
1113+
)
1114+
basic_rule(
1115+
name = "previously_inspected_basic_target",
1116+
deps = [
1117+
":inspected_foo3_target",
1118+
],
1119+
)
1120+
rule_with_aspect(
1121+
name = "twice_inspected_foo3_target",
1122+
inspect = ":previously_inspected_basic_target",
1123+
)
1124+
genrule(
1125+
name = "generated_file",
1126+
outs = ["generated_file.txt"],
1127+
cmd = "echo '' > \$(OUTS)",
1128+
target_compatible_with = [
1129+
":foo1",
1130+
],
1131+
)
1132+
rule_with_aspect(
1133+
name = "inspected_generated_file",
1134+
inspect = ":generated_file",
1135+
)
1136+
EOF
1137+
cat > target_skipping/defs.bzl <<EOF
1138+
BasicProvider = provider()
1139+
def _basic_rule_impl(ctx):
1140+
return [DefaultInfo(), BasicProvider()]
1141+
basic_rule = rule(
1142+
implementation = _basic_rule_impl,
1143+
attrs = {
1144+
"deps": attr.label_list(
1145+
providers = [BasicProvider],
1146+
),
1147+
},
1148+
)
1149+
def _inspecting_aspect_impl(target, ctx):
1150+
print("Running aspect on " + str(target))
1151+
return []
1152+
_inspecting_aspect = aspect(
1153+
implementation = _inspecting_aspect_impl,
1154+
attr_aspects = ["deps"],
1155+
)
1156+
def _rule_with_aspect_impl(ctx):
1157+
out = ctx.actions.declare_file(ctx.label.name)
1158+
ctx.actions.write(out, "")
1159+
return [
1160+
DefaultInfo(files=depset([out])),
1161+
BasicProvider(),
1162+
]
1163+
rule_with_aspect = rule(
1164+
implementation = _rule_with_aspect_impl,
1165+
attrs = {
1166+
"inspect": attr.label(
1167+
aspects = [_inspecting_aspect],
1168+
),
1169+
},
1170+
)
1171+
EOF
1172+
cd target_skipping || fail "couldn't cd into workspace"
1173+
local debug_message1="Running aspect on <target //target_skipping:basic_universal_target>"
1174+
local debug_message2="Running aspect on <target //target_skipping:basic_foo3_target>"
1175+
local debug_message3="Running aspect on <target //target_skipping:other_basic_target>"
1176+
local debug_message4="Running aspect on <target //target_skipping:previously_inspected_basic_target>"
1177+
local debug_message5="Running aspect on <target //target_skipping:generated_file>"
1178+
# Validate that aspects run against compatible targets.
1179+
bazel build \
1180+
--show_result=10 \
1181+
--host_platform=@//target_skipping:foo3_platform \
1182+
--platforms=@//target_skipping:foo3_platform \
1183+
//target_skipping:all &> "${TEST_log}" \
1184+
|| fail "Bazel failed unexpectedly."
1185+
expect_log "${debug_message1}"
1186+
expect_log "${debug_message2}"
1187+
expect_log "${debug_message3}"
1188+
expect_log "${debug_message4}"
1189+
expect_not_log "${debug_message5}"
1190+
# Invert the compatibility and validate that aspects run on the other targets
1191+
# now.
1192+
bazel build \
1193+
--show_result=10 \
1194+
--host_platform=@//target_skipping:foo1_bar1_platform \
1195+
--platforms=@//target_skipping:foo1_bar1_platform \
1196+
//target_skipping:all &> "${TEST_log}" \
1197+
|| fail "Bazel failed unexpectedly."
1198+
expect_not_log "${debug_message1}"
1199+
expect_not_log "${debug_message2}"
1200+
expect_not_log "${debug_message3}"
1201+
expect_not_log "${debug_message4}"
1202+
expect_log "${debug_message5}"
1203+
# Validate that explicitly trying to build a target with an aspect against an
1204+
# incompatible target produces the normal error message.
1205+
bazel build \
1206+
--show_result=10 \
1207+
--host_platform=@//target_skipping:foo1_bar1_platform \
1208+
--platforms=@//target_skipping:foo1_bar1_platform \
1209+
//target_skipping:twice_inspected_foo3_target &> "${TEST_log}" \
1210+
&& fail "Bazel passed unexpectedly."
1211+
# TODO(#15427): Should use expect_log_once here when the issue is fixed.
1212+
expect_log 'ERROR: Target //target_skipping:twice_inspected_foo3_target is incompatible and cannot be built, but was explicitly requested.'
1213+
expect_log '^Dependency chain:$'
1214+
expect_log '^ //target_skipping:twice_inspected_foo3_target$'
1215+
expect_log '^ //target_skipping:previously_inspected_basic_target$'
1216+
expect_log '^ //target_skipping:inspected_foo3_target$'
1217+
expect_log '^ //target_skipping:aliased_other_basic_target$'
1218+
expect_log '^ //target_skipping:other_basic_target$'
1219+
expect_log " //target_skipping:basic_foo3_target <-- target platform (//target_skipping:foo1_bar1_platform) didn't satisfy constraint //target_skipping:foo3$"
1220+
expect_log 'FAILED: Build did NOT complete successfully'
1221+
expect_not_log "${debug_message1}"
1222+
expect_not_log "${debug_message2}"
1223+
expect_not_log "${debug_message3}"
1224+
expect_not_log "${debug_message4}"
1225+
expect_not_log "${debug_message5}"
1226+
}
1227+
10691228
run_suite "target_compatible_with tests"

0 commit comments

Comments
 (0)