Skip to content

Commit 1780838

Browse files
committed
Gather rustc output from compilation
1 parent 3933943 commit 1780838

File tree

7 files changed

+80
-16
lines changed

7 files changed

+80
-16
lines changed

rust/private/common.bzl

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ def _create_crate_info(**kwargs):
4949
kwargs.update({"wrapped_crate_type": None})
5050
if not "metadata" in kwargs:
5151
kwargs.update({"metadata": None})
52+
if not "rust_metadata_rustc_output" in kwargs:
53+
kwargs.update({"rust_metadata_rustc_output": None})
54+
if not "rust_lib_rustc_output" in kwargs:
55+
kwargs.update({"rust_lib_rustc_output": None})
5256
if not "rustc_env_files" in kwargs:
5357
kwargs.update({"rustc_env_files": []})
5458
return CrateInfo(**kwargs)

rust/private/providers.bzl

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ CrateInfo = provider(
2222
"deps": "depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers.",
2323
"edition": "str: The edition of this crate.",
2424
"is_test": "bool: If the crate is being compiled in a test context",
25-
"metadata": "File: The rmeta file produced for this crate. It is optional.",
25+
"metadata": "File: The output from rustc from producing the output file. It is optional.",
26+
"rust_metadata_rustc_output": "File: The rmeta file produced for this crate. It is optional.",
2627
"name": "str: The name of this crate.",
2728
"output": "File: The output File that will be produced, depends on crate type.",
29+
"rust_lib_rustc_output": "File: The output from rustc from producing the output file. It is optional.",
2830
"owner": "Label: The label of the target that produced this CrateInfo",
2931
"proc_macro_deps": "depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers.",
3032
"root": "File: The source File entrypoint to this crate, eg. lib.rs",

rust/private/rust.bzl

+18-4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ def _assert_correct_dep_mapping(ctx):
6464
),
6565
)
6666

67+
68+
def _rustc_output_name(name):
69+
return name + ".rustc-output"
70+
6771
def _determine_lib_name(name, crate_type, toolchain, lib_hash = None):
6872
"""See https://github.com/bazelbuild/rules_rust/issues/405
6973
@@ -275,14 +279,22 @@ def _rust_library_common(ctx, crate_type):
275279
toolchain,
276280
output_hash,
277281
)
282+
278283
rust_lib = ctx.actions.declare_file(rust_lib_name)
284+
rust_lib_build_output = None
285+
if ctx.attr._process_wrapper:
286+
rust_lib_build_output = ctx.actions.declare_file(_rustc_output_name(rust_lib_name))
279287

280288
rust_metadata = None
289+
rust_metadata_build_output = None
281290
if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining:
291+
rust_metadata_name = paths.replace_extension(rust_lib_name, ".rmeta")
292+
282293
rust_metadata = ctx.actions.declare_file(
283-
paths.replace_extension(rust_lib_name, ".rmeta"),
294+
rust_metadata_name,
284295
sibling = rust_lib,
285296
)
297+
rust_metadata_build_output = ctx.actions.declare_file(_rustc_output_name(rust_metadata_name))
286298

287299
deps = transform_deps(ctx.attr.deps)
288300
proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
@@ -300,7 +312,9 @@ def _rust_library_common(ctx, crate_type):
300312
proc_macro_deps = depset(proc_macro_deps),
301313
aliases = ctx.attr.aliases,
302314
output = rust_lib,
315+
rust_lib_rustc_output = rust_lib_build_output,
303316
metadata = rust_metadata,
317+
rust_metadata_rustc_output = rust_metadata_build_output,
304318
edition = get_edition(ctx.attr, toolchain, ctx.label),
305319
rustc_env = ctx.attr.rustc_env,
306320
rustc_env_files = ctx.files.rustc_env_files,
@@ -598,7 +612,7 @@ _common_attrs = {
598612
The order that these files will be processed is unspecified, so
599613
multiple definitions of a particular variable are discouraged.
600614
601-
Note that the variables here are subject to
615+
Note that the variables here are subject to
602616
[workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status)
603617
stamping should the `stamp` attribute be enabled. Stamp variables
604618
should be wrapped in brackets in order to be resolved. E.g.
@@ -611,7 +625,7 @@ _common_attrs = {
611625
List of compiler flags passed to `rustc`.
612626
613627
These strings are subject to Make variable expansion for predefined
614-
source/output path variables like `$location`, `$execpath`, and
628+
source/output path variables like `$location`, `$execpath`, and
615629
`$rootpath`. This expansion is useful if you wish to pass a generated
616630
file of arguments to rustc: `@$(location //package:target)`.
617631
"""),
@@ -723,7 +737,7 @@ _rust_test_attrs = dict({
723737
mandatory = False,
724738
default = True,
725739
doc = dedent("""\
726-
Whether to use `libtest`. For targets using this flag, individual tests can be run by using the
740+
Whether to use `libtest`. For targets using this flag, individual tests can be run by using the
727741
[--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag.
728742
E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`.
729743
"""),

rust/private/rustc.bzl

+25-4
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,12 @@ def construct_arguments(
855855
if build_metadata:
856856
# Configure process_wrapper to terminate rustc when metadata are emitted
857857
process_wrapper_flags.add("--rustc-quit-on-rmeta", "true")
858+
if crate_info.rust_metadata_rustc_output:
859+
process_wrapper_flags.add("--output-file", crate_info.rust_metadata_rustc_output.path)
860+
else:
861+
if crate_info.rust_lib_rustc_output:
862+
process_wrapper_flags.add("--output-file", crate_info.rust_lib_rustc_output.path)
863+
858864

859865
rustc_flags.add("--error-format=" + error_format)
860866

@@ -1018,6 +1024,8 @@ def rustc_compile_action(
10181024
- (DefaultInfo): The output file for this crate, and its runfiles.
10191025
"""
10201026
build_metadata = getattr(crate_info, "metadata", None)
1027+
rust_lib_rustc_output = getattr(crate_info, "rust_lib_rustc_output", None)
1028+
rust_metadata_rustc_output = getattr(crate_info, "rust_metadata_rustc_output", None)
10211029

10221030
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
10231031

@@ -1156,6 +1164,8 @@ def rustc_compile_action(
11561164

11571165
# The action might generate extra output that we don't want to include in the `DefaultInfo` files.
11581166
action_outputs = list(outputs)
1167+
if rust_lib_rustc_output:
1168+
action_outputs.append(rust_lib_rustc_output)
11591169

11601170
# Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate
11611171
# types that benefit from having debug information in a separate file.
@@ -1189,7 +1199,7 @@ def rustc_compile_action(
11891199
ctx.actions.run(
11901200
executable = ctx.executable._process_wrapper,
11911201
inputs = compile_inputs,
1192-
outputs = [build_metadata],
1202+
outputs = [build_metadata, rust_metadata_rustc_output],
11931203
env = env,
11941204
arguments = args_metadata.all,
11951205
mnemonic = "RustcMetadata",
@@ -1307,12 +1317,23 @@ def rustc_compile_action(
13071317

13081318
if toolchain.target_arch != "wasm32":
13091319
providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library)
1320+
1321+
output_group_info = {}
1322+
13101323
if pdb_file:
1311-
providers.append(OutputGroupInfo(pdb_file = depset([pdb_file])))
1324+
output_group_info["pdb_file"] = depset([pdb_file])
13121325
if dsym_folder:
1313-
providers.append(OutputGroupInfo(dsym_folder = depset([dsym_folder])))
1326+
output_group_info["dsym_folder"] = depset([dsym_folder])
1327+
if build_metadata:
1328+
output_group_info["build_metadata"] = depset([build_metadata])
13141329
if build_metadata:
1315-
providers.append(OutputGroupInfo(build_metadata = depset([build_metadata])))
1330+
output_group_info["build_metadata"] = depset([build_metadata])
1331+
output_group_info["rust_metadata_rustc_output"] = depset([rust_metadata_rustc_output])
1332+
if rust_lib_rustc_output:
1333+
output_group_info["rust_lib_rustc_output"] = depset([rust_lib_rustc_output])
1334+
1335+
if output_group_info:
1336+
providers.append(OutputGroupInfo(**output_group_info))
13161337

13171338
return providers
13181339

util/process_wrapper/main.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,26 @@ fn main() {
8989

9090
let mut child_stderr = child.stderr.take().unwrap();
9191

92+
let mut output_file: Box<Option<std::fs::File>> = Box::new(opts.output_file.clone().map(|output_file_name|
93+
OpenOptions::new()
94+
.create(true)
95+
.truncate(true)
96+
.write(true)
97+
.open(output_file_name)
98+
.expect("process wrapper error: unable to open output_file")));
99+
92100
let mut was_killed = false;
93101
let result = if let Some(format) = opts.rustc_output_format {
94102
let quit_on_rmeta = opts.rustc_quit_on_rmeta;
95103
// Process json rustc output and kill the subprocess when we get a signal
96104
// that we emitted a metadata file.
97105
let mut me = false;
98106
let metadata_emitted = &mut me;
99-
let result = process_output(&mut child_stderr, stderr.as_mut(), move |line| {
107+
let result = process_output(&mut child_stderr, stderr.as_mut(), output_file.as_mut(), move |line| {
100108
if quit_on_rmeta {
101-
rustc::stop_on_rmeta_completion(line, format, metadata_emitted)
109+
rustc::stop_on_rmeta_completion(line, format.clone(), metadata_emitted)
102110
} else {
103-
rustc::process_json(line, format)
111+
rustc::process_json(line, format.clone())
104112
}
105113
});
106114
if me {
@@ -112,7 +120,7 @@ fn main() {
112120
result
113121
} else {
114122
// Process output normally by forwarding stderr
115-
process_output(&mut child_stderr, stderr.as_mut(), LineOutput::Message)
123+
process_output(&mut child_stderr, stderr.as_mut(), output_file.as_mut(), LineOutput::Message)
116124
};
117125
result.expect("process wrapper error: failed to process stderr");
118126

util/process_wrapper/options.rs

+10
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ pub(crate) struct Options {
4141
pub(crate) stdout_file: Option<String>,
4242
// If set, redirects the child process stderr to this file.
4343
pub(crate) stderr_file: Option<String>,
44+
// If set, also logs all unprocessed output from the to this file.
45+
// Meant to be used to get json output out of rustc for tooling usage.
46+
pub(crate) output_file: Option<String>,
4447
// If set, it configures rustc to emit an rmeta file and then
4548
// quit.
4649
pub(crate) rustc_quit_on_rmeta: bool,
@@ -60,6 +63,7 @@ pub(crate) fn options() -> Result<Options, OptionError> {
6063
let mut copy_output_raw = None;
6164
let mut stdout_file = None;
6265
let mut stderr_file = None;
66+
let mut output_file = None;
6367
let mut rustc_quit_on_rmeta_raw = None;
6468
let mut rustc_output_format_raw = None;
6569
let mut flags = Flags::new();
@@ -92,6 +96,11 @@ pub(crate) fn options() -> Result<Options, OptionError> {
9296
"Redirect subprocess stderr in this file.",
9397
&mut stderr_file,
9498
);
99+
flags.define_flag(
100+
"--output-file",
101+
"Log all unprocessed subprocess stderr in this file.",
102+
&mut output_file,
103+
);
95104
flags.define_flag(
96105
"--rustc-quit-on-rmeta",
97106
"If enabled, this wrapper will terminate rustc after rmeta has been emitted.",
@@ -202,6 +211,7 @@ pub(crate) fn options() -> Result<Options, OptionError> {
202211
copy_output,
203212
stdout_file,
204213
stderr_file,
214+
output_file,
205215
rustc_quit_on_rmeta,
206216
rustc_output_format,
207217
})

util/process_wrapper/output.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,27 @@ pub(crate) enum LineOutput {
3232
/// to write_end.
3333
pub(crate) fn process_output<F>(
3434
read_end: &mut dyn Read,
35-
write_end: &mut dyn Write,
35+
output_write_end: &mut dyn Write,
36+
opt_file_write_end: &mut Option<std::fs::File>,
3637
mut process_line: F,
3738
) -> io::Result<()>
3839
where
3940
F: FnMut(String) -> LineOutput,
4041
{
4142
let mut reader = io::BufReader::new(read_end);
42-
let mut writer = io::LineWriter::new(write_end);
43+
let mut output_writer = io::LineWriter::new(output_write_end);
44+
let mut file_writer = opt_file_write_end.as_mut().map(|file_write_end| io::LineWriter::new(file_write_end));
4345
loop {
4446
let mut line = String::new();
4547
let read_bytes = reader.read_line(&mut line)?;
4648
if read_bytes == 0 {
4749
break;
4850
}
51+
if let Some(ref mut file) = file_writer {
52+
file.write_all(line.as_bytes())?
53+
}
4954
match process_line(line) {
50-
LineOutput::Message(to_write) => writer.write_all(to_write.as_bytes())?,
55+
LineOutput::Message(to_write) => output_writer.write_all(to_write.as_bytes())?,
5156
LineOutput::Skip => {}
5257
LineOutput::Terminate => return Ok(()),
5358
};

0 commit comments

Comments
 (0)