Skip to content

Commit 327d45f

Browse files
committed
Add a flag output_diagnostics.
When this flag is set, bazel will write diagnostics to the output groups rustc_output and rustc_rmeta_output. Change-Id: I2690b571a0bc61fa58dc11fb480b725985603bd5
1 parent 3e24dd4 commit 327d45f

File tree

7 files changed

+119
-11
lines changed

7 files changed

+119
-11
lines changed

BUILD.bazel

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ load(
1313
"is_proc_macro_dep_enabled",
1414
"no_std",
1515
"per_crate_rustc_flag",
16+
"rustc_output_diagnostics",
1617
)
1718

1819
exports_files(["LICENSE"])
@@ -32,6 +33,13 @@ error_format(
3233
visibility = ["//visibility:public"],
3334
)
3435

36+
# This setting may be changed from the command line to generate rustc diagnostics.
37+
rustc_output_diagnostics(
38+
name = "rustc_output_diagnostics",
39+
build_setting_default = False,
40+
visibility = ["//visibility:public"],
41+
)
42+
3543
# This setting may be used to pass extra options to clippy from the command line.
3644
# It applies across all targets.
3745
clippy_flags(

rust/defs.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ load(
5252
_is_proc_macro_dep_enabled = "is_proc_macro_dep_enabled",
5353
_no_std = "no_std",
5454
_per_crate_rustc_flag = "per_crate_rustc_flag",
55+
_rustc_output_diagnostics = "rustc_output_diagnostics",
5556
)
5657
load(
5758
"//rust/private:rustdoc.bzl",
@@ -109,6 +110,9 @@ rust_clippy = _rust_clippy
109110
capture_clippy_output = _capture_clippy_output
110111
# See @rules_rust//rust/private:clippy.bzl for a complete description.
111112

113+
rustc_output_diagnostics = _rustc_output_diagnostics
114+
# See @rules_rust//rust/private:rustc.bzl for a complete description.
115+
112116
error_format = _error_format
113117
# See @rules_rust//rust/private:rustc.bzl for a complete description.
114118

rust/private/common.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ def _create_crate_info(**kwargs):
5151
kwargs.update({"wrapped_crate_type": None})
5252
if not "metadata" in kwargs:
5353
kwargs.update({"metadata": None})
54+
if not "rustc_rmeta_output" in kwargs:
55+
kwargs.update({"rustc_rmeta_output": None})
56+
if not "rustc_output" in kwargs:
57+
kwargs.update({"rustc_output": None})
5458
if not "rustc_env_files" in kwargs:
5559
kwargs.update({"rustc_env_files": []})
5660
if not "data" in kwargs:

rust/private/providers.bzl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ CrateInfo = provider(
2424
"deps": "depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers.",
2525
"edition": "str: The edition of this crate.",
2626
"is_test": "bool: If the crate is being compiled in a test context",
27-
"metadata": "File: The rmeta file produced for this crate. It is optional.",
27+
"metadata": "File: The output from rustc from producing the output file. It is optional.",
2828
"name": "str: The name of this crate.",
2929
"output": "File: The output File that will be produced, depends on crate type.",
3030
"owner": "Label: The label of the target that produced this CrateInfo",
3131
"proc_macro_deps": "depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers.",
3232
"root": "File: The source File entrypoint to this crate, eg. lib.rs",
3333
"rustc_env": "Dict[String, String]: Additional `\"key\": \"value\"` environment variables to set for rustc.",
3434
"rustc_env_files": "[File]: Files containing additional environment variables to set for rustc.",
35+
"rustc_output": "File: The output from rustc from producing the output file. It is optional.",
36+
"rustc_rmeta_output": "File: The rmeta file produced for this crate. It is optional.",
3537
"srcs": "depset[File]: All source Files that are part of the crate.",
3638
"type": (
3739
"str: The type of this crate " +
@@ -94,6 +96,16 @@ DepVariantInfo = provider(
9496
},
9597
)
9698

99+
RustcOutputDiagnosticsInfo = provider(
100+
doc = (
101+
"Save json diagnostics from rustc. Json diagnostics are able to be " +
102+
"consumed by tools such as rust-analyzer to provide IDE integration"
103+
),
104+
fields = {
105+
"rustc_output_diagnostics": "bool: Whether or not to output diagnostics",
106+
},
107+
)
108+
97109
StdLibInfo = provider(
98110
doc = (
99111
"A collection of files either found within the `rust-stdlib` artifact or " +

rust/private/rust.bzl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ load(
2828
"determine_output_hash",
2929
"expand_dict_value_locations",
3030
"find_toolchain",
31+
"generate_output_diagnostics",
3132
"get_edition",
3233
"get_import_macro_deps",
3334
"transform_deps",
@@ -168,11 +169,13 @@ def _rust_library_common(ctx, crate_type):
168169
)
169170
rust_lib = ctx.actions.declare_file(rust_lib_name)
170171
rust_metadata = None
172+
rustc_rmeta_output = None
171173
if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining:
172174
rust_metadata = ctx.actions.declare_file(
173175
paths.replace_extension(rust_lib_name, ".rmeta"),
174176
sibling = rust_lib,
175177
)
178+
rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata)
176179

177180
deps = transform_deps(ctx.attr.deps)
178181
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):
191194
proc_macro_deps = depset(proc_macro_deps),
192195
aliases = ctx.attr.aliases,
193196
output = rust_lib,
197+
rustc_output = generate_output_diagnostics(ctx, rust_lib),
194198
metadata = rust_metadata,
199+
rustc_rmeta_output = rustc_rmeta_output,
195200
edition = get_edition(ctx.attr, toolchain, ctx.label),
196201
rustc_env = ctx.attr.rustc_env,
197202
rustc_env_files = ctx.files.rustc_env_files,
@@ -239,6 +244,7 @@ def _rust_binary_impl(ctx):
239244
proc_macro_deps = depset(proc_macro_deps),
240245
aliases = ctx.attr.aliases,
241246
output = output,
247+
rustc_output = generate_output_diagnostics(ctx, output),
242248
edition = get_edition(ctx.attr, toolchain, ctx.label),
243249
rustc_env = ctx.attr.rustc_env,
244250
rustc_env_files = ctx.files.rustc_env_files,
@@ -312,6 +318,7 @@ def _rust_test_impl(ctx):
312318
proc_macro_deps = depset(proc_macro_deps, transitive = [crate.proc_macro_deps]),
313319
aliases = ctx.attr.aliases,
314320
output = output,
321+
rustc_output = generate_output_diagnostics(ctx, output),
315322
edition = crate.edition,
316323
rustc_env = rustc_env,
317324
rustc_env_files = rustc_env_files,
@@ -355,6 +362,7 @@ def _rust_test_impl(ctx):
355362
proc_macro_deps = depset(proc_macro_deps),
356363
aliases = ctx.attr.aliases,
357364
output = output,
365+
rustc_output = generate_output_diagnostics(ctx, output),
358366
edition = get_edition(ctx.attr, toolchain, ctx.label),
359367
rustc_env = rustc_env,
360368
rustc_env_files = ctx.files.rustc_env_files,
@@ -641,6 +649,9 @@ _common_attrs = {
641649
allow_single_file = True,
642650
cfg = "exec",
643651
),
652+
"_rustc_output_diagnostics": attr.label(
653+
default = Label("//:rustc_output_diagnostics"),
654+
),
644655
"_stamp_flag": attr.label(
645656
doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
646657
default = Label("//rust/private:stamp"),

rust/private/rustc.bzl

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ load(
2323
"CPP_LINK_STATIC_LIBRARY_ACTION_NAME",
2424
)
2525
load("//rust/private:common.bzl", "rust_common")
26-
load("//rust/private:providers.bzl", _BuildInfo = "BuildInfo")
26+
load("//rust/private:providers.bzl", "RustcOutputDiagnosticsInfo", _BuildInfo = "BuildInfo")
2727
load("//rust/private:stamp.bzl", "is_stamping_enabled")
2828
load(
2929
"//rust/private:utils.bzl",
@@ -929,6 +929,10 @@ def construct_arguments(
929929
if build_metadata:
930930
# Configure process_wrapper to terminate rustc when metadata are emitted
931931
process_wrapper_flags.add("--rustc-quit-on-rmeta", "true")
932+
if crate_info.rustc_rmeta_output:
933+
process_wrapper_flags.add("--output-file", crate_info.rustc_rmeta_output.path)
934+
elif crate_info.rustc_output:
935+
process_wrapper_flags.add("--output-file", crate_info.rustc_output.path)
932936

933937
rustc_flags.add(error_format, format = "--error-format=%s")
934938

@@ -1110,9 +1114,9 @@ def rustc_compile_action(
11101114
"""
11111115
crate_info = rust_common.create_crate_info(**crate_info_dict)
11121116

1113-
build_metadata = None
1114-
if "metadata" in crate_info_dict:
1115-
build_metadata = crate_info_dict["metadata"]
1117+
build_metadata = crate_info_dict.get("metadata", None)
1118+
rustc_output = crate_info_dict.get("rustc_output", None)
1119+
rustc_rmeta_output = crate_info_dict.get("rustc_rmeta_output", None)
11161120

11171121
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
11181122

@@ -1191,7 +1195,7 @@ def rustc_compile_action(
11911195
build_flags_files = build_flags_files,
11921196
force_all_deps_direct = force_all_deps_direct,
11931197
stamp = stamp,
1194-
use_json_output = bool(build_metadata),
1198+
use_json_output = bool(build_metadata) or bool(rustc_output) or bool(rustc_rmeta_output),
11951199
skip_expanding_rustc_env = skip_expanding_rustc_env,
11961200
)
11971201

@@ -1254,6 +1258,8 @@ def rustc_compile_action(
12541258

12551259
# The action might generate extra output that we don't want to include in the `DefaultInfo` files.
12561260
action_outputs = list(outputs)
1261+
if rustc_output:
1262+
action_outputs.append(rustc_output)
12571263

12581264
# Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate
12591265
# types that benefit from having debug information in a separate file.
@@ -1288,7 +1294,7 @@ def rustc_compile_action(
12881294
ctx.actions.run(
12891295
executable = ctx.executable._process_wrapper,
12901296
inputs = compile_inputs,
1291-
outputs = [build_metadata],
1297+
outputs = [build_metadata] + [x for x in [rustc_rmeta_output] if x],
12921298
env = env,
12931299
arguments = args_metadata.all,
12941300
mnemonic = "RustcMetadata",
@@ -1454,12 +1460,24 @@ def rustc_compile_action(
14541460

14551461
if toolchain.target_arch != "wasm32":
14561462
providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library)
1463+
1464+
output_group_info = {}
1465+
14571466
if pdb_file:
1458-
providers.append(OutputGroupInfo(pdb_file = depset([pdb_file])))
1467+
output_group_info["pdb_file"] = depset([pdb_file])
14591468
if dsym_folder:
1460-
providers.append(OutputGroupInfo(dsym_folder = depset([dsym_folder])))
1469+
output_group_info["dsym_folder"] = depset([dsym_folder])
14611470
if build_metadata:
1462-
providers.append(OutputGroupInfo(build_metadata = depset([build_metadata])))
1471+
output_group_info["build_metadata"] = depset([build_metadata])
1472+
if build_metadata:
1473+
output_group_info["build_metadata"] = depset([build_metadata])
1474+
if rustc_rmeta_output:
1475+
output_group_info["rustc_rmeta_output"] = depset([rustc_rmeta_output])
1476+
if rustc_output:
1477+
output_group_info["rustc_output"] = depset([rustc_output])
1478+
1479+
if output_group_info:
1480+
providers.append(OutputGroupInfo(**output_group_info))
14631481

14641482
return providers
14651483

@@ -2044,6 +2062,31 @@ error_format = rule(
20442062
build_setting = config.string(flag = True),
20452063
)
20462064

2065+
def _rustc_output_diagnostics_impl(ctx):
2066+
"""Implementation of the `rustc_output_diagnostics` rule
2067+
2068+
Args:
2069+
ctx (ctx): The rule's context object
2070+
2071+
Returns:
2072+
list: A list containing the RustcOutputDiagnosticsInfo provider
2073+
"""
2074+
return [RustcOutputDiagnosticsInfo(
2075+
rustc_output_diagnostics = ctx.build_setting_value,
2076+
)]
2077+
2078+
rustc_output_diagnostics = rule(
2079+
doc = (
2080+
"Setting this flag from the command line with `--@rules_rust//:rustc_output_diagnostics` " +
2081+
"makes rules_rust save rustc json output(suitable for consumption by rust-analyzer) in a file. " +
2082+
"These are accessible via the " +
2083+
"`rustc_rmeta_output`(for pipelined compilation) and `rustc_output` output groups. " +
2084+
"You can find these using `bazel cquery`"
2085+
),
2086+
implementation = _rustc_output_diagnostics_impl,
2087+
build_setting = config.bool(flag = True),
2088+
)
2089+
20472090
def _extra_rustc_flags_impl(ctx):
20482091
return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value)
20492092

rust/private/utils.bzl

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
load("@bazel_skylib//lib:paths.bzl", "paths")
1818
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", find_rules_cc_toolchain = "find_cpp_toolchain")
19-
load(":providers.bzl", "BuildInfo", "CrateGroupInfo", "CrateInfo", "DepInfo", "DepVariantInfo")
19+
load(":providers.bzl", "BuildInfo", "CrateGroupInfo", "CrateInfo", "DepInfo", "DepVariantInfo", "RustcOutputDiagnosticsInfo")
2020

2121
UNSUPPORTED_FEATURES = [
2222
"thin_lto",
@@ -849,3 +849,29 @@ def _symlink_for_non_generated_source(ctx, src_file, package_root):
849849
return src_symlink
850850
else:
851851
return src_file
852+
853+
def generate_output_diagnostics(ctx, sibling, require_process_wrapper = True):
854+
"""Generates a .rustc-output file if it's required.
855+
856+
Args:
857+
ctx: (ctx): The current rule's context object
858+
sibling: (File): The file to generate the diagnostics for.
859+
require_process_wrapper: (bool): Whether to require the process wrapper
860+
in order to generate the .rustc-output file.
861+
Returns:
862+
Optional[File] The .rustc-object file, if generated.
863+
"""
864+
865+
# Since this feature requires error_format=json, we usually need
866+
# process_wrapper, since it can write the json here, then convert it to the
867+
# regular error format so the user can see the error properly.
868+
if require_process_wrapper and not ctx.attr._process_wrapper:
869+
return None
870+
provider = ctx.attr._rustc_output_diagnostics[RustcOutputDiagnosticsInfo]
871+
if not provider.rustc_output_diagnostics:
872+
return None
873+
874+
return ctx.actions.declare_file(
875+
sibling.basename + ".rustc-output",
876+
sibling = sibling,
877+
)

0 commit comments

Comments
 (0)