diff --git a/src/bin/repo.rs b/src/bin/repo.rs index 8fe2700e..5553f690 100644 --- a/src/bin/repo.rs +++ b/src/bin/repo.rs @@ -63,6 +63,7 @@ const REPO_HELP_STR: &str = r#" cook env and their defaults: CI= set to any value to disable TUI + COOKBOOK_LOGS= whether to capture build logs (default is !CI) COOKBOOK_OFFLINE=false prevent internet access if possible COOKBOOK_NONSTOP=false pkeep running even a recipe build failed COOKBOOK_VERBOSE=true print success/error on each recipe @@ -144,7 +145,7 @@ impl CliConfig { cookbook_dir: current_dir.join("recipes"), repo_dir: current_dir.join("repo"), // build dir here is hardcoded in repo_builder as well - logs_dir: if get_config().cook.tui_logs { + logs_dir: if get_config().cook.logs { Some(current_dir.join("build/logs")) } else { None @@ -212,17 +213,15 @@ fn main_inner() -> anyhow::Result<()> { for recipe in &recipe_names { match repo_inner(&config, &command, recipe) { Ok(_) => { - if verbose { - eprintln!( - "{}{}{} {} - successful{}{}", - style::Bold, - color::Fg(color::AnsiValue(46)), - command.to_string(), - recipe.name.as_str(), - color::Fg(color::Reset), - style::Reset, - ); - } + eprintln!( + "{}{}{} {} - successful{}{}", + style::Bold, + color::Fg(color::AnsiValue(46)), + command.to_string(), + recipe.name.as_str(), + color::Fg(color::Reset), + style::Reset, + ); } Err(e) => { if config.cook.nonstop && verbose { @@ -264,12 +263,48 @@ fn repo_inner( recipe: &CookRecipe, ) -> Result<(), anyhow::Error> { Ok(match *command { - CliCommand::Fetch => { - handle_fetch(recipe, config, &None)?; - } - CliCommand::Cook => { - let source_dir = handle_fetch(recipe, config, &None)?; - handle_cook(recipe, config, source_dir, recipe.is_deps, &None)? + CliCommand::Fetch | CliCommand::Cook => { + let repo_inner_fn = move |logger: &PtyOut| -> Result<(), anyhow::Error> { + let source_dir = handle_fetch(recipe, config, logger)?; + if *command == CliCommand::Cook { + handle_cook(recipe, config, source_dir, recipe.is_deps, logger)?; + } + Ok(()) + }; + let Some(log_path) = &config.logs_dir else { + return repo_inner_fn(&None); + }; + + let (status_tx, status_rx) = mpsc::channel::(); + let (mut stdout_writer, mut stderr_writer) = setup_logger(&status_tx, &recipe.name); + let mut app = TuiApp::new(vec![recipe.clone()]); + app.dump_logs_anyway = true; + let th = thread::spawn(move || { + while let Ok(update) = status_rx.recv() { + let mut should_break = false; + if let StatusUpdate::FlushLog(_p, _q) = &update { + should_break = true; + } + app.update_status(update); + if should_break { + break; + } + } + }); + let mut logger = Some((&mut stdout_writer, &mut stderr_writer)); + let result = repo_inner_fn(&logger); + if let Err(err_ctx) = &result { + log_to_pty!(&logger, "\n{:?}", err_ctx) + } + // successful fetch is not that useful to log + if *command == CliCommand::Cook || result.is_err() { + flush_pty(&mut logger); + let log_path = log_path.join(format!("{}.log", recipe.name.as_str())); + status_tx + .send(StatusUpdate::FlushLog(recipe.name.clone(), log_path)) + .unwrap_or_default(); + } + let _ = th.join(); } CliCommand::Unfetch => handle_clean(recipe, config, true, true)?, CliCommand::Clean => handle_clean(recipe, config, false, true)?, @@ -598,7 +633,7 @@ enum RecipeStatus { Failed(String), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] enum StatusUpdate { StartFetch(PackageName), Fetched(CookRecipe), @@ -650,6 +685,7 @@ struct TuiApp { cook_panel_rect: Option, log_panel_rect: Option, prompt: Option, + dump_logs_anyway: bool, dump_logs_on_exit: Option<(PackageName, String)>, } @@ -681,6 +717,7 @@ impl TuiApp { cook_panel_rect: None, log_panel_rect: None, prompt: None, + dump_logs_anyway: false, dump_logs_on_exit: None, } } @@ -759,6 +796,9 @@ impl TuiApp { StatusUpdate::PushLog(name, chunk) => { let buffer = self.log_byte_buffer.entry(name.clone()).or_default(); buffer.extend_from_slice(&chunk); + if self.dump_logs_anyway { + let _ = std::io::stdout().write_all(&chunk); + } let log_list = self.logs.entry(name.clone()).or_default(); while let Some(newline_pos) = buffer.iter().position(|&b| b == b'\n') { let line_bytes = buffer.drain(..=newline_pos).collect::>(); @@ -842,12 +882,12 @@ fn run_tui_cook( 'done: for (recipe, source_dir) in work_rx { let name = recipe.name.clone(); let is_deps = recipe.is_deps; - cooker_status_tx - .send(StatusUpdate::StartCook(name.clone())) - .unwrap(); let (mut stdout_writer, mut stderr_writer) = setup_logger(&cooker_status_tx, &name); let mut logger = Some((&mut stdout_writer, &mut stderr_writer)); 'again: loop { + cooker_status_tx + .send(StatusUpdate::StartCook(name.clone())) + .unwrap(); let handler = handle_cook( &recipe, &cooker_config, @@ -937,14 +977,12 @@ fn run_tui_cook( let fetcher_handle = thread::spawn(move || { 'done: for recipe in fetcher_recipes { let name = recipe.name.clone(); - fetcher_status_tx - .send(StatusUpdate::StartFetch(name.clone())) - .unwrap(); - let (mut stdout_writer, mut stderr_writer) = setup_logger(&fetcher_status_tx, &name); let mut logger = Some((&mut stdout_writer, &mut stderr_writer)); - 'again: loop { + fetcher_status_tx + .send(StatusUpdate::StartFetch(name.clone())) + .unwrap(); let handler = handle_fetch(&recipe, &fetcher_config, &logger); if let Some(log_path) = fetcher_config.logs_dir.as_ref() // successful fetch log usually not that helpful diff --git a/src/config.rs b/src/config.rs index f014092e..c8836c6d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,12 +12,11 @@ pub struct CookConfigOpt { /// whether to use TUI to allow parallel build /// default value is yes if "CI" env unset and STDIN is open. pub tui: Option, - /// whether to write logs to build/logs dir - /// only usable when tui is used - pub tui_logs: Option, + /// whether to write logs to build/logs dir, default true on TUI + pub logs: Option, /// whether to ignore build errors pub nonstop: Option, - /// whether to print success recipes info and warnings + /// whether to print verbose logs to certain commands /// build failure still be printed anyway pub verbose: Option, } @@ -27,7 +26,7 @@ pub struct CookConfig { pub offline: bool, pub jobs: usize, pub tui: bool, - pub tui_logs: bool, + pub logs: bool, pub nonstop: bool, pub verbose: bool, } @@ -38,7 +37,7 @@ impl From for CookConfig { offline: value.offline.unwrap(), jobs: value.jobs.unwrap(), tui: value.tui.unwrap(), - tui_logs: value.tui_logs.unwrap(), + logs: value.logs.unwrap(), nonstop: value.nonstop.unwrap(), verbose: value.verbose.unwrap(), } @@ -80,8 +79,8 @@ pub fn init_config() { .unwrap_or(1), )); } - if config.cook_opt.tui_logs.is_none() { - config.cook_opt.tui_logs = config.cook_opt.tui; + if config.cook_opt.logs.is_none() { + config.cook_opt.logs = Some(extract_env("COOKBOOK_LOGS", config.cook_opt.tui.unwrap())); } if config.cook_opt.offline.is_none() { config.cook_opt.offline = Some(extract_env("COOKBOOK_OFFLINE", false)); diff --git a/src/cook/cook_build.rs b/src/cook/cook_build.rs index 27827a21..603e49f9 100644 --- a/src/cook/cook_build.rs +++ b/src/cook/cook_build.rs @@ -29,6 +29,7 @@ fn auto_deps_from_dynamic_linking( ) -> BTreeSet { let mut paths = BTreeSet::new(); let mut visited = BTreeSet::new(); + let verbose = crate::config::get_config().cook.verbose; // Base directories may need to be updated for packages that place binaries in odd locations. let mut walk = VecDeque::from([ stage_dir.join("libexec"), @@ -95,7 +96,9 @@ fn auto_deps_from_dynamic_linking( continue; }; if let Ok(relative_path) = path.strip_prefix(stage_dir) { - log_to_pty!(logger, "DEBUG: {} needs {}", relative_path.display(), name); + if verbose { + log_to_pty!(logger, "DEBUG: {} needs {}", relative_path.display(), name); + } } needed.insert(name.to_string()); } @@ -129,7 +132,9 @@ fn auto_deps_from_dynamic_linking( continue; }; if needed.contains(child_name) { - log_to_pty!(logger, "DEBUG: {} provides {}", dep, child_name); + if verbose { + log_to_pty!(logger, "DEBUG: {} provides {}", dep, child_name); + } deps.insert(dep.clone()); missing.remove(child_name); } @@ -138,8 +143,10 @@ fn auto_deps_from_dynamic_linking( } } - for name in missing { - log_to_pty!(logger, "WARN: {} missing", name); + if verbose { + for name in missing { + log_to_pty!(logger, "INFO: {} missing", name); + } } deps @@ -171,6 +178,8 @@ pub fn build( ) -> Result<(PathBuf, BTreeSet), String> { let sysroot_dir = target_dir.join("sysroot"); let stage_dir = target_dir.join("stage"); + let cli_verbose = crate::config::get_config().cook.verbose; + let cli_jobs = crate::config::get_config().cook.jobs; if recipe.build.kind == BuildKind::None { // metapackages don't need to do anything here return Ok((stage_dir, BTreeSet::new())); @@ -207,12 +216,7 @@ pub fn build( if sysroot_dir.is_dir() { let sysroot_modified = modified_dir(&sysroot_dir)?; if sysroot_modified < source_modified || sysroot_modified < deps_modified { - log_to_pty!( - logger, - "DEBUG: '{}' newer than '{}'", - source_dir.display(), - sysroot_dir.display() - ); + log_to_pty!(logger, "DEBUG: updating '{}'", sysroot_dir.display()); remove_all(&sysroot_dir)?; } } @@ -257,12 +261,7 @@ pub fn build( if stage_dir.is_dir() { let stage_modified = modified_dir(&stage_dir)?; if stage_modified < source_modified || stage_modified < deps_modified { - log_to_pty!( - logger, - "DEBUG: '{}' newer than '{}'", - source_dir.display(), - stage_dir.display() - ); + log_to_pty!(logger, "DEBUG: updating '{}'", stage_dir.display()); remove_all(&stage_dir)?; } } @@ -328,10 +327,10 @@ pub fn build( let cookbook_stage = stage_dir_tmp.canonicalize().unwrap(); let cookbook_source = source_dir.canonicalize().unwrap(); let cookbook_sysroot = sysroot_dir.canonicalize().unwrap(); - + let bash_args = if cli_verbose { "-ex" } else { "-e" }; let mut command = if is_redox() { let mut command = Command::new("bash"); - command.arg("-ex"); + command.arg(bash_args); command.env("COOKBOOK_REDOXER", "cargo"); command } else { @@ -339,7 +338,7 @@ pub fn build( .canonicalize() .unwrap_or(PathBuf::from("/bin/false")); let mut command = Command::new(&cookbook_redoxer); - command.arg("env").arg("bash").arg("-ex"); + command.arg("env").arg("bash").arg(bash_args); command.env("COOKBOOK_REDOXER", &cookbook_redoxer); command }; @@ -351,6 +350,10 @@ pub fn build( command.env("COOKBOOK_STAGE", &cookbook_stage); command.env("COOKBOOK_SOURCE", &cookbook_source); command.env("COOKBOOK_SYSROOT", &cookbook_sysroot); + command.env("COOKBOOK_MAKE_JOBS", cli_jobs.to_string()); + if cli_verbose { + command.env("COOKBOOK_VERBOSE", "1"); + } if offline_mode { command.env("COOKBOOK_OFFLINE", "1"); } diff --git a/src/cook/fetch.rs b/src/cook/fetch.rs index b15a3554..021ea71c 100644 --- a/src/cook/fetch.rs +++ b/src/cook/fetch.rs @@ -364,11 +364,14 @@ pub(crate) fn fetch_extract_tar( logger: &PtyOut, ) -> Result<(), String> { let mut command = Command::new("tar"); + let verbose = crate::config::get_config().cook.verbose; if is_redox() { - command.arg("xvf"); + command.arg(if verbose { "xvf" } else { "xf" }); } else { command.arg("--extract"); - command.arg("--verbose"); + if verbose { + command.arg("--verbose"); + } command.arg("--file"); } command.arg(&source_tar); diff --git a/src/cook/package.rs b/src/cook/package.rs index 058884f5..f65ac2e5 100644 --- a/src/cook/package.rs +++ b/src/cook/package.rs @@ -44,12 +44,7 @@ pub fn package( if package_file.is_file() { let stage_modified = modified_dir(stage_dir)?; if modified(&package_file)? < stage_modified { - log_to_pty!( - logger, - "DEBUG: '{}' newer than '{}'", - stage_dir.display(), - package_file.display() - ); + log_to_pty!(logger, "DEBUG: updating '{}'", package_file.display()); remove_all(&package_file)?; remove_all(&package_meta)?; } diff --git a/src/cook/script.rs b/src/cook/script.rs index a9e97d04..8f973036 100644 --- a/src/cook/script.rs +++ b/src/cook/script.rs @@ -11,11 +11,11 @@ function DYNAMIC_INIT { if [ "${TARGET}" != "x86_64-unknown-redox" ] then - echo "WARN: ${TARGET} does not support dynamic linking." >&2 + [ -z "${COOKBOOK_VERBOSE}" ] || echo "WARN: ${TARGET} does not support dynamic linking." >&2 return fi - echo "DEBUG: Program is being compiled dynamically." + [ -z "${COOKBOOK_VERBOSE}" ] || echo "DEBUG: Program is being compiled dynamically." COOKBOOK_CONFIGURE_FLAGS=( --host="${GNU_TARGET}"