support redirecting subprocesses' stdout/stderr to print!() so it gets captured for rust tests

This commit is contained in:
Jacob Lifshay 2024-09-25 01:36:15 -07:00
parent f32c0a7863
commit efc3a539ed
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
6 changed files with 116 additions and 24 deletions

View file

@ -21,6 +21,7 @@ fayalite-proc-macros.workspace = true
hashbrown.workspace = true
num-bigint.workspace = true
num-traits.workspace = true
os_pipe.workspace = true
serde_json.workspace = true
serde.workspace = true
which.workspace = true

View file

@ -5,6 +5,7 @@ use crate::{
firrtl,
intern::Interned,
module::Module,
util::streaming_read_utf8::streaming_read_utf8,
};
use clap::{
builder::{OsStringValueParser, TypedValueParser},
@ -51,6 +52,8 @@ pub struct BaseArgs {
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
#[arg(long)]
pub file_stem: Option<String>,
#[arg(skip = false)]
pub redirect_output_for_rust_test: bool,
}
impl BaseArgs {
@ -60,6 +63,34 @@ impl BaseArgs {
top_fir_file_stem: self.file_stem.clone(),
}
}
/// handles possibly redirecting the command's output for Rust tests
pub fn run_external_command(
&self,
mut command: process::Command,
) -> io::Result<process::ExitStatus> {
if self.redirect_output_for_rust_test {
let (reader, writer) = os_pipe::pipe()?;
let mut reader = io::BufReader::new(reader);
command.stderr(writer.try_clone()?);
command.stdout(writer); // must not leave writer around after spawning child
command.stdin(process::Stdio::null());
let mut child = command.spawn()?;
drop(command); // close writers
Ok(loop {
let status = child.try_wait()?;
streaming_read_utf8(&mut reader, |s| {
// use print! so output goes to Rust test output capture
print!("{s}");
io::Result::Ok(())
})?;
if let Some(status) = status {
break status;
}
})
} else {
command.status()
}
}
}
#[derive(Parser, Debug, Clone)]
@ -188,7 +219,7 @@ impl VerilogArgs {
}
cmd.args(&self.firtool_extra_args);
cmd.current_dir(&self.firrtl.base.output);
let status = cmd.status()?;
let status = self.firrtl.base.run_external_command(cmd)?;
if status.success() {
Ok(output)
} else {

View file

@ -6,6 +6,7 @@ mod const_cmp;
mod const_usize;
mod misc;
mod scoped_ref;
pub(crate) mod streaming_read_utf8;
#[doc(inline)]
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};

View file

@ -0,0 +1,31 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{
io::{self, BufRead},
str,
};
pub(crate) fn streaming_read_utf8<R: BufRead, E: From<io::Error>>(
reader: R,
mut callback: impl FnMut(&str) -> Result<(), E>,
) -> Result<(), E> {
let mut buf = [0; 4];
let mut buf_len = 0;
for byte in reader.bytes() {
buf[buf_len] = byte?;
buf_len += 1;
match str::from_utf8(&buf[..buf_len]) {
Ok(buf) => {
callback(buf)?;
buf_len = 0;
}
Err(e) => {
if e.error_len().is_some() {
callback("\u{FFFD}")?; // replacement character
buf_len = 0;
}
}
}
}
Ok(())
}