Skip to content

Commit 6c5cc32

Browse files
authored
Move flags passed in rust_flags near the end of the rustc invocation. (#4135)
This allows callers of rustc_compile_action to override the default flags. The motivating example for making this change was some path remapping done by Crubit (https://github.com/google/crubit/blob/685a46aabbdf6552803c5bd77da6628008d0d245/rs_bindings_from_cc/bazel_support/compile_rust.bzl#L100). PR #4106 added another `--remap-path-prefix` flag after the code that adds `rust_flags` to the rustc invocation, and the new remapping took precedence.
1 parent 5786455 commit 6c5cc32

4 files changed

Lines changed: 121 additions & 32 deletions

File tree

rust/private/rustc.bzl

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,38 +1243,6 @@ def construct_arguments(
12431243
uniquify = True,
12441244
)
12451245

1246-
# `rust_flags` is either a plain `list[str | (format_string, File)]` or a
1247-
# `ctx.actions.args()` `Args` object.
1248-
#
1249-
# - Lists are folded into the main `rustc_flags` `Args` here, with any
1250-
# `-Zallow-features=` entries extracted into
1251-
# `all_allowed_unstable_features` so they can be merged with
1252-
# `unstable_rust_features_config` and re-emitted as a single arg below.
1253-
#
1254-
# - `Args` inputs cannot be merged with another `Args` and are opaque at
1255-
# analysis time, so we capture the caller's `Args` here and append it as a
1256-
# separate entry in `args.all` (after the main `rustc_flags` `Args`,
1257-
# consistent with the existing "later flags win" semantics). Any
1258-
# `-Zallow-features=` baked into an `Args` value passes through to rustc
1259-
# unchanged — callers that need it merged with
1260-
# `unstable_rust_features_config` should keep using the list form.
1261-
rust_flags_args = None
1262-
if type(rust_flags) == "Args":
1263-
rust_flags_args = rust_flags
1264-
elif rust_flags:
1265-
for flag in _extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features):
1266-
if type(flag) in ["tuple", "list"] and len(flag) == 2:
1267-
rustc_flags.add_all(
1268-
[flag[1]],
1269-
format_each = flag[0],
1270-
expand_directories = False,
1271-
)
1272-
else:
1273-
if map_flag:
1274-
flag = map_flag(flag)
1275-
if flag != None:
1276-
rustc_flags.add(flag)
1277-
12781246
# Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test
12791247
# Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
12801248
data_paths = depset(direct = getattr(attr, "data", []), transitive = [crate_info.compile_data_targets]).to_list()
@@ -1448,6 +1416,41 @@ def construct_arguments(
14481416
if is_no_std(ctx, toolchain, crate_info.is_test):
14491417
rustc_flags.add('--cfg=feature="no_std"')
14501418

1419+
# Add user-provided `rust_flags` last, so they can override the flags above,
1420+
# but before the target-provided authored_rustc_flags.
1421+
#
1422+
# `rust_flags` is either a plain `list[str | (format_string, File)]` or a
1423+
# `ctx.actions.args()` `Args` object.
1424+
#
1425+
# - Lists are folded into the main `rustc_flags` `Args` here, with any
1426+
# `-Zallow-features=` entries extracted into
1427+
# `all_allowed_unstable_features` so they can be merged with
1428+
# `unstable_rust_features_config` and re-emitted as a single arg below.
1429+
#
1430+
# - `Args` inputs cannot be merged with another `Args` and are opaque at
1431+
# analysis time, so we capture the caller's `Args` here and append it as a
1432+
# separate entry in `args.all` (after the main `rustc_flags` `Args`,
1433+
# consistent with the existing "later flags win" semantics). Any
1434+
# `-Zallow-features=` baked into an `Args` value passes through to rustc
1435+
# unchanged — callers that need it merged with
1436+
# `unstable_rust_features_config` should keep using the list form.
1437+
rust_flags_args = None
1438+
if type(rust_flags) == "Args":
1439+
rust_flags_args = rust_flags
1440+
elif rust_flags:
1441+
for flag in _extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features):
1442+
if type(flag) in ["tuple", "list"] and len(flag) == 2:
1443+
rustc_flags.add_all(
1444+
[flag[1]],
1445+
format_each = flag[0],
1446+
expand_directories = False,
1447+
)
1448+
else:
1449+
if map_flag:
1450+
flag = map_flag(flag)
1451+
if flag != None:
1452+
rustc_flags.add(flag)
1453+
14511454
# Add target specific flags last, so they can override previous flags
14521455
authored_rustc_flags = getattr(attr, "rustc_flags", [])
14531456
rustc_flags.add_all(
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
load(":rustc_flag_ordering_test.bzl", "rustc_flag_ordering_test_suite")
2+
3+
rustc_flag_ordering_test_suite(
4+
name = "rustc_flag_ordering_test_suite",
5+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Dummy file for testing
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Unittest to verify rustc flag ordering"""
2+
3+
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
4+
load("//rust:defs.bzl", "rust_test")
5+
6+
def assert_argv_order(env, action, expected_flags):
7+
"""Checks that a set of flags appear in the given order.
8+
9+
Checks that the flags in `expected_flags` are in the command line
10+
arguments for `action` in the given order (possibly with other arguments
11+
in between).
12+
13+
Args:
14+
env: env from analysistest.begin(ctx).
15+
action: The action whose command line will be checked.
16+
expected_flags: The expected set of flags, in the expected order.
17+
"""
18+
argv = action.argv
19+
last_idx = -1
20+
for flag in expected_flags:
21+
found_idx = -1
22+
for i in range(last_idx + 1, len(argv)):
23+
if argv[i] == flag:
24+
found_idx = i
25+
break
26+
27+
asserts.true(
28+
env,
29+
found_idx > last_idx,
30+
"Expected flag '{}' to appear after previous flags in argv: {}".format(flag, argv),
31+
)
32+
last_idx = found_idx
33+
34+
def _rustc_flag_ordering_test(ctx):
35+
env = analysistest.begin(ctx)
36+
target = analysistest.target_under_test(env)
37+
38+
action = target.actions[0]
39+
asserts.equals(env, "Rustc", action.mnemonic)
40+
41+
# We expect:
42+
# 1. --edition=2018 (one of the flags added by default by construct_arguments)
43+
# 2. --test (added via rust_flags in rust_test, now moved right before authored flags)
44+
# 3. --cfg=my_authored_flag (added via rustc_flags attribute, added last in construct_arguments)
45+
assert_argv_order(
46+
env,
47+
action,
48+
[
49+
"--edition=2018",
50+
"--test",
51+
"--cfg=my_authored_flag",
52+
],
53+
)
54+
55+
return analysistest.end(env)
56+
57+
rustc_flag_ordering_test = analysistest.make(_rustc_flag_ordering_test)
58+
59+
def _define_test_targets():
60+
rust_test(
61+
name = "test_target",
62+
srcs = ["lib.rs"],
63+
edition = "2018",
64+
rustc_flags = ["--cfg=my_authored_flag"],
65+
)
66+
67+
def rustc_flag_ordering_test_suite(name):
68+
_define_test_targets()
69+
70+
rustc_flag_ordering_test(
71+
name = "rustc_flag_ordering_test",
72+
target_under_test = ":test_target",
73+
)
74+
75+
native.test_suite(
76+
name = name,
77+
tests = [
78+
":rustc_flag_ordering_test",
79+
],
80+
)

0 commit comments

Comments
 (0)