From 0597bc5ff930e0547c3aae6e15f44f8735f2ad8e Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 19:47:32 +1100 Subject: [PATCH 01/11] Refactor child process handling in command_helpers Replace wait_on_child with StderrForwarder so that we can implement reading from stdout and stderr at the same time --- src/command_helpers.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index b93ee26be..ec9b3488c 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -363,9 +363,28 @@ pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Resul .unwrap(); // Don't care about this output, use the normal settings - wait_on_child(cmd, &mut child, cargo_output)?; + StderrForwarder::new(child).forward_all(); + + let status = match child.wait() { + Ok(s) => s, + Err(e) => { + return Err(Error::new( + ErrorKind::ToolExecError, + format!("failed to wait on spawned child process `{cmd:?}`: {e}"), + )); + } + }; + + cargo_output.print_debug(&status); - Ok(stdout) + if status.success() { + Ok(stdout) + } else { + Err(Error::new( + ErrorKind::ToolExecError, + format!("command did not execute successfully (status code {status}): {cmd:?}"), + )) + } } pub(crate) fn spawn(cmd: &mut Command, cargo_output: &CargoOutput) -> Result { From b0ba90923af271bc238a83072c0bb294269b1a60 Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:43:27 +1100 Subject: [PATCH 02/11] Fix run_output to prevent infinite blocking If the child is blocked on writing to stderr and run_output blocked on reading stdout, then it'd be hung forever --- src/command_helpers.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index ec9b3488c..c062b8a2a 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -352,18 +352,13 @@ pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Resul // We specifically need the output to be captured, so override default let mut captured_cargo_output = cargo_output.clone(); captured_cargo_output.output = OutputKind::Capture; - let mut child = spawn(cmd, &captured_cargo_output)?; + let Output { + status, + stdout, + stderr, + } = spawn(cmd, &captured_cargo_output)?.wait_with_output()?; - let mut stdout = vec![]; - child - .stdout - .take() - .unwrap() - .read_to_end(&mut stdout) - .unwrap(); - - // Don't care about this output, use the normal settings - StderrForwarder::new(child).forward_all(); + stderr.split(|&b| b == b'\n').for_each(write_warning); let status = match child.wait() { Ok(s) => s, From b8894454b352af36f81ae3c86bcec7d7e6bf4dec Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:30:10 +1100 Subject: [PATCH 03/11] Fix run_output --- src/command_helpers.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index c062b8a2a..d0fb7eddb 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -356,20 +356,15 @@ pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Resul status, stdout, stderr, - } = spawn(cmd, &captured_cargo_output)?.wait_with_output()?; + } = spawn(cmd, &captured_cargo_output)? + .wait_with_output() + .map_err(|e| Err(Error::new( + ErrorKind::ToolExecError, + format!("failed to wait on spawned child process `{cmd:?}`: {e}"), + )))?; stderr.split(|&b| b == b'\n').for_each(write_warning); - let status = match child.wait() { - Ok(s) => s, - Err(e) => { - return Err(Error::new( - ErrorKind::ToolExecError, - format!("failed to wait on spawned child process `{cmd:?}`: {e}"), - )); - } - }; - cargo_output.print_debug(&status); if status.success() { From b1e1370989c31ba0e65e0a60dbe102fb20e4c65a Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:50:49 +1100 Subject: [PATCH 04/11] Add new fn spawn_and_wait_for_output --- src/command_helpers.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index d0fb7eddb..700a426aa 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -9,7 +9,7 @@ use std::{ hash::Hasher, io::{self, Read, Write}, path::Path, - process::{Child, ChildStderr, Command, Stdio}, + process::{Child, ChildStderr, Command, Output, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -348,20 +348,20 @@ pub(crate) fn run(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<(), E wait_on_child(cmd, &mut child, cargo_output) } -pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result, Error> { +pub(crate) fn spawn_and_wait_for_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result { // We specifically need the output to be captured, so override default let mut captured_cargo_output = cargo_output.clone(); captured_cargo_output.output = OutputKind::Capture; - let Output { - status, - stdout, - stderr, - } = spawn(cmd, &captured_cargo_output)? + spawn(cmd, &captured_cargo_output)? .wait_with_output() .map_err(|e| Err(Error::new( ErrorKind::ToolExecError, format!("failed to wait on spawned child process `{cmd:?}`: {e}"), - )))?; + ))) +} + +pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result, Error> { + let Output { status, stdout, stderr } = spawn_and_wait_for_output(cmd, cargo_output)?; stderr.split(|&b| b == b'\n').for_each(write_warning); From a61dacbdabdc6fdb8d5ac544e11a0c4a4b164f79 Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:51:29 +1100 Subject: [PATCH 05/11] Replace spawn with spawn_and_wait_for_output --- src/tool.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tool.rs b/src/tool.rs index c1d9d1d16..5303019b1 100644 --- a/src/tool.rs +++ b/src/tool.rs @@ -1,5 +1,5 @@ use crate::{ - command_helpers::{run_output, spawn, CargoOutput}, + command_helpers::{run_output, spawn_and_wait_for_output, CargoOutput}, run, tempfile::NamedTempfile, Error, ErrorKind, OutputKind, @@ -221,13 +221,12 @@ impl Tool { // But with clang-cl it can be part of stderr instead and exit with a // non-zero exit code. let mut captured_cargo_output = compiler_detect_output.clone(); - captured_cargo_output.output = OutputKind::Capture; captured_cargo_output.warnings = true; let Output { status, stdout, stderr, - } = spawn(&mut cmd, &captured_cargo_output)?.wait_with_output()?; + } = spawn_and_wait_for_output(&mut cmd, &captured_cargo_output)?.wait_with_output()?; let stdout = if [&stdout, &stderr] .iter() From 4652e518de47a11b330af318f3c91074b5449196 Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:55:07 +1100 Subject: [PATCH 06/11] Fix error handling in command_helpers.rs --- src/command_helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index 700a426aa..63da53e12 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -354,10 +354,10 @@ pub(crate) fn spawn_and_wait_for_output(cmd: &mut Command, cargo_output: &CargoO captured_cargo_output.output = OutputKind::Capture; spawn(cmd, &captured_cargo_output)? .wait_with_output() - .map_err(|e| Err(Error::new( + .map_err(|e| Error::new( ErrorKind::ToolExecError, format!("failed to wait on spawned child process `{cmd:?}`: {e}"), - ))) + )) } pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result, Error> { From e7ad62d4c7600d6f6600fd3ae8d0e1b099d94140 Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:56:23 +1100 Subject: [PATCH 07/11] Fix tool --- src/tool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool.rs b/src/tool.rs index 5303019b1..f3eac13e9 100644 --- a/src/tool.rs +++ b/src/tool.rs @@ -226,7 +226,7 @@ impl Tool { status, stdout, stderr, - } = spawn_and_wait_for_output(&mut cmd, &captured_cargo_output)?.wait_with_output()?; + } = spawn_and_wait_for_output(&mut cmd, &captured_cargo_output)?; let stdout = if [&stdout, &stderr] .iter() From d6eebd3cb8ea5e730954b03f62f4d639cd7fb6c6 Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:58:36 +1100 Subject: [PATCH 08/11] Cargo fmt --- src/command_helpers.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index 63da53e12..dee36fd59 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -361,7 +361,11 @@ pub(crate) fn spawn_and_wait_for_output(cmd: &mut Command, cargo_output: &CargoO } pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result, Error> { - let Output { status, stdout, stderr } = spawn_and_wait_for_output(cmd, cargo_output)?; + let Output { + status, + stdout, + stderr, + } = spawn_and_wait_for_output(cmd, cargo_output)?; stderr.split(|&b| b == b'\n').for_each(write_warning); From cf08d20b607087c675166f4a15f4ae9376f3d8ac Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 23:01:28 +1100 Subject: [PATCH 09/11] Fix run_output stderr handling by filtering empty lines Filter out empty lines from stderr before writing warnings. --- src/command_helpers.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index dee36fd59..d721c0463 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -367,7 +367,10 @@ pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Resul stderr, } = spawn_and_wait_for_output(cmd, cargo_output)?; - stderr.split(|&b| b == b'\n').for_each(write_warning); + stderr + .split(|&b| b == b'\n') + .filter(|part| !part.is_empty()) + .for_each(write_warning); cargo_output.print_debug(&status); From fa64a9d4a3d9b63627b21448030525f0be545653 Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 23:04:13 +1100 Subject: [PATCH 10/11] Cargo fmt --- src/command_helpers.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index d721c0463..6af000a5a 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -354,10 +354,12 @@ pub(crate) fn spawn_and_wait_for_output(cmd: &mut Command, cargo_output: &CargoO captured_cargo_output.output = OutputKind::Capture; spawn(cmd, &captured_cargo_output)? .wait_with_output() - .map_err(|e| Error::new( - ErrorKind::ToolExecError, - format!("failed to wait on spawned child process `{cmd:?}`: {e}"), - )) + .map_err(|e| { + Error::new( + ErrorKind::ToolExecError, + format!("failed to wait on spawned child process `{cmd:?}`: {e}"), + ) + }) } pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result, Error> { From 71df117403b227fb0b14dccdc26e6fb589f6c8b9 Mon Sep 17 00:00:00 2001 From: Jiahao XU <30436523+NobodyXu@users.noreply.github.com> Date: Sat, 6 Dec 2025 23:05:45 +1100 Subject: [PATCH 11/11] Cargo fmt --- src/command_helpers.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/command_helpers.rs b/src/command_helpers.rs index 6af000a5a..3b01115aa 100644 --- a/src/command_helpers.rs +++ b/src/command_helpers.rs @@ -348,7 +348,10 @@ pub(crate) fn run(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<(), E wait_on_child(cmd, &mut child, cargo_output) } -pub(crate) fn spawn_and_wait_for_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result { +pub(crate) fn spawn_and_wait_for_output( + cmd: &mut Command, + cargo_output: &CargoOutput, +) -> Result { // We specifically need the output to be captured, so override default let mut captured_cargo_output = cargo_output.clone(); captured_cargo_output.output = OutputKind::Capture;