diff --git a/src/b.rs b/src/b.rs index afb2e730..1b701e9a 100644 --- a/src/b.rs +++ b/src/b.rs @@ -1134,6 +1134,16 @@ pub unsafe fn include_path_if_exists(input_paths: &mut Array<*const c_char>, pat Some(()) } +static mut QUIET: *mut bool = ptr::null_mut(); + +macro_rules! log_info { + ($($arg:tt)*) => {{ + if !*QUIET { + printf($($arg)*); + } + }}; +} + pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { let default_target; if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") { @@ -1159,6 +1169,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { let linker = flag_list(c!("L"), c!("Append a flag to the linker of the target platform")); let nostdlib = flag_bool(c!("nostdlib"), false, c!("Do not link with standard libraries like libb and/or libc on some platforms")); let ir = flag_bool(c!("ir"), false, c!("Instead of compiling, dump the IR of the program to stdout")); + QUIET = flag_bool(c!("q"), false, c!("Don't do logging")); let historical = flag_bool(c!("hist"), false, c!("Makes the compiler strictly follow the description of the B language from the \"Users' Reference to B\" by Ken Thompson as much as possible")); let mut input_paths: Array<*const c_char> = zeroed(); @@ -1211,6 +1222,11 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { fprintf(stderr(), c!("ERROR: no inputs are provided\n")); return None; } + + if *QUIET { + // Didn't know if errors would be ignored, so nob is still logging errors + MINIMAL_LOG_LEVEL = LogLevel::Error; + } let mut c: Compiler = zeroed(); c.target = target; @@ -1246,13 +1262,13 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { // But I do plan to have similar testing tool written in Crust. // // - rexim (2025-06-12 20:18:02) - printf(c!("INFO: Compiling files ")); + log_info!(c!("INFO: Compiling files ")); for i in 0..input_paths.count { let input_path = *input_paths.items.add(i); - if i > 0 { printf(c!(" ")); } - printf(c!("%s"), input_path); + if i > 0 { log_info!(c!(" ")); } + log_info!(c!("%s"), input_path); } - printf(c!("\n")); + log_info!(c!("\n")); let mut input: String_Builder = zeroed(); @@ -1279,7 +1295,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { if *ir { codegen::ir::generate_program(&mut output, &c); da_append(&mut output, 0); - printf(c!("%s"), output.items); + log_info!(c!("%s"), output.items); return Some(()) } @@ -1300,7 +1316,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { let output_asm_path = temp_sprintf(c!("%s.s"), effective_output_path); if !write_entire_file(output_asm_path, output.items as *const c_void, output.count) { return None; } - printf(c!("INFO: Generated %s\n"), output_asm_path); + log_info!(c!("INFO: Generated %s\n"), output_asm_path); let (gas, cc) = if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") { (c!("as"), c!("cc")) @@ -1315,7 +1331,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { &mut cmd, gas, c!("-o"), output_obj_path, output_asm_path, } - if !cmd_run_sync_and_reset(&mut cmd) { return None; } + if !cmd_with_controlled_output(&mut cmd) { return None; } cmd_append! { &mut cmd, cc, c!("-no-pie"), c!("-o"), effective_output_path, output_obj_path, @@ -1332,7 +1348,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { *(*linker).items.add(i), } } - if !cmd_run_sync_and_reset(&mut cmd) { return None; } + if !cmd_with_controlled_output(&mut cmd) { return None; } if *run { runner::gas_aarch64_linux::run(&mut cmd, effective_output_path, da_slice(run_args))?; } @@ -1353,7 +1369,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { let output_asm_path = temp_sprintf(c!("%s.asm"), effective_output_path); if !write_entire_file(output_asm_path, output.items as *const c_void, output.count) { return None; } - printf(c!("INFO: Generated %s\n"), output_asm_path); + log_info!(c!("INFO: Generated %s\n"), output_asm_path); if !(cfg!(target_arch = "x86_64") && cfg!(target_os = "linux")) { // TODO: think how to approach cross-compilation @@ -1366,7 +1382,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { &mut cmd, c!("fasm"), output_asm_path, output_obj_path, } - if !cmd_run_sync_and_reset(&mut cmd) { return None; } + if !cmd_with_controlled_output(&mut cmd) { return None; } cmd_append! { &mut cmd, c!("cc"), c!("-no-pie"), c!("-o"), effective_output_path, output_obj_path, @@ -1383,7 +1399,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { *(*linker).items.add(i), } } - if !cmd_run_sync_and_reset(&mut cmd) { return None; } + if !cmd_with_controlled_output(&mut cmd) { return None; } if *run { runner::fasm_x86_64_linux::run(&mut cmd, effective_output_path, da_slice(run_args))? } @@ -1410,7 +1426,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { let output_asm_path = temp_sprintf(c!("%s.asm"), base_path); if !write_entire_file(output_asm_path, output.items as *const c_void, output.count) { return None; } - printf(c!("INFO: Generated %s\n"), output_asm_path); + log_info!(c!("INFO: Generated %s\n"), output_asm_path); let cc = if cfg!(target_arch = "x86_64") && cfg!(target_os = "windows") { c!("cc") @@ -1423,7 +1439,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { &mut cmd, c!("fasm"), output_asm_path, output_obj_path, } - if !cmd_run_sync_and_reset(&mut cmd) { return None; } + if !cmd_with_controlled_output(&mut cmd) { return None; } cmd_append! { &mut cmd, cc, c!("-no-pie"), c!("-o"), effective_output_path, output_obj_path, @@ -1440,7 +1456,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { *(*linker).items.add(i), } } - if !cmd_run_sync_and_reset(&mut cmd) { return None; } + if !cmd_with_controlled_output(&mut cmd) { return None; } if *run { runner::fasm_x86_64_windows::run(&mut cmd, effective_output_path, da_slice(run_args))?; } @@ -1458,7 +1474,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { } if !write_entire_file(effective_output_path, output.items as *const c_void, output.count) { return None; } - printf(c!("INFO: Generated %s\n"), effective_output_path); + log_info!(c!("INFO: Generated %s\n"), effective_output_path); if *run { runner::uxn::run(&mut cmd, c!("uxnemu"), effective_output_path, da_slice(run_args))?; } @@ -1477,7 +1493,7 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { } if !write_entire_file(effective_output_path, output.items as *const c_void, output.count) { return None; } - printf(c!("INFO: Generated %s\n"), effective_output_path); + log_info!(c!("INFO: Generated %s\n"), effective_output_path); if *run { runner::mos6502::run(&mut output, config, effective_output_path)?; } diff --git a/src/nob.rs b/src/nob.rs index 2b044343..0327229c 100644 --- a/src/nob.rs +++ b/src/nob.rs @@ -1,5 +1,6 @@ use core::ffi::*; use core::slice; +use core::ptr; use crate::crust::libc; #[repr(C)] @@ -83,9 +84,27 @@ macro_rules! cmd_append { } } +#[repr(C)] +pub struct CmdRedirect { + pub fdin: *mut Fd, + pub fdout: *mut Fd, + pub fderr: *mut Fd, +} + +#[repr(C)] +pub enum LogLevel { + Info, + Warning, + Error, + NoLogs, +} + extern "C" { #[link_name = "nob_cmd_run_sync_and_reset"] pub fn cmd_run_sync_and_reset(cmd: *mut Cmd) -> bool; + + #[link_name = "nob_cmd_run_sync_redirect_and_reset"] + pub fn cmd_run_sync_redirect_and_reset(cmd: *mut Cmd, redirect: CmdRedirect) -> bool; } pub type String_Builder = Array; @@ -98,6 +117,7 @@ pub struct String_View { } pub type File_Paths = Array<*const c_char>; +pub type Fd = c_int; extern "C" { #[link_name = "nob_read_entire_file"] @@ -124,6 +144,30 @@ extern "C" { pub fn mkdir_if_not_exists(path: *const c_char) -> bool; #[link_name = "nob_read_entire_dir"] pub fn read_entire_dir(parent: *const c_char, children: *mut File_Paths) -> bool; + #[link_name = "nob_minimal_log_level"] + pub static mut MINIMAL_LOG_LEVEL: LogLevel; + #[link_name = "nob_fd_open_for_write"] + pub fn fd_open_for_write(path: *const c_char) -> Fd; + #[link_name = "nob_fd_close"] + pub fn fd_close(fd: Fd); +} + +pub unsafe fn cmd_with_controlled_output(cmd: *mut Cmd) -> bool { + if !matches!(MINIMAL_LOG_LEVEL, LogLevel::Error | LogLevel::NoLogs) { + cmd_run_sync_and_reset(cmd) + } else { + // Someone could optimize this to only open /dev/null at the start of the program + // but I'm not sure if the performance increase would be worth it + let mut out_fd = fd_open_for_write("/dev/null".as_ptr() as *const c_char); + let redirect = CmdRedirect { + fdin: ptr::null_mut(), + fdout: &mut out_fd, + fderr: ptr::null_mut(), + }; + let res = cmd_run_sync_redirect_and_reset(cmd, redirect); + fd_close(out_fd); + res + } } // TODO: This is a generally useful function. Consider making it a part of nob.h