Skip to content

Commit

Permalink
Add an option to save the json output from rustc to pass to rust-anal…
Browse files Browse the repository at this point in the history
…yzer (#1942)

@googleson78 originally wrote
#1657 in order to solve
this problem. As discussed in that PR, they're no longer working on
this, so I offered to pick this up. This is the same PR as that one, but
has some bugfixes and refactoring applied.

---------

Co-authored-by: Georgi Lyubenov <georgi.lyubenov@tweag.io>
Co-authored-by: UebelAndre <github@uebelandre.com>
  • Loading branch information
3 people authored Dec 5, 2023
1 parent 742cb46 commit 0bded80
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 50 deletions.
8 changes: 8 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ load(
"is_proc_macro_dep_enabled",
"no_std",
"per_crate_rustc_flag",
"rustc_output_diagnostics",
)

exports_files(["LICENSE"])
Expand All @@ -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(
Expand Down
8 changes: 5 additions & 3 deletions docs/flatten.md
Original file line number Diff line number Diff line change
Expand Up @@ -1439,8 +1439,8 @@ A toolchain for [rustfmt](https://rust-lang.github.io/rustfmt/)

<pre>
CrateInfo(<a href="#CrateInfo-aliases">aliases</a>, <a href="#CrateInfo-compile_data">compile_data</a>, <a href="#CrateInfo-compile_data_targets">compile_data_targets</a>, <a href="#CrateInfo-data">data</a>, <a href="#CrateInfo-deps">deps</a>, <a href="#CrateInfo-edition">edition</a>, <a href="#CrateInfo-is_test">is_test</a>, <a href="#CrateInfo-metadata">metadata</a>, <a href="#CrateInfo-name">name</a>,
<a href="#CrateInfo-output">output</a>, <a href="#CrateInfo-owner">owner</a>, <a href="#CrateInfo-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rustc_env">rustc_env</a>, <a href="#CrateInfo-rustc_env_files">rustc_env_files</a>, <a href="#CrateInfo-srcs">srcs</a>, <a href="#CrateInfo-type">type</a>,
<a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>)
<a href="#CrateInfo-output">output</a>, <a href="#CrateInfo-owner">owner</a>, <a href="#CrateInfo-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rustc_env">rustc_env</a>, <a href="#CrateInfo-rustc_env_files">rustc_env_files</a>, <a href="#CrateInfo-rustc_output">rustc_output</a>,
<a href="#CrateInfo-rustc_rmeta_output">rustc_rmeta_output</a>, <a href="#CrateInfo-srcs">srcs</a>, <a href="#CrateInfo-type">type</a>, <a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>)
</pre>

A provider containing general Crate information.
Expand All @@ -1457,14 +1457,16 @@ A provider containing general Crate information.
| <a id="CrateInfo-deps"></a>deps | depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers. |
| <a id="CrateInfo-edition"></a>edition | str: The edition of this crate. |
| <a id="CrateInfo-is_test"></a>is_test | bool: If the crate is being compiled in a test context |
| <a id="CrateInfo-metadata"></a>metadata | File: The rmeta file produced for this crate. It is optional. |
| <a id="CrateInfo-metadata"></a>metadata | File: The output from rustc from producing the output file. It is optional. |
| <a id="CrateInfo-name"></a>name | str: The name of this crate. |
| <a id="CrateInfo-output"></a>output | File: The output File that will be produced, depends on crate type. |
| <a id="CrateInfo-owner"></a>owner | Label: The label of the target that produced this CrateInfo |
| <a id="CrateInfo-proc_macro_deps"></a>proc_macro_deps | depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers. |
| <a id="CrateInfo-root"></a>root | File: The source File entrypoint to this crate, eg. lib.rs |
| <a id="CrateInfo-rustc_env"></a>rustc_env | Dict[String, String]: Additional <code>"key": "value"</code> environment variables to set for rustc. |
| <a id="CrateInfo-rustc_env_files"></a>rustc_env_files | [File]: Files containing additional environment variables to set for rustc. |
| <a id="CrateInfo-rustc_output"></a>rustc_output | File: The output from rustc from producing the output file. It is optional. |
| <a id="CrateInfo-rustc_rmeta_output"></a>rustc_rmeta_output | File: The rmeta file produced for this crate. It is optional. |
| <a id="CrateInfo-srcs"></a>srcs | depset[File]: All source Files that are part of the crate. |
| <a id="CrateInfo-type"></a>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)). |
| <a id="CrateInfo-wrapped_crate_type"></a>wrapped_crate_type | str, optional: The original crate type for targets generated using a previously defined crate (typically tests using the <code>rust_test::crate</code> attribute) |
Expand Down
8 changes: 5 additions & 3 deletions docs/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

<pre>
CrateInfo(<a href="#CrateInfo-aliases">aliases</a>, <a href="#CrateInfo-compile_data">compile_data</a>, <a href="#CrateInfo-compile_data_targets">compile_data_targets</a>, <a href="#CrateInfo-data">data</a>, <a href="#CrateInfo-deps">deps</a>, <a href="#CrateInfo-edition">edition</a>, <a href="#CrateInfo-is_test">is_test</a>, <a href="#CrateInfo-metadata">metadata</a>, <a href="#CrateInfo-name">name</a>,
<a href="#CrateInfo-output">output</a>, <a href="#CrateInfo-owner">owner</a>, <a href="#CrateInfo-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rustc_env">rustc_env</a>, <a href="#CrateInfo-rustc_env_files">rustc_env_files</a>, <a href="#CrateInfo-srcs">srcs</a>, <a href="#CrateInfo-type">type</a>,
<a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>)
<a href="#CrateInfo-output">output</a>, <a href="#CrateInfo-owner">owner</a>, <a href="#CrateInfo-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rustc_env">rustc_env</a>, <a href="#CrateInfo-rustc_env_files">rustc_env_files</a>, <a href="#CrateInfo-rustc_output">rustc_output</a>,
<a href="#CrateInfo-rustc_rmeta_output">rustc_rmeta_output</a>, <a href="#CrateInfo-srcs">srcs</a>, <a href="#CrateInfo-type">type</a>, <a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>)
</pre>

A provider containing general Crate information.
Expand All @@ -29,14 +29,16 @@ A provider containing general Crate information.
| <a id="CrateInfo-deps"></a>deps | depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers. |
| <a id="CrateInfo-edition"></a>edition | str: The edition of this crate. |
| <a id="CrateInfo-is_test"></a>is_test | bool: If the crate is being compiled in a test context |
| <a id="CrateInfo-metadata"></a>metadata | File: The rmeta file produced for this crate. It is optional. |
| <a id="CrateInfo-metadata"></a>metadata | File: The output from rustc from producing the output file. It is optional. |
| <a id="CrateInfo-name"></a>name | str: The name of this crate. |
| <a id="CrateInfo-output"></a>output | File: The output File that will be produced, depends on crate type. |
| <a id="CrateInfo-owner"></a>owner | Label: The label of the target that produced this CrateInfo |
| <a id="CrateInfo-proc_macro_deps"></a>proc_macro_deps | depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers. |
| <a id="CrateInfo-root"></a>root | File: The source File entrypoint to this crate, eg. lib.rs |
| <a id="CrateInfo-rustc_env"></a>rustc_env | Dict[String, String]: Additional <code>"key": "value"</code> environment variables to set for rustc. |
| <a id="CrateInfo-rustc_env_files"></a>rustc_env_files | [File]: Files containing additional environment variables to set for rustc. |
| <a id="CrateInfo-rustc_output"></a>rustc_output | File: The output from rustc from producing the output file. It is optional. |
| <a id="CrateInfo-rustc_rmeta_output"></a>rustc_rmeta_output | File: The rmeta file produced for this crate. It is optional. |
| <a id="CrateInfo-srcs"></a>srcs | depset[File]: All source Files that are part of the crate. |
| <a id="CrateInfo-type"></a>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)). |
| <a id="CrateInfo-wrapped_crate_type"></a>wrapped_crate_type | str, optional: The original crate type for targets generated using a previously defined crate (typically tests using the <code>rust_test::crate</code> attribute) |
Expand Down
10 changes: 10 additions & 0 deletions docs/rust_analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.



<a id="rust_analyzer_toolchain"></a>

Expand Down
10 changes: 10 additions & 0 deletions docs/rust_analyzer.vm
Original file line number Diff line number Diff line change
Expand Up @@ -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.

]]#
4 changes: 4 additions & 0 deletions rust/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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.

Expand Down
4 changes: 4 additions & 0 deletions rust/private/common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 13 additions & 1 deletion rust/private/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ 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",
"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 " +
Expand Down Expand Up @@ -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 " +
Expand Down
11 changes: 11 additions & 0 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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))
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"),
Expand Down
61 changes: 52 additions & 9 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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,
)

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down
Loading

0 comments on commit 0bded80

Please sign in to comment.