diff --git a/BUILD.bazel b/BUILD.bazel index 2eb9e03a25..1f4f2bcb7b 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -13,6 +13,7 @@ load( "is_proc_macro_dep_enabled", "no_std", "per_crate_rustc_flag", + "rustc_output_diagnostics", ) exports_files(["LICENSE"]) @@ -32,6 +33,13 @@ error_format( visibility = ["//visibility:public"], ) +# This setting may be changed from the command line to generate rustc diagnostics. +rustc_output_diagnostics( + name = "rustc_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/docs/flatten.md b/docs/flatten.md index e7e7cb4673..00af38c3ca 100644 --- a/docs/flatten.md +++ b/docs/flatten.md @@ -1439,8 +1439,8 @@ A toolchain for [rustfmt](https://rust-lang.github.io/rustfmt/)
CrateInfo(aliases, compile_data, compile_data_targets, data, deps, edition, is_test, metadata, name, - output, owner, proc_macro_deps, root, rustc_env, rustc_env_files, srcs, type, - wrapped_crate_type) + output, owner, proc_macro_deps, root, rustc_env, rustc_env_files, rustc_output, + rustc_rmeta_output, srcs, type, wrapped_crate_type)A provider containing general Crate information. @@ -1457,7 +1457,7 @@ 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 | @@ -1465,6 +1465,8 @@ A provider containing general Crate information. | root | File: The source File entrypoint to this crate, eg. lib.rs | | 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. |
+| rustc_output | File: The output from rustc from producing the output file. It is optional. |
+| rustc_rmeta_output | File: The rmeta file produced for this crate. It is optional. |
| srcs | depset[File]: All source Files that are part of the crate. |
| type | str: The type of this crate (see [rustc --crate-type](https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit)). |
| wrapped_crate_type | str, optional: The original crate type for targets generated using a previously defined crate (typically tests using the rust_test::crate
attribute) |
diff --git a/docs/providers.md b/docs/providers.md
index c97ad9bce9..87a37924c1 100644
--- a/docs/providers.md
+++ b/docs/providers.md
@@ -11,8 +11,8 @@
CrateInfo(aliases, compile_data, compile_data_targets, data, deps, edition, is_test, metadata, name, - output, owner, proc_macro_deps, root, rustc_env, rustc_env_files, srcs, type, - wrapped_crate_type) + output, owner, proc_macro_deps, root, rustc_env, rustc_env_files, rustc_output, + rustc_rmeta_output, srcs, type, wrapped_crate_type)A provider containing general Crate information. @@ -29,7 +29,7 @@ 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 | @@ -37,6 +37,8 @@ A provider containing general Crate information. | root | File: The source File entrypoint to this crate, eg. lib.rs | | 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. |
+| rustc_output | File: The output from rustc from producing the output file. It is optional. |
+| rustc_rmeta_output | File: The rmeta file produced for this crate. It is optional. |
| srcs | depset[File]: All source Files that are part of the crate. |
| type | str: The type of this crate (see [rustc --crate-type](https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit)). |
| wrapped_crate_type | str, optional: The original crate type for targets generated using a previously defined crate (typically tests using the rust_test::crate
attribute) |
diff --git a/docs/rust_analyzer.md b/docs/rust_analyzer.md
index bd91b0b790..fd4be79da7 100644
--- a/docs/rust_analyzer.md
+++ b/docs/rust_analyzer.md
@@ -78,6 +78,16 @@ to ensure a `rust-project.json` file is created and up to date when the editor i
}
```
+#### Alternative vscode option (prototype)
+
+Add the following to your bazelrc:
+```
+build --@rules_rust//:output_diagnostics=true --output_groups=+rust_lib_rustc_output,+rust_metadata_rustc_output
+```
+
+Then you can use a prototype [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=MattStark.bazel-rust-analyzer) that automatically collects the outputs whenever you recompile.
+
+
diff --git a/docs/rust_analyzer.vm b/docs/rust_analyzer.vm
index ad74f91a09..21f1f715d5 100644
--- a/docs/rust_analyzer.vm
+++ b/docs/rust_analyzer.vm
@@ -71,4 +71,14 @@ to ensure a `rust-project.json` file is created and up to date when the editor i
]
}
```
+
+#### Alternative vscode option (prototype)
+
+Add the following to your bazelrc:
+```
+build --@rules_rust//:output_diagnostics=true --output_groups=+rust_lib_rustc_output,+rust_metadata_rustc_output
+```
+
+Then you can use a prototype [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=MattStark.bazel-rust-analyzer) that automatically collects the outputs whenever you recompile.
+
]]#
diff --git a/rust/defs.bzl b/rust/defs.bzl
index 7c9724399e..e6afbba47c 100644
--- a/rust/defs.bzl
+++ b/rust/defs.bzl
@@ -52,6 +52,7 @@ load(
_is_proc_macro_dep_enabled = "is_proc_macro_dep_enabled",
_no_std = "no_std",
_per_crate_rustc_flag = "per_crate_rustc_flag",
+ _rustc_output_diagnostics = "rustc_output_diagnostics",
)
load(
"//rust/private:rustdoc.bzl",
@@ -109,6 +110,9 @@ rust_clippy = _rust_clippy
capture_clippy_output = _capture_clippy_output
# See @rules_rust//rust/private:clippy.bzl for a complete description.
+rustc_output_diagnostics = _rustc_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/common.bzl b/rust/private/common.bzl
index efb4b2b6e5..f900379cbc 100644
--- a/rust/private/common.bzl
+++ b/rust/private/common.bzl
@@ -51,6 +51,10 @@ def _create_crate_info(**kwargs):
kwargs.update({"wrapped_crate_type": None})
if not "metadata" in kwargs:
kwargs.update({"metadata": None})
+ if not "rustc_rmeta_output" in kwargs:
+ kwargs.update({"rustc_rmeta_output": None})
+ if not "rustc_output" in kwargs:
+ kwargs.update({"rustc_output": None})
if not "rustc_env_files" in kwargs:
kwargs.update({"rustc_env_files": []})
if not "data" in kwargs:
diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl
index e4db9c9f9d..cd7ec71b8e 100644
--- a/rust/private/providers.bzl
+++ b/rust/private/providers.bzl
@@ -24,7 +24,7 @@ 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",
@@ -32,6 +32,8 @@ CrateInfo = provider(
"root": "File: The source File entrypoint to this crate, eg. lib.rs",
"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.",
+ "rustc_output": "File: The output from rustc from producing the output file. It is optional.",
+ "rustc_rmeta_output": "File: The rmeta file produced for this crate. It is optional.",
"srcs": "depset[File]: All source Files that are part of the crate.",
"type": (
"str: The type of this crate " +
@@ -94,6 +96,16 @@ DepVariantInfo = provider(
},
)
+RustcOutputDiagnosticsInfo = provider(
+ doc = (
+ "Save json diagnostics from rustc. Json diagnostics are able to be " +
+ "consumed by tools such as rust-analyzer to provide IDE integration"
+ ),
+ fields = {
+ "rustc_output_diagnostics": "bool: Whether or not to output diagnostics",
+ },
+)
+
StdLibInfo = provider(
doc = (
"A collection of files either found within the `rust-stdlib` artifact or " +
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index 484e35d34e..d9caf28114 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -28,6 +28,7 @@ load(
"determine_output_hash",
"expand_dict_value_locations",
"find_toolchain",
+ "generate_output_diagnostics",
"get_edition",
"get_import_macro_deps",
"transform_deps",
@@ -168,11 +169,13 @@ def _rust_library_common(ctx, crate_type):
)
rust_lib = ctx.actions.declare_file(rust_lib_name)
rust_metadata = None
+ rustc_rmeta_output = None
if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining:
rust_metadata = ctx.actions.declare_file(
paths.replace_extension(rust_lib_name, ".rmeta"),
sibling = rust_lib,
)
+ rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata)
deps = transform_deps(ctx.attr.deps)
proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
@@ -191,7 +194,9 @@ def _rust_library_common(ctx, crate_type):
proc_macro_deps = depset(proc_macro_deps),
aliases = ctx.attr.aliases,
output = rust_lib,
+ rustc_output = generate_output_diagnostics(ctx, rust_lib),
metadata = rust_metadata,
+ rustc_rmeta_output = rustc_rmeta_output,
edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = ctx.attr.rustc_env,
rustc_env_files = ctx.files.rustc_env_files,
@@ -239,6 +244,7 @@ def _rust_binary_impl(ctx):
proc_macro_deps = depset(proc_macro_deps),
aliases = ctx.attr.aliases,
output = output,
+ rustc_output = generate_output_diagnostics(ctx, output),
edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = ctx.attr.rustc_env,
rustc_env_files = ctx.files.rustc_env_files,
@@ -312,6 +318,7 @@ def _rust_test_impl(ctx):
proc_macro_deps = depset(proc_macro_deps, transitive = [crate.proc_macro_deps]),
aliases = ctx.attr.aliases,
output = output,
+ rustc_output = generate_output_diagnostics(ctx, output),
edition = crate.edition,
rustc_env = rustc_env,
rustc_env_files = rustc_env_files,
@@ -355,6 +362,7 @@ def _rust_test_impl(ctx):
proc_macro_deps = depset(proc_macro_deps),
aliases = ctx.attr.aliases,
output = output,
+ rustc_output = generate_output_diagnostics(ctx, output),
edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = rustc_env,
rustc_env_files = ctx.files.rustc_env_files,
@@ -641,6 +649,9 @@ _common_attrs = {
allow_single_file = True,
cfg = "exec",
),
+ "_rustc_output_diagnostics": attr.label(
+ default = Label("//:rustc_output_diagnostics"),
+ ),
"_stamp_flag": attr.label(
doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
default = Label("//rust/private:stamp"),
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 21f57b12c3..481ef25694 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -23,7 +23,7 @@ load(
"CPP_LINK_STATIC_LIBRARY_ACTION_NAME",
)
load("//rust/private:common.bzl", "rust_common")
-load("//rust/private:providers.bzl", _BuildInfo = "BuildInfo")
+load("//rust/private:providers.bzl", "RustcOutputDiagnosticsInfo", _BuildInfo = "BuildInfo")
load("//rust/private:stamp.bzl", "is_stamping_enabled")
load(
"//rust/private:utils.bzl",
@@ -929,6 +929,10 @@ 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.rustc_rmeta_output:
+ process_wrapper_flags.add("--output-file", crate_info.rustc_rmeta_output.path)
+ elif crate_info.rustc_output:
+ process_wrapper_flags.add("--output-file", crate_info.rustc_output.path)
rustc_flags.add(error_format, format = "--error-format=%s")
@@ -1112,9 +1116,9 @@ def rustc_compile_action(
"""
crate_info = rust_common.create_crate_info(**crate_info_dict)
- build_metadata = None
- if "metadata" in crate_info_dict:
- build_metadata = crate_info_dict["metadata"]
+ build_metadata = crate_info_dict.get("metadata", None)
+ rustc_output = crate_info_dict.get("rustc_output", None)
+ rustc_rmeta_output = crate_info_dict.get("rustc_rmeta_output", None)
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
@@ -1193,7 +1197,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(rustc_output) or bool(rustc_rmeta_output),
skip_expanding_rustc_env = skip_expanding_rustc_env,
)
@@ -1256,6 +1260,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 rustc_output:
+ action_outputs.append(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.
@@ -1290,7 +1296,7 @@ def rustc_compile_action(
ctx.actions.run(
executable = ctx.executable._process_wrapper,
inputs = compile_inputs,
- outputs = [build_metadata],
+ outputs = [build_metadata] + [x for x in [rustc_rmeta_output] if x],
env = env,
arguments = args_metadata.all,
mnemonic = "RustcMetadata",
@@ -1456,12 +1462,24 @@ 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:
- providers.append(OutputGroupInfo(build_metadata = depset([build_metadata])))
+ output_group_info["build_metadata"] = depset([build_metadata])
+ if build_metadata:
+ output_group_info["build_metadata"] = depset([build_metadata])
+ if rustc_rmeta_output:
+ output_group_info["rustc_rmeta_output"] = depset([rustc_rmeta_output])
+ if rustc_output:
+ output_group_info["rustc_output"] = depset([rustc_output])
+
+ if output_group_info:
+ providers.append(OutputGroupInfo(**output_group_info))
return providers
@@ -2046,6 +2064,31 @@ error_format = rule(
build_setting = config.string(flag = True),
)
+def _rustc_output_diagnostics_impl(ctx):
+ """Implementation of the `rustc_output_diagnostics` rule
+
+ Args:
+ ctx (ctx): The rule's context object
+
+ Returns:
+ list: A list containing the RustcOutputDiagnosticsInfo provider
+ """
+ return [RustcOutputDiagnosticsInfo(
+ rustc_output_diagnostics = ctx.build_setting_value,
+ )]
+
+rustc_output_diagnostics = rule(
+ doc = (
+ "Setting this flag from the command line with `--@rules_rust//:rustc_output_diagnostics` " +
+ "makes rules_rust save rustc json output(suitable for consumption by rust-analyzer) in a file. " +
+ "These are accessible via the " +
+ "`rustc_rmeta_output`(for pipelined compilation) and `rustc_output` output groups. " +
+ "You can find these using `bazel cquery`"
+ ),
+ implementation = _rustc_output_diagnostics_impl,
+ build_setting = config.bool(flag = True),
+)
+
def _extra_rustc_flags_impl(ctx):
return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value)
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
index 89c41bbe04..36b83bfeb6 100644
--- a/rust/private/utils.bzl
+++ b/rust/private/utils.bzl
@@ -16,7 +16,7 @@
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", find_rules_cc_toolchain = "find_cpp_toolchain")
-load(":providers.bzl", "BuildInfo", "CrateGroupInfo", "CrateInfo", "DepInfo", "DepVariantInfo")
+load(":providers.bzl", "BuildInfo", "CrateGroupInfo", "CrateInfo", "DepInfo", "DepVariantInfo", "RustcOutputDiagnosticsInfo")
UNSUPPORTED_FEATURES = [
"thin_lto",
@@ -849,3 +849,29 @@ def _symlink_for_non_generated_source(ctx, src_file, package_root):
return src_symlink
else:
return src_file
+
+def generate_output_diagnostics(ctx, sibling, require_process_wrapper = True):
+ """Generates a .rustc-output file if it's required.
+
+ Args:
+ ctx: (ctx): The current rule's context object
+ sibling: (File): The file to generate the diagnostics for.
+ require_process_wrapper: (bool): Whether to require the process wrapper
+ in order to generate the .rustc-output file.
+ Returns:
+ Optional[File] The .rustc-object file, if generated.
+ """
+
+ # Since this feature requires error_format=json, we usually need
+ # process_wrapper, since it can write the json here, then convert it to the
+ # regular error format so the user can see the error properly.
+ if require_process_wrapper and not ctx.attr._process_wrapper:
+ return None
+ provider = ctx.attr._rustc_output_diagnostics[RustcOutputDiagnosticsInfo]
+ if not provider.rustc_output_diagnostics:
+ return None
+
+ return ctx.actions.declare_file(
+ sibling.basename + ".rustc-output",
+ sibling = sibling,
+ )
diff --git a/util/process_wrapper/main.rs b/util/process_wrapper/main.rs
index e618f833da..86135a52e7 100644
--- a/util/process_wrapper/main.rs
+++ b/util/process_wrapper/main.rs
@@ -100,6 +100,19 @@ fn main() -> Result<(), ProcessWrapperError> {
"unable to get child stderr".to_string(),
))?;
+ let mut output_file: Option