From ac0f3016cb79917fc5a26271ccbf0e2bf13a073d Mon Sep 17 00:00:00 2001 From: Borja Lorente Date: Fri, 12 Dec 2025 17:38:31 +0000 Subject: [PATCH 1/3] feat: Allow process_wrapper to capture exit codes instead of forwarding them --- rust/private/clippy.bzl | 11 ++++++++++- util/process_wrapper/main.rs | 21 +++++++++++++++++++-- util/process_wrapper/options.rs | 20 ++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl index 318c05af8d..a9b50b50b7 100644 --- a/rust/private/clippy.bzl +++ b/rust/private/clippy.bzl @@ -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: @@ -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 @@ -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: + 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: diff --git a/util/process_wrapper/main.rs b/util/process_wrapper/main.rs index 39a6d6db16..14971b36c3 100644 --- a/util/process_wrapper/main.rs +++ b/util/process_wrapper/main.rs @@ -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; @@ -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 { @@ -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)] diff --git a/util/process_wrapper/options.rs b/util/process_wrapper/options.rs index 2f252cadc7..b50fa9ff8b 100644 --- a/util/process_wrapper/options.rs +++ b/util/process_wrapper/options.rs @@ -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, + // 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, // If set, it configures rustc to emit an rmeta file and then // quit. pub(crate) rustc_quit_on_rmeta: bool, @@ -64,6 +68,8 @@ pub(crate) fn options() -> Result { 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(); @@ -102,6 +108,16 @@ pub(crate) fn options() -> Result { "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.", @@ -179,6 +195,8 @@ pub(crate) fn options() -> Result { }) .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() { @@ -227,6 +245,8 @@ pub(crate) fn options() -> Result { stdout_file, stderr_file, output_file, + forward_exit_code, + exit_code_file, rustc_quit_on_rmeta, rustc_output_format, }) From 225f8fce929f134bca4eafa48ebfacdd2678dfac Mon Sep 17 00:00:00 2001 From: Borja Lorente Date: Tue, 16 Dec 2025 08:49:51 +0000 Subject: [PATCH 2/3] Address comments --- rust/private/clippy.bzl | 2 +- util/process_wrapper/options.rs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl index a9b50b50b7..773ccfa4c9 100644 --- a/rust/private/clippy.bzl +++ b/rust/private/clippy.bzl @@ -203,7 +203,7 @@ def rust_clippy_action(ctx, clippy_executable, process_wrapper, crate_info, conf outputs.append(success_marker) if forward_clippy_exit_code == False: - args.process_wrapper_flags.add("--forward-exit-code", "false") + 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) diff --git a/util/process_wrapper/options.rs b/util/process_wrapper/options.rs index b50fa9ff8b..2a1c3a99a2 100644 --- a/util/process_wrapper/options.rs +++ b/util/process_wrapper/options.rs @@ -44,7 +44,7 @@ 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, - // If set, exit with the same code as the child process. + // 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, @@ -68,7 +68,7 @@ pub(crate) fn options() -> Result { let mut stdout_file = None; let mut stderr_file = None; let mut output_file = None; - let mut forward_exit_code_raw = 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; @@ -109,9 +109,9 @@ pub(crate) fn options() -> Result { &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, + "--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", @@ -195,8 +195,7 @@ pub(crate) fn options() -> Result { }) .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 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() { @@ -245,7 +244,7 @@ pub(crate) fn options() -> Result { stdout_file, stderr_file, output_file, - forward_exit_code, + forward_exit_code: !swallow_exit_code, exit_code_file, rustc_quit_on_rmeta, rustc_output_format, From 5f13335a4528921f6a07217ca1b466e2d44ae9f0 Mon Sep 17 00:00:00 2001 From: Borja Lorente Date: Tue, 16 Dec 2025 08:58:35 +0000 Subject: [PATCH 3/3] formatting --- util/process_wrapper/main.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/util/process_wrapper/main.rs b/util/process_wrapper/main.rs index 14971b36c3..8abfa0c494 100644 --- a/util/process_wrapper/main.rs +++ b/util/process_wrapper/main.rs @@ -216,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 { @@ -234,11 +234,12 @@ fn main() -> Result<(), ProcessWrapperError> { .create(true) .truncate(true) .write(true) - .open(&ecf) + .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)))?; + writeln!(writer, "{}", code).map_err(|e| { + ProcessWrapperError(format!("failed to write exit code to file: {}", e)) + })?; } if opts.forward_exit_code {