From 0d3915b9deaa75d23ad2d3c6c2f40dafb1e9c6f5 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 27 Oct 2022 15:57:18 +0300 Subject: [PATCH 1/7] Factor out processing Rustc::Message --- util/process_wrapper/rustc.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/util/process_wrapper/rustc.rs b/util/process_wrapper/rustc.rs index 67b75f9c36..f786655c5d 100644 --- a/util/process_wrapper/rustc.rs +++ b/util/process_wrapper/rustc.rs @@ -73,13 +73,7 @@ pub(crate) fn process_json(line: String, error_format: ErrorFormat) -> LineOutpu ) }); match parsed.try_into() { - Ok(RustcMessage::Message(msg)) => match error_format { - // If the output should be json, we just forward the messages as-is - // using `line`. - ErrorFormat::Json => LineOutput::Message(line), - // Otherwise we return the rendered field. - _ => LineOutput::Message(msg), - }, + Ok(RustcMessage::Message(rendered)) => output_based_on_error_format(line, rendered, error_format), _ => LineOutput::Skip, } } @@ -89,6 +83,7 @@ pub(crate) fn process_json(line: String, error_format: ErrorFormat) -> LineOutpu /// so the compiler can be terminated. /// This is used to implement pipelining in rules_rust, please see /// https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199 +/// TODO pass a function to handle the emit event and merge with process_json pub(crate) fn stop_on_rmeta_completion( line: String, error_format: ErrorFormat, @@ -105,13 +100,21 @@ pub(crate) fn stop_on_rmeta_completion( *kill = true; LineOutput::Terminate } - Ok(RustcMessage::Message(msg)) => match error_format { - // If the output should be json, we just forward the messages as-is - // using `line`. - ErrorFormat::Json => LineOutput::Message(line), - // Otherwise we return the rendered field. - _ => LineOutput::Message(msg), - }, + Ok(RustcMessage::Message(rendered)) => output_based_on_error_format(line, rendered, error_format), _ => LineOutput::Skip, } } + +fn output_based_on_error_format( + line: String, + rendered: String, + error_format: ErrorFormat, +) -> LineOutput { + match error_format { + // If the output should be json, we just forward the messages as-is + // using `line`. + ErrorFormat::Json => LineOutput::Message(line), + // Otherwise we return the rendered field. + ErrorFormat::Rendered => LineOutput::Message(rendered), + } +} From 3933943d39ea6e75e9f6b9feca9ca5a6f7ae2800 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 27 Oct 2022 16:01:56 +0300 Subject: [PATCH 2/7] Fix comment on process_wraper's options::rustc_output_format The error format is checked whenever it's set, not only when rustc_quit_on_rmeta is set. --- util/process_wrapper/options.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util/process_wrapper/options.rs b/util/process_wrapper/options.rs index dc1b82c77e..830c816d03 100644 --- a/util/process_wrapper/options.rs +++ b/util/process_wrapper/options.rs @@ -44,8 +44,7 @@ pub(crate) struct Options { // If set, it configures rustc to emit an rmeta file and then // quit. pub(crate) rustc_quit_on_rmeta: bool, - // If rustc_quit_on_rmeta is set to true, this controls the - // output format of rustc messages. + // This controls the output format of rustc messages. pub(crate) rustc_output_format: Option, } From 221367714cb7590897ce7ac05da76aa115eeecad Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Fri, 4 Nov 2022 10:39:33 +0200 Subject: [PATCH 3/7] Gather rustc output from compilation --- rust/private/common.bzl | 4 ++++ rust/private/providers.bzl | 4 +++- rust/private/rust.bzl | 22 ++++++++++++++++---- rust/private/rustc.bzl | 29 ++++++++++++++++++++++---- util/process_wrapper/main.rs | 36 +++++++++++++++++++++++++-------- util/process_wrapper/options.rs | 10 +++++++++ util/process_wrapper/output.rs | 11 +++++++--- util/process_wrapper/rustc.rs | 8 ++++++-- 8 files changed, 102 insertions(+), 22 deletions(-) diff --git a/rust/private/common.bzl b/rust/private/common.bzl index 20eb40b61d..6c322ae77e 100644 --- a/rust/private/common.bzl +++ b/rust/private/common.bzl @@ -49,6 +49,10 @@ def _create_crate_info(**kwargs): kwargs.update({"wrapped_crate_type": None}) if not "metadata" in kwargs: kwargs.update({"metadata": None}) + if not "rust_metadata_rustc_output" in kwargs: + kwargs.update({"rust_metadata_rustc_output": None}) + if not "rust_lib_rustc_output" in kwargs: + kwargs.update({"rust_lib_rustc_output": None}) if not "rustc_env_files" in kwargs: kwargs.update({"rustc_env_files": []}) return CrateInfo(**kwargs) diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl index 464444ba93..952a107d26 100644 --- a/rust/private/providers.bzl +++ b/rust/private/providers.bzl @@ -22,12 +22,14 @@ CrateInfo = provider( "deps": "depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers.", "edition": "str: The edition of this crate.", "is_test": "bool: If the crate is being compiled in a test context", - "metadata": "File: The rmeta file produced for this crate. It is optional.", + "metadata": "File: The output from rustc from producing the output file. It is optional.", "name": "str: The name of this crate.", "output": "File: The output File that will be produced, depends on crate type.", "owner": "Label: The label of the target that produced this CrateInfo", "proc_macro_deps": "depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers.", "root": "File: The source File entrypoint to this crate, eg. lib.rs", + "rust_lib_rustc_output": "File: The output from rustc from producing the output file. It is optional.", + "rust_metadata_rustc_output": "File: The rmeta file produced for this crate. It is optional.", "rustc_env": "Dict[String, String]: Additional `\"key\": \"value\"` environment variables to set for rustc.", "rustc_env_files": "[File]: Files containing additional environment variables to set for rustc.", "srcs": "depset[File]: All source Files that are part of the crate.", diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index 8ae8a0b215..ff4fb09ad3 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -64,6 +64,10 @@ def _assert_correct_dep_mapping(ctx): ), ) + +def _rustc_output_name(name): + return name + ".rustc-output" + def _determine_lib_name(name, crate_type, toolchain, lib_hash = None): """See https://github.com/bazelbuild/rules_rust/issues/405 @@ -275,14 +279,22 @@ def _rust_library_common(ctx, crate_type): toolchain, output_hash, ) + rust_lib = ctx.actions.declare_file(rust_lib_name) + rust_lib_build_output = None + if ctx.attr._process_wrapper: + rust_lib_build_output = ctx.actions.declare_file(_rustc_output_name(rust_lib_name)) rust_metadata = None + rust_metadata_build_output = None if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining: + rust_metadata_name = paths.replace_extension(rust_lib_name, ".rmeta") + rust_metadata = ctx.actions.declare_file( - paths.replace_extension(rust_lib_name, ".rmeta"), + rust_metadata_name, sibling = rust_lib, ) + rust_metadata_build_output = ctx.actions.declare_file(_rustc_output_name(rust_metadata_name)) deps = transform_deps(ctx.attr.deps) 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): proc_macro_deps = depset(proc_macro_deps), aliases = ctx.attr.aliases, output = rust_lib, + rust_lib_rustc_output = rust_lib_build_output, metadata = rust_metadata, + rust_metadata_rustc_output = rust_metadata_build_output, edition = get_edition(ctx.attr, toolchain, ctx.label), rustc_env = ctx.attr.rustc_env, rustc_env_files = ctx.files.rustc_env_files, @@ -598,7 +612,7 @@ _common_attrs = { The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. - Note that the variables here are subject to + Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the `stamp` attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. @@ -611,7 +625,7 @@ _common_attrs = { List of compiler flags passed to `rustc`. These strings are subject to Make variable expansion for predefined - source/output path variables like `$location`, `$execpath`, and + source/output path variables like `$location`, `$execpath`, and `$rootpath`. This expansion is useful if you wish to pass a generated file of arguments to rustc: `@$(location //package:target)`. """), @@ -723,7 +737,7 @@ _rust_test_attrs = dict({ mandatory = False, default = True, doc = dedent("""\ - Whether to use `libtest`. For targets using this flag, individual tests can be run by using the + Whether to use `libtest`. For targets using this flag, individual tests can be run by using the [--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag. E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`. """), diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 3a8c43cc77..d1e4c1c524 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -855,6 +855,12 @@ def construct_arguments( if build_metadata: # Configure process_wrapper to terminate rustc when metadata are emitted process_wrapper_flags.add("--rustc-quit-on-rmeta", "true") + if crate_info.rust_metadata_rustc_output: + process_wrapper_flags.add("--output-file", crate_info.rust_metadata_rustc_output.path) + else: + if crate_info.rust_lib_rustc_output: + process_wrapper_flags.add("--output-file", crate_info.rust_lib_rustc_output.path) + rustc_flags.add("--error-format=" + error_format) @@ -1018,6 +1024,8 @@ def rustc_compile_action( - (DefaultInfo): The output file for this crate, and its runfiles. """ build_metadata = getattr(crate_info, "metadata", None) + rust_lib_rustc_output = getattr(crate_info, "rust_lib_rustc_output", None) + rust_metadata_rustc_output = getattr(crate_info, "rust_metadata_rustc_output", None) cc_toolchain, feature_configuration = find_cc_toolchain(ctx) @@ -1156,6 +1164,8 @@ def rustc_compile_action( # The action might generate extra output that we don't want to include in the `DefaultInfo` files. action_outputs = list(outputs) + if rust_lib_rustc_output: + action_outputs.append(rust_lib_rustc_output) # Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate # types that benefit from having debug information in a separate file. @@ -1189,7 +1199,7 @@ def rustc_compile_action( ctx.actions.run( executable = ctx.executable._process_wrapper, inputs = compile_inputs, - outputs = [build_metadata], + outputs = [build_metadata, rust_metadata_rustc_output], env = env, arguments = args_metadata.all, mnemonic = "RustcMetadata", @@ -1307,12 +1317,23 @@ def rustc_compile_action( if toolchain.target_arch != "wasm32": providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library) + + output_group_info = {} + if pdb_file: - providers.append(OutputGroupInfo(pdb_file = depset([pdb_file]))) + output_group_info["pdb_file"] = depset([pdb_file]) if dsym_folder: - providers.append(OutputGroupInfo(dsym_folder = depset([dsym_folder]))) + output_group_info["dsym_folder"] = depset([dsym_folder]) + if build_metadata: + output_group_info["build_metadata"] = depset([build_metadata]) if build_metadata: - providers.append(OutputGroupInfo(build_metadata = depset([build_metadata]))) + output_group_info["build_metadata"] = depset([build_metadata]) + output_group_info["rust_metadata_rustc_output"] = depset([rust_metadata_rustc_output]) + if rust_lib_rustc_output: + output_group_info["rust_lib_rustc_output"] = depset([rust_lib_rustc_output]) + + if output_group_info: + providers.append(OutputGroupInfo(**output_group_info)) return providers diff --git a/util/process_wrapper/main.rs b/util/process_wrapper/main.rs index 6d985b34af..c0ab1c9858 100644 --- a/util/process_wrapper/main.rs +++ b/util/process_wrapper/main.rs @@ -89,6 +89,16 @@ fn main() { let mut child_stderr = child.stderr.take().unwrap(); + let mut output_file: Box> = + Box::new(opts.output_file.clone().map(|output_file_name| { + OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(output_file_name) + .expect("process wrapper error: unable to open output_file") + })); + let mut was_killed = false; let result = if let Some(format) = opts.rustc_output_format { let quit_on_rmeta = opts.rustc_quit_on_rmeta; @@ -96,13 +106,18 @@ fn main() { // that we emitted a metadata file. let mut me = false; let metadata_emitted = &mut me; - let result = process_output(&mut child_stderr, stderr.as_mut(), move |line| { - if quit_on_rmeta { - rustc::stop_on_rmeta_completion(line, format, metadata_emitted) - } else { - rustc::process_json(line, format) - } - }); + let result = process_output( + &mut child_stderr, + stderr.as_mut(), + output_file.as_mut(), + move |line| { + if quit_on_rmeta { + rustc::stop_on_rmeta_completion(line, format, metadata_emitted) + } else { + rustc::process_json(line, format) + } + }, + ); if me { // If recv returns Ok(), a signal was sent in this channel so we should terminate the child process. // We can safely ignore the Result from kill() as we don't care if the process already terminated. @@ -112,7 +127,12 @@ fn main() { result } else { // Process output normally by forwarding stderr - process_output(&mut child_stderr, stderr.as_mut(), LineOutput::Message) + process_output( + &mut child_stderr, + stderr.as_mut(), + output_file.as_mut(), + LineOutput::Message, + ) }; result.expect("process wrapper error: failed to process stderr"); diff --git a/util/process_wrapper/options.rs b/util/process_wrapper/options.rs index 830c816d03..feb22d3978 100644 --- a/util/process_wrapper/options.rs +++ b/util/process_wrapper/options.rs @@ -41,6 +41,9 @@ pub(crate) struct Options { pub(crate) stdout_file: Option, // If set, redirects the child process stderr to this file. pub(crate) stderr_file: Option, + // If set, also logs all unprocessed output from the to this file. + // Meant to be used to get json output out of rustc for tooling usage. + pub(crate) output_file: Option, // If set, it configures rustc to emit an rmeta file and then // quit. pub(crate) rustc_quit_on_rmeta: bool, @@ -60,6 +63,7 @@ pub(crate) fn options() -> Result { let mut copy_output_raw = None; let mut stdout_file = None; let mut stderr_file = None; + let mut output_file = None; let mut rustc_quit_on_rmeta_raw = None; let mut rustc_output_format_raw = None; let mut flags = Flags::new(); @@ -92,6 +96,11 @@ pub(crate) fn options() -> Result { "Redirect subprocess stderr in this file.", &mut stderr_file, ); + flags.define_flag( + "--output-file", + "Log all unprocessed subprocess stderr in this file.", + &mut output_file, + ); flags.define_flag( "--rustc-quit-on-rmeta", "If enabled, this wrapper will terminate rustc after rmeta has been emitted.", @@ -202,6 +211,7 @@ pub(crate) fn options() -> Result { copy_output, stdout_file, stderr_file, + output_file, rustc_quit_on_rmeta, rustc_output_format, }) diff --git a/util/process_wrapper/output.rs b/util/process_wrapper/output.rs index 84d61d9d75..033c66e779 100644 --- a/util/process_wrapper/output.rs +++ b/util/process_wrapper/output.rs @@ -32,22 +32,27 @@ pub(crate) enum LineOutput { /// to write_end. pub(crate) fn process_output( read_end: &mut dyn Read, - write_end: &mut dyn Write, + output_write_end: &mut dyn Write, + opt_file_write_end: &mut Option, mut process_line: F, ) -> io::Result<()> where F: FnMut(String) -> LineOutput, { let mut reader = io::BufReader::new(read_end); - let mut writer = io::LineWriter::new(write_end); + let mut output_writer = io::LineWriter::new(output_write_end); + let mut file_writer = opt_file_write_end.as_mut().map(io::LineWriter::new); loop { let mut line = String::new(); let read_bytes = reader.read_line(&mut line)?; if read_bytes == 0 { break; } + if let Some(ref mut file) = file_writer { + file.write_all(line.as_bytes())? + } match process_line(line) { - LineOutput::Message(to_write) => writer.write_all(to_write.as_bytes())?, + LineOutput::Message(to_write) => output_writer.write_all(to_write.as_bytes())?, LineOutput::Skip => {} LineOutput::Terminate => return Ok(()), }; diff --git a/util/process_wrapper/rustc.rs b/util/process_wrapper/rustc.rs index f786655c5d..bf0ce80f85 100644 --- a/util/process_wrapper/rustc.rs +++ b/util/process_wrapper/rustc.rs @@ -73,7 +73,9 @@ pub(crate) fn process_json(line: String, error_format: ErrorFormat) -> LineOutpu ) }); match parsed.try_into() { - Ok(RustcMessage::Message(rendered)) => output_based_on_error_format(line, rendered, error_format), + Ok(RustcMessage::Message(rendered)) => { + output_based_on_error_format(line, rendered, error_format) + } _ => LineOutput::Skip, } } @@ -100,7 +102,9 @@ pub(crate) fn stop_on_rmeta_completion( *kill = true; LineOutput::Terminate } - Ok(RustcMessage::Message(rendered)) => output_based_on_error_format(line, rendered, error_format), + Ok(RustcMessage::Message(rendered)) => { + output_based_on_error_format(line, rendered, error_format) + } _ => LineOutput::Skip, } } From 15033e0e4bf588a4a498829afa9402033534c35f Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 10 Nov 2022 15:02:58 +0200 Subject: [PATCH 4/7] Add bool config setting to enable diagnostic output --- BUILD.bazel | 21 ++++++++++++++++++++- rust/defs.bzl | 4 ++++ rust/private/rust.bzl | 12 ++++++++---- rust/private/rustc.bzl | 37 ++++++++++++++++++++++++++++++++----- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 11411d4f1d..1ea1251e62 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,5 +1,17 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load("//rust:defs.bzl", "capture_clippy_output", "clippy_flags", "error_format", "extra_exec_rustc_flag", "extra_exec_rustc_flags", "extra_rustc_flag", "extra_rustc_flags", "is_proc_macro_dep", "is_proc_macro_dep_enabled") +load( + "//rust:defs.bzl", + "capture_clippy_output", + "clippy_flags", + "error_format", + "extra_exec_rustc_flag", + "extra_exec_rustc_flags", + "extra_rustc_flag", + "extra_rustc_flags", + "is_proc_macro_dep", + "is_proc_macro_dep_enabled", + "output_diagnostics", +) exports_files(["LICENSE"]) @@ -18,6 +30,13 @@ error_format( visibility = ["//visibility:public"], ) +# This setting may be changed from the command line to generate rustc diagnostics. +output_diagnostics( + name = "output_diagnostics", + build_setting_default = False, + visibility = ["//visibility:public"], +) + # This setting may be used to pass extra options to clippy from the command line. # It applies across all targets. clippy_flags( diff --git a/rust/defs.bzl b/rust/defs.bzl index 92c8a77406..24aa32492d 100644 --- a/rust/defs.bzl +++ b/rust/defs.bzl @@ -49,6 +49,7 @@ load( _extra_rustc_flags = "extra_rustc_flags", _is_proc_macro_dep = "is_proc_macro_dep", _is_proc_macro_dep_enabled = "is_proc_macro_dep_enabled", + _output_diagnostics = "output_diagnostics", ) load( "//rust/private:rustdoc.bzl", @@ -103,6 +104,9 @@ rust_clippy = _rust_clippy capture_clippy_output = _capture_clippy_output # See @rules_rust//rust/private:clippy.bzl for a complete description. +output_diagnostics = _output_diagnostics +# See @rules_rust//rust/private:rustc.bzl for a complete description. + error_format = _error_format # See @rules_rust//rust/private:rustc.bzl for a complete description. diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index ff4fb09ad3..43466b9977 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -64,9 +64,8 @@ def _assert_correct_dep_mapping(ctx): ), ) - def _rustc_output_name(name): - return name + ".rustc-output" + return name + ".rustc-output" def _determine_lib_name(name, crate_type, toolchain, lib_hash = None): """See https://github.com/bazelbuild/rules_rust/issues/405 @@ -282,7 +281,8 @@ def _rust_library_common(ctx, crate_type): rust_lib = ctx.actions.declare_file(rust_lib_name) rust_lib_build_output = None - if ctx.attr._process_wrapper: + output_diagnostics = ctx.attr._output_diagnostics + if ctx.attr._process_wrapper and output_diagnostics: rust_lib_build_output = ctx.actions.declare_file(_rustc_output_name(rust_lib_name)) rust_metadata = None @@ -294,7 +294,8 @@ def _rust_library_common(ctx, crate_type): rust_metadata_name, sibling = rust_lib, ) - rust_metadata_build_output = ctx.actions.declare_file(_rustc_output_name(rust_metadata_name)) + if output_diagnostics: + rust_metadata_build_output = ctx.actions.declare_file(_rustc_output_name(rust_metadata_name)) deps = transform_deps(ctx.attr.deps) proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx)) @@ -687,6 +688,9 @@ _common_attrs = { "_is_proc_macro_dep_enabled": attr.label( default = Label("//:is_proc_macro_dep_enabled"), ), + "_output_diagnostics": attr.label( + default = Label("//:output_diagnostics"), + ), "_process_wrapper": attr.label( doc = "A process wrapper for running rustc on all platforms.", default = Label("//util/process_wrapper"), diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index d1e4c1c524..3fbcfa563b 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -52,6 +52,11 @@ ErrorFormatInfo = provider( fields = {"error_format": "(string) [" + ", ".join(_error_format_values) + "]"}, ) +OutputDiagnosticsInfo = provider( + doc = "Save json diagnostics form rustc", + fields = {"output_diagnostics": "(bool)"}, +) + ExtraRustcFlagsInfo = provider( doc = "Pass each value as an additional flag to non-exec rustc invocations", fields = {"extra_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"}, @@ -857,10 +862,8 @@ def construct_arguments( process_wrapper_flags.add("--rustc-quit-on-rmeta", "true") if crate_info.rust_metadata_rustc_output: process_wrapper_flags.add("--output-file", crate_info.rust_metadata_rustc_output.path) - else: - if crate_info.rust_lib_rustc_output: - process_wrapper_flags.add("--output-file", crate_info.rust_lib_rustc_output.path) - + elif crate_info.rust_lib_rustc_output: + process_wrapper_flags.add("--output-file", crate_info.rust_lib_rustc_output.path) rustc_flags.add("--error-format=" + error_format) @@ -1104,7 +1107,7 @@ def rustc_compile_action( build_flags_files = build_flags_files, force_all_deps_direct = force_all_deps_direct, stamp = stamp, - use_json_output = bool(build_metadata), + use_json_output = bool(build_metadata) or bool(rust_lib_rustc_output) or bool(rust_metadata_rustc_output), ) args_metadata = None @@ -1844,6 +1847,30 @@ error_format = rule( build_setting = config.string(flag = True), ) +def _output_diagnostics_impl(ctx): + """Implementation of the `output_diagnostics` rule + + Args: + ctx (ctx): The rule's context object + + Returns: + list: A list containing the OutputDiagnosticsInfo provider + """ + return [OutputDiagnosticsInfo(output_diagnostics = ctx.build_setting_value)] + +output_diagnostics = rule( + doc = ( + "Setting this flag from the command line with `--@rules_rust//:output_diagnostics` " + + "makes rules_rust save rustc json output(suitable for consumption by rust-analyzer) in a file. " + + "These are accessible via the " + + "`rust_metadata_rustc_output`(for pipelined compilation) and `rust_lib_rustc_output` output groups. " + + "You can find these either by using something like `find -name '*.rustc-output'` or by using " + + "`bazel cquery --output=files`." + ), + implementation = _output_diagnostics_impl, + build_setting = config.bool(flag = True), +) + def _extra_rustc_flags_impl(ctx): return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value) From 858e1245e06127219ae277f75083682b41ed8d8e Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Wed, 16 Nov 2022 22:23:22 +0200 Subject: [PATCH 5/7] Check if rust_metadata_rustc_output is None before using it in more places --- rust/private/rustc.bzl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 3fbcfa563b..5f91abb741 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -1202,7 +1202,7 @@ def rustc_compile_action( ctx.actions.run( executable = ctx.executable._process_wrapper, inputs = compile_inputs, - outputs = [build_metadata, rust_metadata_rustc_output], + outputs = [build_metadata] + [x for x in [rust_metadata_rustc_output] if x], env = env, arguments = args_metadata.all, mnemonic = "RustcMetadata", @@ -1331,7 +1331,8 @@ def rustc_compile_action( output_group_info["build_metadata"] = depset([build_metadata]) if build_metadata: output_group_info["build_metadata"] = depset([build_metadata]) - output_group_info["rust_metadata_rustc_output"] = depset([rust_metadata_rustc_output]) + if rust_metadata_rustc_output: + output_group_info["rust_metadata_rustc_output"] = depset([rust_metadata_rustc_output]) if rust_lib_rustc_output: output_group_info["rust_lib_rustc_output"] = depset([rust_lib_rustc_output]) From 67ae8417dd8ff84ffe0d45cfb2596a930ed7e9c3 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Wed, 16 Nov 2022 22:41:59 +0200 Subject: [PATCH 6/7] Regenerate documentation --- docs/flatten.md | 7 +++++-- docs/providers.md | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/flatten.md b/docs/flatten.md index 9a73478905..7987593e14 100644 --- a/docs/flatten.md +++ b/docs/flatten.md @@ -1379,7 +1379,8 @@ A test rule for performing `rustfmt --check` on a set of targets
 CrateInfo(aliases, compile_data, deps, edition, is_test, metadata, name, output, owner,
-          proc_macro_deps, root, rustc_env, rustc_env_files, srcs, type, wrapped_crate_type)
+          proc_macro_deps, root, rust_lib_rustc_output, rust_metadata_rustc_output, rustc_env,
+          rustc_env_files, srcs, type, wrapped_crate_type)
 
A provider containing general Crate information. @@ -1394,12 +1395,14 @@ A provider containing general Crate information. | deps | depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers. | | edition | str: The edition of this crate. | | is_test | bool: If the crate is being compiled in a test context | -| metadata | File: The rmeta file produced for this crate. It is optional. | +| metadata | File: The output from rustc from producing the output file. It is optional. | | name | str: The name of this crate. | | output | File: The output File that will be produced, depends on crate type. | | owner | Label: The label of the target that produced this CrateInfo | | proc_macro_deps | depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers. | | root | File: The source File entrypoint to this crate, eg. lib.rs | +| rust_lib_rustc_output | File: The output from rustc from producing the output file. It is optional. | +| rust_metadata_rustc_output | File: The rmeta file produced for this crate. It is optional. | | rustc_env | Dict[String, String]: Additional "key": "value" environment variables to set for rustc. | | rustc_env_files | [File]: Files containing additional environment variables to set for rustc. | | srcs | depset[File]: All source Files that are part of the crate. | diff --git a/docs/providers.md b/docs/providers.md index 11f77b7d1b..1834ed68c4 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -11,7 +11,8 @@
 CrateInfo(aliases, compile_data, deps, edition, is_test, metadata, name, output, owner,
-          proc_macro_deps, root, rustc_env, rustc_env_files, srcs, type, wrapped_crate_type)
+          proc_macro_deps, root, rust_lib_rustc_output, rust_metadata_rustc_output, rustc_env,
+          rustc_env_files, srcs, type, wrapped_crate_type)
 
A provider containing general Crate information. @@ -26,12 +27,14 @@ A provider containing general Crate information. | deps | depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers. | | edition | str: The edition of this crate. | | is_test | bool: If the crate is being compiled in a test context | -| metadata | File: The rmeta file produced for this crate. It is optional. | +| metadata | File: The output from rustc from producing the output file. It is optional. | | name | str: The name of this crate. | | output | File: The output File that will be produced, depends on crate type. | | owner | Label: The label of the target that produced this CrateInfo | | proc_macro_deps | depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers. | | root | File: The source File entrypoint to this crate, eg. lib.rs | +| rust_lib_rustc_output | File: The output from rustc from producing the output file. It is optional. | +| rust_metadata_rustc_output | File: The rmeta file produced for this crate. It is optional. | | rustc_env | Dict[String, String]: Additional "key": "value" environment variables to set for rustc. | | rustc_env_files | [File]: Files containing additional environment variables to set for rustc. | | srcs | depset[File]: All source Files that are part of the crate. | From 0aec7e9e1b8ce71c21cd5b1f5e5896dfb445a41c Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Wed, 16 Nov 2022 22:48:54 +0200 Subject: [PATCH 7/7] Place build_outputs next to their corresponding rlib/rmeta Hopefully this fixes compilation issues on windows --- rust/private/rust.bzl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index 43466b9977..b9eab3ec85 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -283,7 +283,10 @@ def _rust_library_common(ctx, crate_type): rust_lib_build_output = None output_diagnostics = ctx.attr._output_diagnostics if ctx.attr._process_wrapper and output_diagnostics: - rust_lib_build_output = ctx.actions.declare_file(_rustc_output_name(rust_lib_name)) + rust_lib_build_output = ctx.actions.declare_file( + _rustc_output_name(rust_lib_name), + sibling = rust_lib, + ) rust_metadata = None rust_metadata_build_output = None @@ -295,7 +298,10 @@ def _rust_library_common(ctx, crate_type): sibling = rust_lib, ) if output_diagnostics: - rust_metadata_build_output = ctx.actions.declare_file(_rustc_output_name(rust_metadata_name)) + rust_metadata_build_output = ctx.actions.declare_file( + _rustc_output_name(rust_metadata_name), + sibling = rust_metadata, + ) deps = transform_deps(ctx.attr.deps) proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))