diff --git a/src/cli.rs b/src/cli.rs index 8c90a053..625dd09d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -151,6 +151,16 @@ pub fn clap() -> clap::Command { .help("if multiple tasks targets are available, execute them all") .action(ArgAction::SetTrue), ) + .arg( + Arg::new("keep_going") + .help("keep going until N jobs / tasks fail (0 means infinity)") + .short('k') + .long("keep_going") + .env("LAZE_KEEP_GOING") + .num_args(1) + .default_value("1") + .value_parser(clap::value_parser!(usize)), + ) .arg(jobs()) .next_help_heading("What to build") .arg( diff --git a/src/main.rs b/src/main.rs index 3ef84c06..5ca42220 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,6 +74,7 @@ fn ninja_run( verbose: bool, targets: Option>, jobs: Option, + keep_going: Option, ) -> Result { let mut ninja_cmd = NinjaCmdBuilder::default(); @@ -86,6 +87,10 @@ fn ninja_run( ninja_cmd.jobs(jobs); } + if let Some(keep_going) = keep_going { + ninja_cmd.keep_going(keep_going); + } + let ninja_cmd = ninja_cmd.build().unwrap(); let ninja_binary = ninja_cmd.binary; let ninja_exit = ninja_cmd @@ -270,6 +275,7 @@ fn try_main() -> Result { }; let jobs = build_matches.get_one::("jobs").copied(); + let keep_going = build_matches.get_one::("keep_going").copied(); let partitioner = build_matches .get_one::("partition") @@ -390,6 +396,8 @@ fn try_main() -> Result { verbose > 0, Some(ninja_targets), jobs, + None, // have to fail on build error b/c no way of knowing *which* target + // failed )? != 0 { return Err(anyhow!("build error")); @@ -401,6 +409,7 @@ fn try_main() -> Result { targets.iter(), args.as_ref(), verbose, + keep_going.unwrap(), project_root.as_std_path(), )?; @@ -440,7 +449,13 @@ fn try_main() -> Result { ) }; - ninja_run(ninja_build_file.as_path(), verbose > 0, targets, jobs)?; + ninja_run( + ninja_build_file.as_path(), + verbose > 0, + targets, + jobs, + keep_going, + )?; } } Some(("clean", clean_matches)) => { @@ -456,7 +471,13 @@ fn try_main() -> Result { false => "clean", }; let clean_target: Option> = Some(vec!["-t".into(), tool.into()]); - ninja_run(ninja_build_file.as_path(), verbose > 0, clean_target, None)?; + ninja_run( + ninja_build_file.as_path(), + verbose > 0, + clean_target, + None, + None, + )?; } _ => {} }; diff --git a/src/ninja/mod.rs b/src/ninja/mod.rs index 8fe0bcc0..0afc0378 100644 --- a/src/ninja/mod.rs +++ b/src/ninja/mod.rs @@ -9,16 +9,13 @@ use std::process::{Command, ExitStatus, Stdio}; use camino::{Utf8Path, Utf8PathBuf}; use indexmap::IndexMap; -#[derive(Debug, PartialEq, Eq, Clone)] -#[derive(Default)] +#[derive(Debug, PartialEq, Eq, Clone, Default)] pub enum NinjaRuleDeps { #[default] None, GCC(String), } - - #[derive(Builder, Debug, PartialEq, Eq, Clone)] #[builder(setter(into))] pub struct NinjaRule<'a> { @@ -317,6 +314,9 @@ pub struct NinjaCmd<'a> { #[builder(default = "None")] jobs: Option, + + #[builder(default = "None")] + keep_going: Option, } impl<'a> NinjaCmd<'a> { @@ -333,6 +333,11 @@ impl<'a> NinjaCmd<'a> { cmd.arg(jobs.to_string()); } + if let Some(keep_going) = self.keep_going { + cmd.arg("-k"); + cmd.arg(keep_going.to_string()); + } + if let Some(targets) = &self.targets { for target in targets { cmd.arg(target); diff --git a/src/task_runner.rs b/src/task_runner.rs index d3ba7fed..223d8be0 100644 --- a/src/task_runner.rs +++ b/src/task_runner.rs @@ -18,6 +18,7 @@ pub fn run_tasks<'a, I>( tasks: I, args: std::option::Option<&Vec<&str>>, verbose: u8, + keep_going: usize, project_root: &Path, ) -> Result<(Vec>, usize), Error> where @@ -33,16 +34,20 @@ where ); let result = task.execute(project_root, args, verbose); - if result.is_err() { - errors += 1; - } + let is_error = result.is_err(); - let result = RunTaskResult { + results.push(RunTaskResult { build, task, result, - }; - results.push(result); + }); + + if is_error { + errors += 1; + if keep_going > 0 && errors >= keep_going { + break; + } + } } Ok((results, errors))