Skip to content
Draft
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
2 changes: 2 additions & 0 deletions rust/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ load(
_rust_clippy = "rust_clippy",
_rust_clippy_action = "rust_clippy_action",
_rust_clippy_aspect = "rust_clippy_aspect",
_rust_create_clippy_action = "rust_create_clippy_action",
)
load("//rust/private:common.bzl", _rust_common = "rust_common")
load(
Expand Down Expand Up @@ -123,6 +124,7 @@ capture_clippy_output = _capture_clippy_output

rust_clippy_action = struct(
action = _rust_clippy_action,
create_action = _rust_create_clippy_action,
get_clippy_ready_crate_info = _get_clippy_ready_crate_info,
)
# See @rules_rust//rust/private:clippy.bzl for a complete description.
Expand Down
92 changes: 85 additions & 7 deletions rust/private/clippy.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -101,25 +101,44 @@ def get_clippy_ready_crate_info(target, aspect_ctx = None):
else:
return None

def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, config, output = None, success_marker = None, cap_at_warnings = False, extra_clippy_flags = [], error_format = None, clippy_diagnostics_file = None):
"""Run clippy with the specified parameters.

Args:
_RUST_CLIPPY_ACTION_MACRO_ARGS = """
ctx (ctx): The aspect's context object. This function should not read ctx.attr, but it might read ctx.rule.attr
clippy_executable (File): The clippy executable to run
process_wrapper (File): An executable process wrapper that can run clippy, usually @rules_rust//utils/process_wrapper
crate_info (CrateInfo): The source crate information
config (File): The clippy configuration file. Reference: https://doc.rust-lang.org/clippy/configuration.html#configuring-clippy
output (File): The output file for clippy stdout/stderr. If None, no output will be captured
success_marker (File): A file that will be written if clippy succeeds
exit_code_file (File): A file that will contain clippy's exit code
forward_clippy_exit_code (bool): If set, let the action exit with the same exit code as clippy
cap_at_warnings (bool): If set, it will cap all reports as warnings, allowing the build to continue even with clippy failures
extra_clippy_flags (List[str]): A list of extra options to pass to clippy. If not set, every warnings will be turned into errors
error_format (str): Which error format to use. Must be acceptable by rustc: https://doc.rust-lang.org/beta/rustc/command-line-arguments.html#--error-format-control-how-errors-are-produced
clippy_diagnostics_file (File): File to output diagnostics to. If None, no diagnostics will be written
"""

def rust_create_clippy_action(
ctx,
clippy_executable,
process_wrapper,
crate_info,
config,
output = None,
success_marker = None,
exit_code_file = None,
forward_clippy_exit_code = True,
cap_at_warnings = False,
extra_clippy_flags = [],
error_format = None,
clippy_diagnostics_file = None):
"""Calculate inputs and arguments to run clippy with the specified parameters.

Args: {args}

Returns:
None
"""
A struct indicating how to run clippy. The fields of the struct match the fields of `ctx.action.run`.
""".format(args = _RUST_CLIPPY_ACTION_MACRO_ARGS)
print("BL: first line flags ={}".format(extra_clippy_flags))
toolchain = find_toolchain(ctx)
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)

Expand Down Expand Up @@ -186,6 +205,8 @@ def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, conf
if crate_info.is_test:
args.rustc_flags.add("--test")

print("BL: inside flags ={}".format(extra_clippy_flags))

# Then append the clippy flags specified from the command line, so they override what is
# specified on the library.
clippy_flags += extra_clippy_flags
Expand All @@ -200,6 +221,13 @@ def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, conf
args.process_wrapper_flags.add("--touch-file", success_marker)
outputs.append(success_marker)

if forward_clippy_exit_code == False:
args.process_wrapper_flags.add("--forward-exit-code", "false")

if exit_code_file != None:
args.process_wrapper_flags.add("--exit-code-file", exit_code_file)
outputs.append(exit_code_file)

if clippy_flags or lint_files:
args.rustc_flags.add_all(clippy_flags)
else:
Expand All @@ -223,7 +251,7 @@ def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, conf
env["CLIPPY_CONF_DIR"] = "${{pwd}}/{}".format(config.dirname)
compile_inputs = depset([config], transitive = [compile_inputs])

ctx.actions.run(
return struct(
executable = process_wrapper,
inputs = compile_inputs,
outputs = outputs + [x for x in [clippy_diagnostics_file] if x],
Expand All @@ -235,6 +263,56 @@ def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, conf
toolchain = "@rules_rust//rust:toolchain_type",
)

def rust_clippy_action(
ctx,
clippy_executable,
process_wrapper,
crate_info,
config,
output = None,
success_marker = None,
exit_code_file = None,
forward_clippy_exit_code = True,
cap_at_warnings = False,
extra_clippy_flags = [],
error_format = None,
clippy_diagnostics_file = None):
"""Run clippy with the specified parameters.

Args: {args}

Returns:
None
""".format(args = _RUST_CLIPPY_ACTION_MACRO_ARGS)
print("BL: flags ={}".format(extra_clippy_flags))
clippy_action = rust_create_clippy_action(
ctx = ctx,
clippy_executable = clippy_executable,
process_wrapper = process_wrapper,
crate_info = crate_info,
config = config,
output = output,
success_marker = success_marker,
exit_code_file = exit_code_file,
forward_clippy_exit_code = forward_clippy_exit_code,
cap_at_warnings = cap_at_warnings,
extra_clippy_flags = extra_clippy_flags,
error_format = error_format,
clippy_diagnostics_file = clippy_diagnostics_file,
)

ctx.actions.run(
executable = clippy_action.executable,
inputs = clippy_action.inputs,
outputs = clippy_action.outputs,
env = clippy_action.env,
tools = clippy_action.tools,
arguments = clippy_action.arguments,
mnemonic = clippy_action.mnemonic,
progress_message = clippy_action.progress_message,
toolchain = clippy_action.toolchain,
)

def _clippy_aspect_impl(target, ctx):
# Exit early if a target already has a clippy output group. This
# can be useful for rules which always want to inhibit clippy.
Expand Down
21 changes: 19 additions & 2 deletions util/process_wrapper/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::collections::HashMap;
use std::fmt;
use std::fs::{copy, OpenOptions};
use std::io;
use std::io::Write;
use std::process::{exit, Command, ExitStatus, Stdio};

use tinyjson::JsonValue;
Expand Down Expand Up @@ -215,7 +216,7 @@ fn main() -> Result<(), ProcessWrapperError> {
.create(true)
.truncate(true)
.write(true)
.open(tf)
.open(&tf)
.map_err(|e| ProcessWrapperError(format!("failed to create touch file: {}", e)))?;
}
if let Some((copy_source, copy_dest)) = opts.copy_output {
Expand All @@ -228,7 +229,23 @@ fn main() -> Result<(), ProcessWrapperError> {
}
}

exit(code)
if let Some(ecf) = opts.exit_code_file {
let exit_code_file = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(&ecf)
.map_err(|e| ProcessWrapperError(format!("failed to create exit code file: {}", e)))?;
let mut writer = io::LineWriter::new(exit_code_file);
writeln!(writer, "{}", code)
.map_err(|e| ProcessWrapperError(format!("failed to write exit code to file: {}", e)))?;
}

if opts.forward_exit_code {
exit(code)
} else {
exit(0)
}
}

#[cfg(test)]
Expand Down
20 changes: 20 additions & 0 deletions util/process_wrapper/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub(crate) struct Options {
// If set, also logs all unprocessed output from the rustc output to this file.
// Meant to be used to get json output out of rustc for tooling usage.
pub(crate) output_file: Option<String>,
// If set, exit with the same code as the child process.
pub(crate) forward_exit_code: bool,
// If set, writes the exit code of the process into this file.
pub(crate) exit_code_file: Option<String>,
// If set, it configures rustc to emit an rmeta file and then
// quit.
pub(crate) rustc_quit_on_rmeta: bool,
Expand All @@ -64,6 +68,8 @@ pub(crate) fn options() -> Result<Options, OptionError> {
let mut stdout_file = None;
let mut stderr_file = None;
let mut output_file = None;
let mut forward_exit_code_raw = None;
let mut exit_code_file = None;
let mut rustc_quit_on_rmeta_raw = None;
let mut rustc_output_format_raw = None;
let mut flags = Flags::new();
Expand Down Expand Up @@ -102,6 +108,16 @@ pub(crate) fn options() -> Result<Options, OptionError> {
"Log all unprocessed subprocess stderr in this file.",
&mut output_file,
);
flags.define_flag(
"--forward-exit-code",
"If set, the process_wrapper will exit with the same exit code as the subprocess if it had no internal errors. True by default, disable with `--forward-exit-code=false`",
&mut forward_exit_code_raw,
);
flags.define_flag(
"--exit-code-file",
"Log the exit code of the process to this file.",
&mut exit_code_file,
);
flags.define_flag(
"--rustc-quit-on-rmeta",
"If enabled, this wrapper will terminate rustc after rmeta has been emitted.",
Expand Down Expand Up @@ -179,6 +195,8 @@ pub(crate) fn options() -> Result<Options, OptionError> {
})
.transpose()?;

// As we want `true` by default, we accept anything except `"false"` as `"true"`
let forward_exit_code = forward_exit_code_raw.is_some_and(|s| s != "false");
let rustc_quit_on_rmeta = rustc_quit_on_rmeta_raw.is_some_and(|s| s == "true");
let rustc_output_format = rustc_output_format_raw
.map(|v| match v.as_str() {
Expand Down Expand Up @@ -227,6 +245,8 @@ pub(crate) fn options() -> Result<Options, OptionError> {
stdout_file,
stderr_file,
output_file,
forward_exit_code,
exit_code_file,
rustc_quit_on_rmeta,
rustc_output_format,
})
Expand Down
Loading