diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl index 318c05af8d..773ccfa4c9 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("--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: diff --git a/util/process_wrapper/main.rs b/util/process_wrapper/main.rs index 39a6d6db16..8abfa0c494 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; @@ -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)] diff --git a/util/process_wrapper/options.rs b/util/process_wrapper/options.rs index 2f252cadc7..2a1c3a99a2 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 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, // 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 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(); @@ -102,6 +108,16 @@ pub(crate) fn options() -> Result { "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.", @@ -179,6 +195,7 @@ pub(crate) fn options() -> Result { }) .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() { @@ -227,6 +244,8 @@ pub(crate) fn options() -> Result { stdout_file, stderr_file, output_file, + forward_exit_code: !swallow_exit_code, + exit_code_file, rustc_quit_on_rmeta, rustc_output_format, })