Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 35 additions & 32 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -1243,38 +1243,6 @@ def construct_arguments(
uniquify = True,
)

# `rust_flags` is either a plain `list[str | (format_string, File)]` or a
# `ctx.actions.args()` `Args` object.
#
# - Lists are folded into the main `rustc_flags` `Args` here, with any
# `-Zallow-features=` entries extracted into
# `all_allowed_unstable_features` so they can be merged with
# `unstable_rust_features_config` and re-emitted as a single arg below.
#
# - `Args` inputs cannot be merged with another `Args` and are opaque at
# analysis time, so we capture the caller's `Args` here and append it as a
# separate entry in `args.all` (after the main `rustc_flags` `Args`,
# consistent with the existing "later flags win" semantics). Any
# `-Zallow-features=` baked into an `Args` value passes through to rustc
# unchanged — callers that need it merged with
# `unstable_rust_features_config` should keep using the list form.
rust_flags_args = None
if type(rust_flags) == "Args":
rust_flags_args = rust_flags
elif rust_flags:
for flag in _extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features):
if type(flag) in ["tuple", "list"] and len(flag) == 2:
rustc_flags.add_all(
[flag[1]],
format_each = flag[0],
expand_directories = False,
)
else:
if map_flag:
flag = map_flag(flag)
if flag != None:
rustc_flags.add(flag)

# Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test
# Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
data_paths = depset(direct = getattr(attr, "data", []), transitive = [crate_info.compile_data_targets]).to_list()
Expand Down Expand Up @@ -1448,6 +1416,41 @@ def construct_arguments(
if is_no_std(ctx, toolchain, crate_info.is_test):
rustc_flags.add('--cfg=feature="no_std"')

# Add user-provided `rust_flags` last, so they can override the flags above,
# but before the target-provided authored_rustc_flags.
#
# `rust_flags` is either a plain `list[str | (format_string, File)]` or a
# `ctx.actions.args()` `Args` object.
#
# - Lists are folded into the main `rustc_flags` `Args` here, with any
# `-Zallow-features=` entries extracted into
# `all_allowed_unstable_features` so they can be merged with
# `unstable_rust_features_config` and re-emitted as a single arg below.
#
# - `Args` inputs cannot be merged with another `Args` and are opaque at
# analysis time, so we capture the caller's `Args` here and append it as a
# separate entry in `args.all` (after the main `rustc_flags` `Args`,
# consistent with the existing "later flags win" semantics). Any
# `-Zallow-features=` baked into an `Args` value passes through to rustc
# unchanged — callers that need it merged with
# `unstable_rust_features_config` should keep using the list form.
rust_flags_args = None
if type(rust_flags) == "Args":
rust_flags_args = rust_flags
elif rust_flags:
for flag in _extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features):
if type(flag) in ["tuple", "list"] and len(flag) == 2:
rustc_flags.add_all(
[flag[1]],
format_each = flag[0],
expand_directories = False,
)
else:
if map_flag:
flag = map_flag(flag)
if flag != None:
rustc_flags.add(flag)

# Add target specific flags last, so they can override previous flags
authored_rustc_flags = getattr(attr, "rustc_flags", [])
rustc_flags.add_all(
Expand Down
5 changes: 5 additions & 0 deletions test/unit/rustc_flag_ordering/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
load(":rustc_flag_ordering_test.bzl", "rustc_flag_ordering_test_suite")

rustc_flag_ordering_test_suite(
name = "rustc_flag_ordering_test_suite",
)
1 change: 1 addition & 0 deletions test/unit/rustc_flag_ordering/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Dummy file for testing
80 changes: 80 additions & 0 deletions test/unit/rustc_flag_ordering/rustc_flag_ordering_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Unittest to verify rustc flag ordering"""

load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load("//rust:defs.bzl", "rust_test")

def assert_argv_order(env, action, expected_flags):
"""Checks that a set of flags appear in the given order.

Checks that the flags in `expected_flags` are in the command line
arguments for `action` in the given order (possibly with other arguments
in between).

Args:
env: env from analysistest.begin(ctx).
action: The action whose command line will be checked.
expected_flags: The expected set of flags, in the expected order.
"""
argv = action.argv
last_idx = -1
for flag in expected_flags:
found_idx = -1
for i in range(last_idx + 1, len(argv)):
if argv[i] == flag:
found_idx = i
break

asserts.true(
env,
found_idx > last_idx,
"Expected flag '{}' to appear after previous flags in argv: {}".format(flag, argv),
)
last_idx = found_idx

def _rustc_flag_ordering_test(ctx):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

action = target.actions[0]
asserts.equals(env, "Rustc", action.mnemonic)

# We expect:
# 1. --edition=2018 (one of the flags added by default by construct_arguments)
# 2. --test (added via rust_flags in rust_test, now moved right before authored flags)
# 3. --cfg=my_authored_flag (added via rustc_flags attribute, added last in construct_arguments)
assert_argv_order(
env,
action,
[
"--edition=2018",
"--test",
"--cfg=my_authored_flag",
],
)

return analysistest.end(env)

rustc_flag_ordering_test = analysistest.make(_rustc_flag_ordering_test)

def _define_test_targets():
rust_test(
name = "test_target",
srcs = ["lib.rs"],
edition = "2018",
rustc_flags = ["--cfg=my_authored_flag"],
)

def rustc_flag_ordering_test_suite(name):
_define_test_targets()

rustc_flag_ordering_test(
name = "rustc_flag_ordering_test",
target_under_test = ":test_target",
)

native.test_suite(
name = name,
tests = [
":rustc_flag_ordering_test",
],
)
Loading