Skip to content

Commit 4b3492a

Browse files
authored
Optionally capture clippy lints as a build output (#937)
* Allow clippy lints to optionally be captured as a build output, so they can be consumed by downstream tools integrated with the build system. * Fix buildifier lint. * Fix buildifier lints. * Fix missing import of ClippyInfo * Fix path for //:capture_clippy_output * Return ClippyInfo even if there is no crate info * Add test for failure semantics when capturing clippy output. * Fully qualify :providers.bzl * Rename ClippyConfigInfo to CaptureClippyOutputInfo * Update doc fields to state that captured output uses the configured error_format * Sort loads. * Sort loads. * Merge capture_output logic into a single if block. * Regenerate documentation * Build documentation for capture_clippy_output. * Update docs. * Update human-readable docs to specify that captured clippy output uses the configured error_format. * Make redundant doc fields consistent. Move CaptureClippyOutputInfo to provider.bzl. Improve readability of clippy_failure_test.sh
1 parent ec3bebf commit 4b3492a

File tree

11 files changed

+134
-32
lines changed

11 files changed

+134
-32
lines changed

BUILD.bazel

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
2-
load("//rust:rust.bzl", "error_format", "extra_rustc_flags")
2+
load("//rust:rust.bzl", "capture_clippy_output", "error_format", "extra_rustc_flags")
33

44
exports_files(["LICENSE"])
55

@@ -46,3 +46,9 @@ alias(
4646
actual = "//tools/rustfmt",
4747
visibility = ["//visibility:public"],
4848
)
49+
50+
capture_clippy_output(
51+
name = "capture_clippy_output",
52+
build_setting_default = False,
53+
visibility = ["//visibility:public"],
54+
)

docs/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ PAGES = dict([
6161
"rust_test_suite",
6262
"error_format",
6363
"extra_rustc_flags",
64+
"capture_clippy_output",
6465
],
6566
),
6667
page(

docs/defs.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,25 @@
1010
* [rust_test_suite](#rust_test_suite)
1111
* [error_format](#error_format)
1212
* [extra_rustc_flags](#extra_rustc_flags)
13+
* [capture_clippy_output](#capture_clippy_output)
14+
15+
<a id="#capture_clippy_output"></a>
16+
17+
## capture_clippy_output
18+
19+
<pre>
20+
capture_clippy_output(<a href="#capture_clippy_output-name">name</a>)
21+
</pre>
22+
23+
Control whether to print clippy output or store it to a file, using the configured error_format.
24+
25+
**ATTRIBUTES**
26+
27+
28+
| Name | Description | Type | Mandatory | Default |
29+
| :------------- | :------------- | :------------- | :------------- | :------------- |
30+
| <a id="capture_clippy_output-name"></a>name | A unique name for this target. | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required | |
31+
1332

1433
<a id="#error_format"></a>
1534

docs/flatten.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* [CrateInfo](#CrateInfo)
44
* [DepInfo](#DepInfo)
55
* [StdLibInfo](#StdLibInfo)
6+
* [capture_clippy_output](#capture_clippy_output)
67
* [cargo_bootstrap_repository](#cargo_bootstrap_repository)
78
* [cargo_build_script](#cargo_build_script)
89
* [cargo_env](#cargo_env)
@@ -47,6 +48,24 @@
4748
* [rustfmt_test](#rustfmt_test)
4849

4950

51+
<a id="#capture_clippy_output"></a>
52+
53+
## capture_clippy_output
54+
55+
<pre>
56+
capture_clippy_output(<a href="#capture_clippy_output-name">name</a>)
57+
</pre>
58+
59+
Control whether to print clippy output or store it to a file, using the configured error_format.
60+
61+
**ATTRIBUTES**
62+
63+
64+
| Name | Description | Type | Mandatory | Default |
65+
| :------------- | :------------- | :------------- | :------------- | :------------- |
66+
| <a id="capture_clippy_output-name"></a>name | A unique name for this target. | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required | |
67+
68+
5069
<a id="#cargo_bootstrap_repository"></a>
5170

5271
## cargo_bootstrap_repository

docs/symbols.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ load(
4242
)
4343
load(
4444
"@rules_rust//rust:defs.bzl",
45+
_capture_clippy_output = "capture_clippy_output",
4546
_error_format = "error_format",
4647
_extra_rustc_flags = "extra_rustc_flags",
4748
_rust_analyzer = "rust_analyzer",
@@ -147,6 +148,7 @@ error_format = _error_format
147148
extra_rustc_flags = _extra_rustc_flags
148149
incompatible_flag = _incompatible_flag
149150
fail_when_enabled = _fail_when_enabled
151+
capture_clippy_output = _capture_clippy_output
150152

151153
CrateInfo = _CrateInfo
152154
DepInfo = _DepInfo

rust/defs.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ load(
2020
)
2121
load(
2222
"//rust/private:clippy.bzl",
23+
_capture_clippy_output = "capture_clippy_output",
2324
_rust_clippy = "rust_clippy",
2425
_rust_clippy_aspect = "rust_clippy_aspect",
2526
)
@@ -91,6 +92,9 @@ rust_clippy_aspect = _rust_clippy_aspect
9192
rust_clippy = _rust_clippy
9293
# See @rules_rust//rust/private:clippy.bzl for a complete description.
9394

95+
capture_clippy_output = _capture_clippy_output
96+
# See @rules_rust//rust/private:clippy.bzl for a complete description.
97+
9498
error_format = _error_format
9599
# See @rules_rust//rust/private:rustc.bzl for a complete description.
96100

rust/private/clippy.bzl

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""A module defining clippy rules"""
1616

1717
load("//rust/private:common.bzl", "rust_common")
18+
load("//rust/private:providers.bzl", "CaptureClippyOutputInfo", "ClippyInfo")
1819
load(
1920
"//rust/private:rustc.bzl",
2021
"collect_deps",
@@ -51,7 +52,7 @@ def _get_clippy_ready_crate_info(target, aspect_ctx):
5152
def _clippy_aspect_impl(target, ctx):
5253
crate_info = _get_clippy_ready_crate_info(target, ctx)
5354
if not crate_info:
54-
return []
55+
return [ClippyInfo(output = depset([]))]
5556

5657
toolchain = find_toolchain(ctx)
5758
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
@@ -75,10 +76,6 @@ def _clippy_aspect_impl(target, ctx):
7576
build_info,
7677
)
7778

78-
# A marker file indicating clippy has executed successfully.
79-
# This file is necessary because "ctx.actions.run" mandates an output.
80-
clippy_marker = ctx.actions.declare_file(ctx.label.name + ".clippy.ok")
81-
8279
args, env = construct_arguments(
8380
ctx = ctx,
8481
attr = ctx.rule.attr,
@@ -97,24 +94,35 @@ def _clippy_aspect_impl(target, ctx):
9794
emit = ["dep-info", "metadata"],
9895
)
9996

100-
# Turn any warnings from clippy or rustc into an error, as otherwise
101-
# Bazel will consider the execution result of the aspect to be "success",
102-
# and Clippy won't be re-triggered unless the source file is modified.
103-
if "__bindgen" in ctx.rule.attr.tags:
104-
# bindgen-generated content is likely to trigger warnings, so
105-
# only fail on clippy warnings
106-
args.rustc_flags.add("-Dclippy::style")
107-
args.rustc_flags.add("-Dclippy::correctness")
108-
args.rustc_flags.add("-Dclippy::complexity")
109-
args.rustc_flags.add("-Dclippy::perf")
110-
else:
111-
# fail on any warning
112-
args.rustc_flags.add("-Dwarnings")
113-
11497
if crate_info.is_test:
11598
args.rustc_flags.add("--test")
11699

117-
args.process_wrapper_flags.add("--touch-file", clippy_marker.path)
100+
if ctx.attr._capture_output[CaptureClippyOutputInfo].capture_output:
101+
clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.out")
102+
args.process_wrapper_flags.add("--stdout-file", clippy_out.path)
103+
104+
# If we are capturing the output, we want the build system to be able to keep going
105+
# and consume the output. Some clippy lints are denials, so we treat them as warnings.
106+
args.rustc_flags.add("-Wclippy::all")
107+
else:
108+
# A marker file indicating clippy has executed successfully.
109+
# This file is necessary because "ctx.actions.run" mandates an output.
110+
clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.ok")
111+
args.process_wrapper_flags.add("--touch-file", clippy_out.path)
112+
113+
# Turn any warnings from clippy or rustc into an error, as otherwise
114+
# Bazel will consider the execution result of the aspect to be "success",
115+
# and Clippy won't be re-triggered unless the source file is modified.
116+
if "__bindgen" in ctx.rule.attr.tags:
117+
# bindgen-generated content is likely to trigger warnings, so
118+
# only fail on clippy warnings
119+
args.rustc_flags.add("-Dclippy::style")
120+
args.rustc_flags.add("-Dclippy::correctness")
121+
args.rustc_flags.add("-Dclippy::complexity")
122+
args.rustc_flags.add("-Dclippy::perf")
123+
else:
124+
# fail on any warning
125+
args.rustc_flags.add("-Dwarnings")
118126

119127
# Upstream clippy requires one of these two filenames or it silently uses
120128
# the default config. Enforce the naming so users are not confused.
@@ -127,15 +135,16 @@ def _clippy_aspect_impl(target, ctx):
127135
ctx.actions.run(
128136
executable = ctx.executable._process_wrapper,
129137
inputs = compile_inputs,
130-
outputs = [clippy_marker],
138+
outputs = [clippy_out],
131139
env = env,
132140
tools = [toolchain.clippy_driver],
133141
arguments = args.all,
134142
mnemonic = "Clippy",
135143
)
136144

137145
return [
138-
OutputGroupInfo(clippy_checks = depset([clippy_marker])),
146+
OutputGroupInfo(clippy_checks = depset([clippy_out])),
147+
ClippyInfo(output = depset([clippy_out])),
139148
]
140149

141150
# Example: Run the clippy checker on all targets in the codebase.
@@ -146,6 +155,10 @@ rust_clippy_aspect = aspect(
146155
fragments = ["cpp"],
147156
host_fragments = ["cpp"],
148157
attrs = {
158+
"_capture_output": attr.label(
159+
doc = "Value of the `capture_clippy_output` build setting",
160+
default = Label("//:capture_clippy_output"),
161+
),
149162
"_cc_toolchain": attr.label(
150163
doc = (
151164
"Required attribute to access the cc_toolchain. See [Accessing the C++ toolchain]" +
@@ -170,6 +183,7 @@ rust_clippy_aspect = aspect(
170183
cfg = "exec",
171184
),
172185
},
186+
provides = [ClippyInfo],
173187
toolchains = [
174188
str(Label("//rust:toolchain")),
175189
"@bazel_tools//tools/cpp:toolchain_type",
@@ -260,3 +274,20 @@ rust_clippy(
260274
```
261275
""",
262276
)
277+
278+
def _capture_clippy_output_impl(ctx):
279+
"""Implementation of the `capture_clippy_output` rule
280+
281+
Args:
282+
ctx (ctx): The rule's context object
283+
284+
Returns:
285+
list: A list containing the CaptureClippyOutputInfo provider
286+
"""
287+
return [CaptureClippyOutputInfo(capture_output = ctx.build_setting_value)]
288+
289+
capture_clippy_output = rule(
290+
doc = "Control whether to print clippy output or store it to a file, using the configured error_format.",
291+
implementation = _capture_clippy_output_impl,
292+
build_setting = config.bool(flag = True),
293+
)

rust/private/providers.bzl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,15 @@ StdLibInfo = provider(
6868
"std_rlibs": "List[File]: All `.rlib` files",
6969
},
7070
)
71+
72+
CaptureClippyOutputInfo = provider(
73+
doc = "Value of the `capture_clippy_output` build setting",
74+
fields = {"capture_output": "Value of the `capture_clippy_output` build setting"},
75+
)
76+
77+
ClippyInfo = provider(
78+
doc = "Provides information on a clippy run.",
79+
fields = {
80+
"output": "File with the clippy output.",
81+
},
82+
)

rust/rust.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
load(
1818
"//rust:defs.bzl",
19+
_capture_clippy_output = "capture_clippy_output",
1920
_error_format = "error_format",
2021
_extra_rustc_flags = "extra_rustc_flags",
2122
_rust_analyzer = "rust_analyzer",
@@ -81,6 +82,9 @@ rust_clippy_aspect = _rust_clippy_aspect
8182
rust_clippy = _rust_clippy
8283
# See @rules_rust//rust:private/clippy.bzl for a complete description.
8384

85+
capture_clippy_output = _capture_clippy_output
86+
# See @rules_rust//rust:private/clippy.bzl for a complete description.
87+
8488
rust_analyzer_aspect = _rust_analyzer_aspect
8589
# See @rules_rust//rust:private/rust_analyzer.bzl for a complete description.
8690

rust/rust_common.bzl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Module with Rust definitions required to write custom Rust rules."""
1616

17-
load("//rust/private:providers.bzl", _CrateInfo = "CrateInfo")
17+
load("//rust/private:providers.bzl", _ClippyInfo = "ClippyInfo", _CrateInfo = "CrateInfo")
1818

1919
CrateInfo = _CrateInfo
20+
ClippyInfo = _ClippyInfo

0 commit comments

Comments
 (0)