Skip to content

Commit 8f118ee

Browse files
Explicitly set -Ccodegen-units=1 for actions that emit an object file. (#3381)
Also, remove any other `-Ccodegen-units` that may have been set in various ways. The build rules expect to see a single object file, not the multiple object files that are produced when `-Ccodegen-units` (with a setting greater than 1) is used. The `_add_codegen_units_flags()` function already took this into account (`is_codegen_units_enabled()` returns false when emitting an object file), but there are various other ways that `-Ccodegen-units` might get introduced into the command line -- see the included test for details. Co-authored-by: scentini <rosica@google.com>
1 parent 585594e commit 8f118ee

File tree

2 files changed

+237
-24
lines changed

2 files changed

+237
-24
lines changed

rust/private/rustc.bzl

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,12 @@ def collect_inputs(
800800
compile_inputs = depset(build_env_files + lint_files, transitive = [build_script_compile_inputs, compile_inputs])
801801
return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs
802802

803+
def _will_emit_object_file(emit):
804+
return any([e == "obj" or e.startswith("obj=") for e in emit])
805+
806+
def _remove_codegen_units(flag):
807+
return None if flag.startswith("-Ccodegen-units") else flag
808+
803809
def construct_arguments(
804810
*,
805811
ctx,
@@ -918,6 +924,12 @@ def construct_arguments(
918924
rustc_path.add("--")
919925
rustc_path.add(tool_path)
920926

927+
# If we're emitting an object file, remove any `-Ccodegen-units=` flags.
928+
# The build rules expect to see a single object file, not the multiple
929+
# object files that are produced when `-Ccodegen-units` (with a setting
930+
# greater than 1) is used.
931+
map_flag = _remove_codegen_units if _will_emit_object_file(emit) else None
932+
921933
# Rustc arguments
922934
rustc_flags = ctx.actions.args()
923935
rustc_flags.set_param_file_format("multiline")
@@ -1002,15 +1014,15 @@ def construct_arguments(
10021014

10031015
# Tell Rustc where to find the standard library (or libcore)
10041016
rustc_flags.add_all(toolchain.rust_std_paths, before_each = "-L", format_each = "%s")
1005-
rustc_flags.add_all(rust_flags)
1017+
rustc_flags.add_all(rust_flags, map_each = map_flag)
10061018

10071019
# Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test
10081020
# Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
10091021
data_paths = depset(direct = getattr(attr, "data", []), transitive = [crate_info.compile_data_targets]).to_list()
10101022

10111023
add_edition_flags(rustc_flags, crate_info)
10121024
_add_lto_flags(ctx, toolchain, rustc_flags, crate_info)
1013-
_add_codegen_units_flags(toolchain, rustc_flags)
1025+
_add_codegen_units_flags(toolchain, emit, rustc_flags)
10141026

10151027
# Link!
10161028
if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or add_flags_for_binary:
@@ -1083,29 +1095,29 @@ def construct_arguments(
10831095
env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir
10841096

10851097
if crate_info.type in toolchain.extra_rustc_flags_for_crate_types.keys():
1086-
rustc_flags.add_all(toolchain.extra_rustc_flags_for_crate_types[crate_info.type])
1098+
rustc_flags.add_all(toolchain.extra_rustc_flags_for_crate_types[crate_info.type], map_each = map_flag)
10871099

10881100
if is_exec_configuration(ctx):
1089-
rustc_flags.add_all(toolchain.extra_exec_rustc_flags)
1101+
rustc_flags.add_all(toolchain.extra_exec_rustc_flags, map_each = map_flag)
10901102
else:
1091-
rustc_flags.add_all(toolchain.extra_rustc_flags)
1103+
rustc_flags.add_all(toolchain.extra_rustc_flags, map_each = map_flag)
10921104

10931105
# extra_rustc_flags apply to the target configuration, not the exec configuration.
10941106
if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx):
1095-
rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags)
1107+
rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags, map_each = map_flag)
10961108

10971109
if hasattr(ctx.attr, "_extra_rustc_flag") and not is_exec_configuration(ctx):
1098-
rustc_flags.add_all(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags)
1110+
rustc_flags.add_all(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags, map_each = map_flag)
10991111

11001112
if hasattr(ctx.attr, "_per_crate_rustc_flag") and not is_exec_configuration(ctx):
11011113
per_crate_rustc_flags = ctx.attr._per_crate_rustc_flag[PerCrateRustcFlagsInfo].per_crate_rustc_flags
1102-
_add_per_crate_rustc_flags(ctx, rustc_flags, crate_info, per_crate_rustc_flags)
1114+
_add_per_crate_rustc_flags(ctx, rustc_flags, map_flag, crate_info, per_crate_rustc_flags)
11031115

11041116
if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx):
1105-
rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1117+
rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags, map_each = map_flag)
11061118

11071119
if hasattr(ctx.attr, "_extra_exec_rustc_flag") and is_exec_configuration(ctx):
1108-
rustc_flags.add_all(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1120+
rustc_flags.add_all(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags, map_each = map_flag)
11091121

11101122
if _is_no_std(ctx, toolchain, crate_info):
11111123
rustc_flags.add('--cfg=feature="no_std"')
@@ -1118,6 +1130,7 @@ def construct_arguments(
11181130
data_paths,
11191131
{},
11201132
),
1133+
map_each = map_flag,
11211134
)
11221135

11231136
# Needed for bzlmod-aware runfiles resolution.
@@ -1632,15 +1645,23 @@ def _add_lto_flags(ctx, toolchain, args, crate):
16321645
lto_args = construct_lto_arguments(ctx, toolchain, crate)
16331646
args.add_all(lto_args)
16341647

1635-
def _add_codegen_units_flags(toolchain, args):
1648+
def _add_codegen_units_flags(toolchain, emit, args):
16361649
"""Adds flags to an Args object to configure codgen_units for 'rustc'.
16371650
16381651
https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units
16391652
16401653
Args:
16411654
toolchain (rust_toolchain): The current target's `rust_toolchain`.
1655+
emit (list): Values for the --emit flag to rustc.
16421656
args (Args): A reference to an Args object
16431657
"""
1658+
1659+
# If we're emitting an object file, the build rules expect to see a single
1660+
# object file.
1661+
if _will_emit_object_file(emit):
1662+
args.add("-Ccodegen-units=1")
1663+
return
1664+
16441665
if not is_codegen_units_enabled(toolchain):
16451666
return
16461667

@@ -2171,12 +2192,13 @@ def _get_dirname(file):
21712192
"""
21722193
return file.dirname
21732194

2174-
def _add_per_crate_rustc_flags(ctx, args, crate_info, per_crate_rustc_flags):
2175-
"""Adds matching per-crate rustc flags to an arguments object reference
2195+
def _add_per_crate_rustc_flags(ctx, args, map_flag, crate_info, per_crate_rustc_flags):
2196+
"""Adds matching per-crate rustc flags to `args`.
21762197
21772198
Args:
21782199
ctx (ctx): The source rule's context object
21792200
args (Args): A reference to an Args object
2201+
map_flag (function): An optional function to use to map added flags
21802202
crate_info (CrateInfo): A CrateInfo provider
21812203
per_crate_rustc_flags (list): A list of per_crate_rustc_flag values
21822204
"""
@@ -2200,7 +2222,10 @@ def _add_per_crate_rustc_flags(ctx, args, crate_info, per_crate_rustc_flags):
22002222
execution_path = crate_info.root.path
22012223

22022224
if label.startswith(prefix_filter) or execution_path.startswith(prefix_filter):
2203-
args.add(flag)
2225+
if map_flag:
2226+
flag = map_flag(flag)
2227+
if flag:
2228+
args.add(flag)
22042229

22052230
def _error_format_impl(ctx):
22062231
"""Implementation of the `error_format` rule

test/integration/cc_common_link/unit/cc_common_link_test.bzl

Lines changed: 198 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
"""Unittests for the --experimental_use_cc_common_link build setting."""
22

33
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
4+
load("@bazel_skylib//rules:write_file.bzl", "write_file")
45
load("@rules_cc//cc:defs.bzl", "cc_library")
56
load(
67
"@rules_rust//rust:defs.bzl",
78
"rust_binary",
89
"rust_shared_library",
910
"rust_test",
1011
)
12+
load("@rules_rust//rust:toolchain.bzl", "rust_stdlib_filegroup", "rust_toolchain")
1113

1214
DepActionsInfo = provider(
1315
"Contains information about dependencies actions.",
@@ -46,6 +48,31 @@ use_cc_common_link_on_target = rule(
4648
},
4749
)
4850

51+
def _with_collect_dep_actions_impl(ctx):
52+
# return [ctx.attr.target[DepActionsInfo], ctx.attr.target[OutputGroupInfo]]
53+
return [ctx.attr.target[DepActionsInfo]]
54+
55+
with_collect_dep_actions = rule(
56+
implementation = _with_collect_dep_actions_impl,
57+
attrs = {
58+
"target": attr.label(
59+
aspects = [collect_dep_actions_aspect],
60+
),
61+
},
62+
)
63+
64+
def _with_exec_cfg_impl(ctx):
65+
return [ctx.attr.target[DepActionsInfo]]
66+
67+
with_exec_cfg = rule(
68+
implementation = _with_exec_cfg_impl,
69+
attrs = {
70+
"target": attr.label(
71+
cfg = "exec",
72+
),
73+
},
74+
)
75+
4976
def _outputs_object_file(action):
5077
object_files = [output for output in action.outputs.to_list() if output.extension in ("o", "obj")]
5178
return len(object_files) > 0
@@ -192,24 +219,185 @@ def _cc_common_link_test_targets():
192219
target_under_test = ":bin_with_cc_common_link",
193220
)
194221

222+
return [
223+
"use_cc_common_link_on_binary",
224+
"use_cc_common_link_on_binary_with_pdb",
225+
"use_cc_common_link_on_test",
226+
"use_cc_common_link_on_crate_test",
227+
"use_cc_common_link_on_cdylib",
228+
"custom_malloc_on_binary_test",
229+
]
230+
231+
_RUSTC_FLAGS_CODEGEN_UNITS = 2
232+
_SETTINGS_CODEGEN_UNITS = 3
233+
_PER_CRATE_RUSTC_FLAG_CODEGEN_UNITS = 4
234+
_EXTRA_RUSTC_FLAG_CODEGEN_UNITS = 5
235+
_EXTRA_RUSTC_FLAGS_CODEGEN_UNITS = 6
236+
_EXTRA_EXEC_RUSTC_FLAG_CODEGEN_UNITS = 7
237+
_EXTRA_EXEC_RUSTC_FLAGS_CODEGEN_UNITS = 8
238+
_TOOLCHAIN_EXTRA_RUSTC_FLAGS_CODEGEN_UNITS = 9
239+
_TOOLCHAIN_EXTRA_EXEC_RUSTC_FLAGS_CODEGEN_UNITS = 10
240+
_TOOLCHAIN_EXTRA_RUSTC_FLAGS_FOR_CRATE_TYPES = 11
241+
242+
def _codegen_units_test_impl(ctx):
243+
env = analysistest.begin(ctx)
244+
tut = analysistest.target_under_test(env)
245+
registered_actions = tut[DepActionsInfo].actions
246+
247+
rustc_actions = [action for action in registered_actions if action.mnemonic == "Rustc"]
248+
asserts.equals(env, 1, len(rustc_actions))
249+
rustc_action = rustc_actions[0]
250+
251+
actual = sorted([arg for arg in rustc_action.argv if arg.startswith("-Ccodegen-units=")])
252+
expected = sorted(["-Ccodegen-units=%s" % codegen_units for codegen_units in ctx.attr.expected_codegen_units])
253+
254+
asserts.equals(env, expected, actual)
255+
256+
return analysistest.end(env)
257+
258+
codegen_units_test = analysistest.make(
259+
_codegen_units_test_impl,
260+
config_settings = {
261+
# By default, don't use cc_common.link to link.
262+
str(Label("@rules_rust//rust/settings:experimental_use_cc_common_link")): False,
263+
# Set `-Ccodegen-units` in various different ways.
264+
str(Label("@rules_rust//rust/settings:codegen_units")): _SETTINGS_CODEGEN_UNITS,
265+
# Empty prefix (before the `@`) means the flag is applied to all crates.
266+
str(Label("@rules_rust//rust/settings:experimental_per_crate_rustc_flag")): ["@-Ccodegen-units=%s" % _PER_CRATE_RUSTC_FLAG_CODEGEN_UNITS],
267+
str(Label("@rules_rust//rust/settings:extra_rustc_flag")): ["-Ccodegen-units=%s" % _EXTRA_RUSTC_FLAG_CODEGEN_UNITS],
268+
str(Label("@rules_rust//rust/settings:extra_rustc_flags")): ["-Ccodegen-units=%s" % _EXTRA_RUSTC_FLAGS_CODEGEN_UNITS],
269+
str(Label("@rules_rust//rust/settings:extra_exec_rustc_flag")): ["-Ccodegen-units=%s" % _EXTRA_EXEC_RUSTC_FLAG_CODEGEN_UNITS],
270+
str(Label("@rules_rust//rust/settings:extra_exec_rustc_flags")): ["-Ccodegen-units=%s" % _EXTRA_EXEC_RUSTC_FLAGS_CODEGEN_UNITS],
271+
"//command_line_option:extra_toolchains": ["//unit:codegen_units_toolchain"],
272+
},
273+
attrs = {"expected_codegen_units": attr.int_list()},
274+
)
275+
276+
def _codegen_units_test_targets():
277+
# Targets under test.
278+
rust_binary(
279+
name = "codegen_units_bin",
280+
srcs = ["bin.rs"],
281+
edition = "2018",
282+
rustc_flags = ["-Ccodegen-units=%s" % _RUSTC_FLAGS_CODEGEN_UNITS],
283+
)
284+
use_cc_common_link_on_target(
285+
name = "codegen_units_bin_with_cc_common_link",
286+
target = ":codegen_units_bin",
287+
)
288+
with_collect_dep_actions(
289+
name = "codegen_units_bin_with_collect_dep_actions",
290+
target = ":codegen_units_bin",
291+
)
292+
with_exec_cfg(
293+
name = "codegen_units_exec_bin_with_cc_common_link",
294+
target = ":codegen_units_bin_with_cc_common_link",
295+
)
296+
with_exec_cfg(
297+
name = "codegen_units_exec_bin_with_collect_dep_actions",
298+
target = ":codegen_units_bin_with_collect_dep_actions",
299+
)
300+
301+
# Fake toolchain along with its dependencies.
302+
write_file(
303+
name = "mock_rustc",
304+
out = "mock_rustc.exe",
305+
)
306+
write_file(
307+
name = "stdlib_src_self_contained",
308+
out = "self-contained/something.o",
309+
)
310+
rust_stdlib_filegroup(
311+
name = "std_libs",
312+
srcs = ["self-contained/something.o"],
313+
)
314+
write_file(
315+
name = "mock_rustdoc",
316+
out = "mock_rustdoc.exe",
317+
)
318+
rust_toolchain(
319+
name = "codegen_units_toolchain_impl",
320+
binary_ext = "",
321+
dylib_ext = ".so",
322+
exec_triple = "x86_64-unknown-none",
323+
target_triple = "x86_64-unknown-none",
324+
rust_doc = ":mock_rustdoc",
325+
rust_std = ":std_libs",
326+
rustc = ":mock_rustc",
327+
staticlib_ext = ".a",
328+
stdlib_linkflags = [],
329+
extra_rustc_flags = ["-Ccodegen-units=%s" % _TOOLCHAIN_EXTRA_RUSTC_FLAGS_CODEGEN_UNITS],
330+
extra_exec_rustc_flags = ["-Ccodegen-units=%s" % _TOOLCHAIN_EXTRA_EXEC_RUSTC_FLAGS_CODEGEN_UNITS],
331+
extra_rustc_flags_for_crate_types = {"bin": ["-Ccodegen-units=%s" % _TOOLCHAIN_EXTRA_RUSTC_FLAGS_FOR_CRATE_TYPES]},
332+
visibility = ["//visibility:public"],
333+
)
334+
native.toolchain(
335+
name = "codegen_units_toolchain",
336+
toolchain = ":codegen_units_toolchain_impl",
337+
toolchain_type = "@rules_rust//rust:toolchain",
338+
)
339+
340+
# Tests
341+
codegen_units_test(
342+
name = "codegen_units_filtered",
343+
target_under_test = ":codegen_units_bin_with_cc_common_link",
344+
# When using cc_common.link, we expect an explicit `-Ccodegen-units=1`.
345+
expected_codegen_units = [1],
346+
)
347+
codegen_units_test(
348+
name = "codegen_units_filtered_exec",
349+
target_under_test = ":codegen_units_exec_bin_with_cc_common_link",
350+
# When using cc_common.link, we expect an explicit `-Ccodegen-units=1`.
351+
expected_codegen_units = [1],
352+
)
353+
codegen_units_test(
354+
name = "codegen_units_not_filtered",
355+
target_under_test = ":codegen_units_bin_with_collect_dep_actions",
356+
# When not using cc_common.link, all `-Ccodegen-units` flags added in
357+
# various ways should remain unchanged.
358+
expected_codegen_units = [
359+
_RUSTC_FLAGS_CODEGEN_UNITS,
360+
_SETTINGS_CODEGEN_UNITS,
361+
_PER_CRATE_RUSTC_FLAG_CODEGEN_UNITS,
362+
_EXTRA_RUSTC_FLAG_CODEGEN_UNITS,
363+
_EXTRA_RUSTC_FLAGS_CODEGEN_UNITS,
364+
_TOOLCHAIN_EXTRA_RUSTC_FLAGS_CODEGEN_UNITS,
365+
_TOOLCHAIN_EXTRA_RUSTC_FLAGS_FOR_CRATE_TYPES,
366+
],
367+
)
368+
codegen_units_test(
369+
name = "codegen_units_not_filtered_exec",
370+
target_under_test = ":codegen_units_exec_bin_with_collect_dep_actions",
371+
# When not using cc_common.link, all `-Ccodegen-units` flags added in
372+
# various ways should remain unchanged.
373+
expected_codegen_units = [
374+
_RUSTC_FLAGS_CODEGEN_UNITS,
375+
_SETTINGS_CODEGEN_UNITS,
376+
_EXTRA_EXEC_RUSTC_FLAG_CODEGEN_UNITS,
377+
_EXTRA_EXEC_RUSTC_FLAGS_CODEGEN_UNITS,
378+
_TOOLCHAIN_EXTRA_EXEC_RUSTC_FLAGS_CODEGEN_UNITS,
379+
_TOOLCHAIN_EXTRA_RUSTC_FLAGS_FOR_CRATE_TYPES,
380+
],
381+
)
382+
383+
return [
384+
"codegen_units_filtered",
385+
"codegen_units_filtered_exec",
386+
"codegen_units_not_filtered",
387+
"codegen_units_not_filtered_exec",
388+
]
389+
195390
def cc_common_link_test_suite(name, **kwargs):
196391
"""Entry-point macro called from the BUILD file.
197392
198393
Args:
199394
name: Name of the macro.
200395
**kwargs: Additional keyword arguments,
201396
"""
202-
_cc_common_link_test_targets()
203-
204397
native.test_suite(
205398
name = name,
206-
tests = [
207-
"use_cc_common_link_on_binary",
208-
"use_cc_common_link_on_binary_with_pdb",
209-
"use_cc_common_link_on_test",
210-
"use_cc_common_link_on_crate_test",
211-
"use_cc_common_link_on_cdylib",
212-
"custom_malloc_on_binary_test",
213-
],
399+
tests =
400+
_cc_common_link_test_targets() +
401+
_codegen_units_test_targets(),
214402
**kwargs
215403
)

0 commit comments

Comments
 (0)