Skip to content

Commit 12762a7

Browse files
authored
Add support for collecting a target's cfg flags into CrateInfo. (#3603)
This change add a new `cfgs` field to `CrateInfo`. When setting `//rust/settings:collect_cfgs` is set, all cfg flags for a target will be collected into the `cfgs` field.
1 parent 76520e6 commit 12762a7

File tree

8 files changed

+330
-43
lines changed

8 files changed

+330
-43
lines changed

rust/private/providers.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ CrateInfo = provider(
4646
"str, optional: The original crate type for targets generated using a previously defined " +
4747
"crate (typically tests using the `rust_test::crate` attribute)"
4848
),
49+
"cfgs": (
50+
"List[str]: The set of enabled cfgs for this crate. Note that this field is populated only " +
51+
"when @rules_rust//rust/settings:collect_cfgs is set."
52+
),
4953
},
5054
)
5155

rust/private/rust.bzl

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Rust rule implementations"""
1616

1717
load("@bazel_skylib//lib:paths.bzl", "paths")
18+
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
1819
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
1920
load("//rust/private:common.bzl", "COMMON_PROVIDERS", "rust_common")
2021
load(
@@ -24,7 +25,7 @@ load(
2425
"BuildInfo",
2526
"LintsInfo",
2627
)
27-
load("//rust/private:rustc.bzl", "rustc_compile_action")
28+
load("//rust/private:rustc.bzl", "collect_extra_rustc_flags", "is_no_std", "rustc_compile_action")
2829
load(
2930
"//rust/private:utils.bzl",
3031
"can_build_metadata",
@@ -224,6 +225,7 @@ def _rust_library_common(ctx, crate_type):
224225
compile_data = depset(compile_data),
225226
compile_data_targets = depset(ctx.attr.compile_data),
226227
owner = ctx.label,
228+
cfgs = _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test = False),
227229
),
228230
)
229231

@@ -287,6 +289,7 @@ def _rust_binary_impl(ctx):
287289
compile_data = depset(compile_data),
288290
compile_data_targets = depset(ctx.attr.compile_data),
289291
owner = ctx.label,
292+
cfgs = _collect_cfgs(ctx, toolchain, crate_root, ctx.attr.crate_type, crate_is_test = False),
290293
),
291294
)
292295

@@ -413,6 +416,7 @@ def _rust_test_impl(ctx):
413416
compile_data_targets = compile_data_targets,
414417
wrapped_crate_type = crate.type,
415418
owner = ctx.label,
419+
cfgs = _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test = True),
416420
)
417421
else:
418422
crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
@@ -475,6 +479,7 @@ def _rust_test_impl(ctx):
475479
compile_data = depset(compile_data),
476480
compile_data_targets = depset(ctx.attr.compile_data),
477481
owner = ctx.label,
482+
cfgs = _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test = True),
478483
)
479484

480485
providers = rustc_compile_action(
@@ -798,6 +803,10 @@ _common_attrs = {
798803
doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
799804
default = Label("//rust/private:stamp"),
800805
),
806+
"_collect_cfgs": attr.label(
807+
doc = "Enable collection of cfg flags with results stored in CrateInfo.cfgs.",
808+
default = Label("//rust/settings:collect_cfgs"),
809+
),
801810
} | RUSTC_ATTRS | _rustc_allocator_libraries_attrs
802811

803812
_coverage_attrs = {
@@ -1706,3 +1715,34 @@ def _replace_illlegal_chars(name):
17061715
for illegal_char in ["-", "/", "."]:
17071716
name = name.replace(illegal_char, "_")
17081717
return name
1718+
1719+
def _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test):
1720+
"""Collect all cfg flags for a crate but only when @rules_rust//rust/settings:collect_cfgs is set.
1721+
1722+
Cfgs are gathered from the target's own attributes (e.g., rustc_flags, crate_featues, etc.), as
1723+
well as from the toolchain (e.g., toolchain.extra_rustc_flags).
1724+
1725+
Args:
1726+
ctx (ctx): The current rule's context object.
1727+
toolchain (rust_toolchain): The current Rust toolchain.
1728+
crate_root (File): The root file of the crate.
1729+
crate_type (str): The crate type.
1730+
crate_is_test (bool): Whether the crate is a test target or not.
1731+
1732+
Returns:
1733+
List[str]: All cfg flags for the target (or empty list if build setting is unset).
1734+
"""
1735+
1736+
if not (hasattr(ctx.attr, "_collect_cfgs") and ctx.attr._collect_cfgs[BuildSettingInfo].value):
1737+
return []
1738+
1739+
cfgs = {'feature="{}"'.format(feature): True for feature in getattr(ctx.attr, "crate_features", [])}
1740+
1741+
if is_no_std(ctx, toolchain, crate_is_test):
1742+
cfgs['feature="no_std"'] = True
1743+
1744+
rustc_flags = getattr(ctx.attr, "rustc_flags", []) + collect_extra_rustc_flags(ctx, toolchain, crate_root, crate_type)
1745+
cfgs |= {flag.removeprefix("--cfg="): True for flag in rustc_flags if flag.startswith("--cfg=")}
1746+
cfgs |= {value: True for (flag, value) in zip(rustc_flags[:-1], rustc_flags[1:]) if flag == "--cfg"}
1747+
1748+
return list(cfgs)

rust/private/rustc.bzl

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,39 +1115,16 @@ def construct_arguments(
11151115
if toolchain._rename_first_party_crates:
11161116
env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir
11171117

1118-
if crate_info.type in toolchain.extra_rustc_flags_for_crate_types.keys():
1119-
rustc_flags.add_all(toolchain.extra_rustc_flags_for_crate_types[crate_info.type], map_each = map_flag)
1120-
1121-
if is_exec_configuration(ctx):
1122-
rustc_flags.add_all(toolchain.extra_exec_rustc_flags, map_each = map_flag)
1123-
else:
1124-
rustc_flags.add_all(toolchain.extra_rustc_flags, map_each = map_flag)
1125-
11261118
# extra_rustc_env applies to the target configuration, not the exec configuration.
11271119
if hasattr(ctx.attr, "_extra_rustc_env") and not is_exec_configuration(ctx):
11281120
env.update(ctx.attr._extra_rustc_env[ExtraRustcEnvInfo].extra_rustc_env)
11291121

1130-
# extra_rustc_flags apply to the target configuration, not the exec configuration.
1131-
if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx):
1132-
rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags, map_each = map_flag)
1133-
1134-
if hasattr(ctx.attr, "_extra_rustc_flag") and not is_exec_configuration(ctx):
1135-
rustc_flags.add_all(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags, map_each = map_flag)
1136-
1137-
if hasattr(ctx.attr, "_per_crate_rustc_flag") and not is_exec_configuration(ctx):
1138-
per_crate_rustc_flags = ctx.attr._per_crate_rustc_flag[PerCrateRustcFlagsInfo].per_crate_rustc_flags
1139-
_add_per_crate_rustc_flags(ctx, rustc_flags, map_flag, crate_info, per_crate_rustc_flags)
1140-
11411122
if hasattr(ctx.attr, "_extra_exec_rustc_env") and is_exec_configuration(ctx):
11421123
env.update(ctx.attr._extra_exec_rustc_env[ExtraExecRustcEnvInfo].extra_exec_rustc_env)
11431124

1144-
if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx):
1145-
rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags, map_each = map_flag)
1146-
1147-
if hasattr(ctx.attr, "_extra_exec_rustc_flag") and is_exec_configuration(ctx):
1148-
rustc_flags.add_all(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags, map_each = map_flag)
1125+
rustc_flags.add_all(collect_extra_rustc_flags(ctx, toolchain, crate_info.root, crate_info.type), map_each = map_flag)
11491126

1150-
if _is_no_std(ctx, toolchain, crate_info):
1127+
if is_no_std(ctx, toolchain, crate_info.is_test):
11511128
rustc_flags.add('--cfg=feature="no_std"')
11521129

11531130
# Add target specific flags last, so they can override previous flags
@@ -1175,6 +1152,45 @@ def construct_arguments(
11751152

11761153
return args, env
11771154

1155+
def collect_extra_rustc_flags(ctx, toolchain, crate_root, crate_type):
1156+
"""Gather all 'extra' rustc flags from the target's attributes and toolchain.
1157+
1158+
Args:
1159+
ctx (ctx): The current rule's context object.
1160+
toolchain (rust_toolchain): The current Rust toolchain.
1161+
crate_root (File): The root file of the crate.
1162+
crate_type (str): The crate type.
1163+
1164+
Returns:
1165+
List[str]: Extra rustc flags.
1166+
"""
1167+
flags = []
1168+
1169+
if crate_type in toolchain.extra_rustc_flags_for_crate_types.keys():
1170+
flags.extend(toolchain.extra_rustc_flags_for_crate_types[crate_type])
1171+
1172+
is_exec = is_exec_configuration(ctx)
1173+
1174+
flags.extend(toolchain.extra_exec_rustc_flags if is_exec else toolchain.extra_rustc_flags)
1175+
1176+
if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec:
1177+
flags.extend(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags)
1178+
1179+
if hasattr(ctx.attr, "_extra_rustc_flag") and not is_exec:
1180+
flags.extend(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags)
1181+
1182+
if hasattr(ctx.attr, "_per_crate_rustc_flag") and not is_exec:
1183+
per_crate_rustc_flags = ctx.attr._per_crate_rustc_flag[PerCrateRustcFlagsInfo].per_crate_rustc_flags
1184+
flags.extend(_collect_per_crate_rustc_flags(ctx, crate_root, per_crate_rustc_flags))
1185+
1186+
if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec:
1187+
flags.extend(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1188+
1189+
if hasattr(ctx.attr, "_extra_exec_rustc_flag") and is_exec:
1190+
flags.extend(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1191+
1192+
return flags
1193+
11781194
def rustc_compile_action(
11791195
*,
11801196
ctx,
@@ -1635,12 +1651,8 @@ def rustc_compile_action(
16351651

16361652
return providers
16371653

1638-
def _is_no_std(ctx, toolchain, crate_info):
1639-
if is_exec_configuration(ctx) or crate_info.is_test:
1640-
return False
1641-
if toolchain._no_std == "off":
1642-
return False
1643-
return True
1654+
def is_no_std(ctx, toolchain, crate_is_test):
1655+
return not (is_exec_configuration(ctx) or crate_is_test or toolchain._no_std == "off")
16441656

16451657
def _should_use_rustc_allocator_libraries(toolchain):
16461658
use_or_default = toolchain._experimental_use_allocator_libraries_with_mangled_symbols
@@ -1675,7 +1687,7 @@ def _get_std_and_alloc_info(ctx, toolchain, crate_info):
16751687
return libs.libstd_and_allocator_ccinfo
16761688
return toolchain.libstd_and_allocator_ccinfo
16771689
if toolchain._experimental_use_global_allocator:
1678-
if _is_no_std(ctx, toolchain, crate_info):
1690+
if is_no_std(ctx, toolchain, crate_info.is_test):
16791691
if attr_global_allocator_library:
16801692
return libs.nostd_and_global_allocator_ccinfo
16811693
return toolchain.nostd_and_global_allocator_ccinfo
@@ -2285,16 +2297,20 @@ def _get_dirname(file):
22852297
"""
22862298
return file.dirname
22872299

2288-
def _add_per_crate_rustc_flags(ctx, args, map_flag, crate_info, per_crate_rustc_flags):
2289-
"""Adds matching per-crate rustc flags to `args`.
2300+
def _collect_per_crate_rustc_flags(ctx, crate_root, per_crate_rustc_flags):
2301+
"""Return all matching per-crate rustc flags.
22902302
22912303
Args:
22922304
ctx (ctx): The source rule's context object
2293-
args (Args): A reference to an Args object
2294-
map_flag (function): An optional function to use to map added flags
2295-
crate_info (CrateInfo): A CrateInfo provider
2305+
crate_root (File): The root file of the crate
22962306
per_crate_rustc_flags (list): A list of per_crate_rustc_flag values
2307+
2308+
Returns:
2309+
List[str]: matching per-crate rustc flags
22972310
"""
2311+
2312+
flags = []
2313+
22982314
for per_crate_rustc_flag in per_crate_rustc_flags:
22992315
at_index = per_crate_rustc_flag.find("@")
23002316
if at_index == -1:
@@ -2312,13 +2328,12 @@ def _add_per_crate_rustc_flags(ctx, args, map_flag, crate_info, per_crate_rustc_
23122328
label = label_string[2:]
23132329
else:
23142330
label = label_string
2315-
execution_path = crate_info.root.path
2331+
execution_path = crate_root.path
23162332

23172333
if label.startswith(prefix_filter) or execution_path.startswith(prefix_filter):
2318-
if map_flag:
2319-
flag = map_flag(flag)
2320-
if flag:
2321-
args.add(flag)
2334+
flags.append(flag)
2335+
2336+
return flags
23222337

23232338
def _error_format_impl(ctx):
23242339
"""Implementation of the `error_format` rule

rust/settings/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ load(
99
"clippy_output_diagnostics",
1010
"clippy_toml",
1111
"codegen_units",
12+
"collect_cfgs",
1213
"error_format",
1314
"experimental_link_std_dylib",
1415
"experimental_per_crate_rustc_flag",
@@ -70,6 +71,8 @@ clippy_toml()
7071

7172
codegen_units()
7273

74+
collect_cfgs()
75+
7376
error_format()
7477

7578
clippy_error_format()

rust/settings/settings.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,12 @@ def codegen_units():
490490
name = "codegen_units",
491491
build_setting_default = -1,
492492
)
493+
494+
# buildifier: disable=unnamed-macro
495+
def collect_cfgs():
496+
"""Enable collection of cfg flags with results stored in CrateInfo.cfgs.
497+
"""
498+
bool_flag(
499+
name = "collect_cfgs",
500+
build_setting_default = False,
501+
)

test/unit/collect_cfgs/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
load(":collect_cfgs_test.bzl", "collect_cfgs_test_suite")
2+
3+
collect_cfgs_test_suite(
4+
name = "collect_cfgs_test_suite",
5+
)

0 commit comments

Comments
 (0)