Skip to content

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

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
21 changes: 20 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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"])

Expand All @@ -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(
Expand Down
7 changes: 5 additions & 2 deletions docs/flatten.md
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,8 @@ A test rule for performing `rustfmt --check` on a set of targets

<pre>
CrateInfo(<a href="#CrateInfo-aliases">aliases</a>, <a href="#CrateInfo-compile_data">compile_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-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rust_lib_rustc_output">rust_lib_rustc_output</a>, <a href="#CrateInfo-rust_metadata_rustc_output">rust_metadata_rustc_output</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>)
</pre>

A provider containing general Crate information.
Expand All @@ -1394,12 +1395,14 @@ 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-rust_lib_rustc_output"></a>rust_lib_rustc_output | File: The output from rustc from producing the output file. It is optional. |
| <a id="CrateInfo-rust_metadata_rustc_output"></a>rust_metadata_rustc_output | File: The rmeta file produced for this crate. It is optional. |
| <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-srcs"></a>srcs | depset[File]: All source Files that are part of the crate. |
Expand Down
7 changes: 5 additions & 2 deletions docs/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

<pre>
CrateInfo(<a href="#CrateInfo-aliases">aliases</a>, <a href="#CrateInfo-compile_data">compile_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-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rust_lib_rustc_output">rust_lib_rustc_output</a>, <a href="#CrateInfo-rust_metadata_rustc_output">rust_metadata_rustc_output</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>)
</pre>

A provider containing general Crate information.
Expand All @@ -26,12 +27,14 @@ 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-rust_lib_rustc_output"></a>rust_lib_rustc_output | File: The output from rustc from producing the output file. It is optional. |
| <a id="CrateInfo-rust_metadata_rustc_output"></a>rust_metadata_rustc_output | File: The rmeta file produced for this crate. It is optional. |
| <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-srcs"></a>srcs | depset[File]: All source Files that are part of the crate. |
Expand Down
4 changes: 4 additions & 0 deletions rust/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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.

Expand Down
4 changes: 4 additions & 0 deletions rust/private/common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion rust/private/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
32 changes: 28 additions & 4 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ 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

Expand Down Expand Up @@ -275,14 +278,30 @@ def _rust_library_common(ctx, crate_type):
toolchain,
output_hash,
)

rust_lib = ctx.actions.declare_file(rust_lib_name)
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),
sibling = rust_lib,
)

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,
)
if output_diagnostics:
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))
Expand All @@ -300,7 +319,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,
Expand Down Expand Up @@ -598,7 +619,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.
Expand All @@ -611,7 +632,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)`.
"""),
Expand Down Expand Up @@ -673,6 +694,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"),
Expand Down Expand Up @@ -723,7 +747,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`.
"""),
Expand Down
59 changes: 54 additions & 5 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down Expand Up @@ -855,6 +860,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.rust_metadata_rustc_output:
process_wrapper_flags.add("--output-file", crate_info.rust_metadata_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)

Expand Down Expand Up @@ -1018,6 +1027,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)

Expand Down Expand Up @@ -1096,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
Expand Down Expand Up @@ -1156,6 +1167,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.
Expand Down Expand Up @@ -1189,7 +1202,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 [rust_metadata_rustc_output] if x],
env = env,
arguments = args_metadata.all,
mnemonic = "RustcMetadata",
Expand Down Expand Up @@ -1307,12 +1320,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:
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])
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])

if output_group_info:
providers.append(OutputGroupInfo(**output_group_info))

return providers

Expand Down Expand Up @@ -1823,6 +1848,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 <dir> -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)

Expand Down
Loading