Skip to content

Add an option to save the json output from rustc to pass to rust-analyzer #1942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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