From 13401947c6e26228603383a7bd28e1f931f23da9 Mon Sep 17 00:00:00 2001 From: Ryo Onodera Date: Sun, 14 Jul 2024 16:25:28 +0900 Subject: [PATCH] Add --partition M/N to distribute builds --- README.md | 3 +++ src/cli.rs | 10 +++++++++- src/main.rs | 39 +++++++++++++++++++++++++++++++++++++-- tests/long-help.txt | 3 +++ tests/short-help.txt | 2 ++ 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b9d7ca2..752b314 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,9 @@ OPTIONS: --keep-going Keep going on failure. + --partition + Partition runs and execute only its subset according to M/N. + --log-group Log grouping: none, github-actions. diff --git a/src/cli.rs b/src/cli.rs index f6933e3..3df1c97 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -13,7 +13,7 @@ use lexopt::{ ValueExt, }; -use crate::{term, version::VersionRange, Feature, LogGroup, Rustup}; +use crate::{term, version::VersionRange, Feature, LogGroup, Partition, Rustup}; pub(crate) struct Args { pub(crate) leading_args: Vec, @@ -53,6 +53,8 @@ pub(crate) struct Args { pub(crate) clean_per_version: bool, /// --keep-going pub(crate) keep_going: bool, + /// --partition + pub(crate) partition: Option, /// --print-command-list pub(crate) print_command_list: bool, /// --version-range/--rust-version @@ -155,6 +157,7 @@ impl Args { let mut clean_per_run = false; let mut clean_per_version = false; let mut keep_going = false; + let mut partition = None; let mut print_command_list = false; let mut no_manifest_path = false; let mut locked = false; @@ -308,6 +311,7 @@ impl Args { Long("clean-per-run") => parse_flag!(clean_per_run), Long("clean-per-version") => parse_flag!(clean_per_version), Long("keep-going") => parse_flag!(keep_going), + Long("partition") => parse_opt!(partition, false), Long("print-command-list") => parse_flag!(print_command_list), Long("no-manifest-path") => parse_flag!(no_manifest_path), Long("locked") => parse_flag!(locked), @@ -571,6 +575,8 @@ impl Args { None => LogGroup::auto(), }; + let partition = partition.as_deref().map(str::parse).transpose()?; + if no_dev_deps || no_private { let flag = if no_dev_deps && no_private { "--no-dev-deps and --no-private modify" @@ -620,6 +626,7 @@ impl Args { clean_per_run, clean_per_version, keep_going, + partition, print_command_list, no_manifest_path, include_features: include_features.into_iter().map(Into::into).collect(), @@ -813,6 +820,7 @@ const HELP: &[HelpText<'_>] = &[ "This flag can only be used together with --version-range flag.", ]), ("", "--keep-going", "", "Keep going on failure", &[]), + ("", "--partition", "", "Partition runs and execute only its subset according to M/N", &[]), ("", "--log-group", "", "Log grouping: none, github-actions", &[ "If this option is not used, the environment will be automatically detected." ]), diff --git a/src/main.rs b/src/main.rs index 46eded9..0ddf249 100644 --- a/src/main.rs +++ b/src/main.rs @@ -639,6 +639,22 @@ impl FromStr for LogGroup { } } +pub(crate) struct Partition { + index: usize, + count: usize, +} + +impl FromStr for Partition { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + match s.split('/').map(str::parse::).collect::>()[..] { + [Ok(index), Ok(count)] if 0 < index && index <= count => Ok(Self { index, count }), + _ => bail!("bad or out-of-range partition: {s}"), + } + } +} + fn exec_cargo( cx: &Context, id: &PackageId, @@ -672,7 +688,26 @@ fn exec_cargo_inner( if progress.count != 0 && !cx.print_command_list && cx.log_group == LogGroup::None { eprintln!(); } - progress.count += 1; + + let new_count = progress.count + 1; + let mut skip = false; + if let Some(partition) = &cx.partition { + if progress.count % partition.count != partition.index - 1 { + let mut msg = String::new(); + if term::verbose() { + write!(msg, "skipping {line}").unwrap(); + } else { + write!(msg, "skipping {line} on {}", cx.packages(id).name).unwrap(); + } + write!(msg, " ({}/{})", new_count, progress.total).unwrap(); + let _guard = cx.log_group.print(&msg); + skip = true; + } + } + progress.count = new_count; + if skip { + return Ok(()); + } if cx.clean_per_run { cargo_clean(cx, Some(id))?; @@ -690,7 +725,7 @@ fn exec_cargo_inner( } else { write!(msg, "running {line} on {}", cx.packages(id).name).unwrap(); } - write!(msg, " ({}/{})", progress.count, progress.total).unwrap(); + write!(msg, " ({}/{})", new_count, progress.total).unwrap(); let _guard = cx.log_group.print(&msg); line.run() diff --git a/tests/long-help.txt b/tests/long-help.txt index 5435036..c7b07fa 100644 --- a/tests/long-help.txt +++ b/tests/long-help.txt @@ -186,6 +186,9 @@ OPTIONS: --keep-going Keep going on failure. + --partition + Partition runs and execute only its subset according to M/N. + --log-group Log grouping: none, github-actions. diff --git a/tests/short-help.txt b/tests/short-help.txt index a06b7c6..5185a78 100644 --- a/tests/short-help.txt +++ b/tests/short-help.txt @@ -46,6 +46,8 @@ OPTIONS: command --clean-per-version Remove artifacts per Rust version --keep-going Keep going on failure + --partition Partition runs and execute only its subset according to + M/N --log-group Log grouping: none, github-actions --print-command-list Print commands without run (Unstable) --no-manifest-path Do not pass --manifest-path option to cargo (Unstable)