support redirecting subprocesses' stdout/stderr to print!() so it gets captured for rust tests
This commit is contained in:
		
							parent
							
								
									f32c0a7863
								
							
						
					
					
						commit
						42945b1f9c
					
				
					 6 changed files with 114 additions and 24 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										29
									
								
								crates/fayalite/src/util/streaming_read_utf8.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								crates/fayalite/src/util/streaming_read_utf8.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
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(())
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue