Skip to content
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
11 changes: 10 additions & 1 deletion rust/private/clippy.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ 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):
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:
Expand All @@ -112,6 +112,8 @@ def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, conf
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
Expand Down Expand Up @@ -200,6 +202,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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simpler: would a user ever need to set one option but not the other? If there's an exit code file, then capture, otherwise forward

Copy link
Contributor Author

@blorente blorente Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, but I'm not convinced it's the right approach. Because this is the process wrapper so it can be used for anything, I can see someone wanting to ignore non-zero exit values but not wanting to declare a file, e.g. if we 'bazel run'.

I agree that this is not a real use case right now, so I could definitely be convinced to collapse the arguments.

args.process_wrapper_flags.add("--swallow-subprocess-exit-code", "true")

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 Down
20 changes: 19 additions & 1 deletion 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 @@ -228,7 +229,24 @@ 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
19 changes: 19 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 exit code as the child process if there are no internal errors.
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 swallow_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(
"--swallow-subprocess-exit-code",
"If set, the process_wrapper will not forward the exit code for the subprocess, and instead only fail if there are internal errors.",
&mut swallow_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,7 @@ pub(crate) fn options() -> Result<Options, OptionError> {
})
.transpose()?;

let swallow_exit_code = swallow_exit_code_raw.is_some_and(|s| s != "true");
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 +244,8 @@ pub(crate) fn options() -> Result<Options, OptionError> {
stdout_file,
stderr_file,
output_file,
forward_exit_code: !swallow_exit_code,
exit_code_file,
rustc_quit_on_rmeta,
rustc_output_format,
})
Expand Down