Skip to content

Commit

Permalink
Merge #140
Browse files Browse the repository at this point in the history
140: Add --keep-going flag r=taiki-e a=taiki-e

Closes #133 

<img width="1046" alt="keep-going" src="https://user-images.githubusercontent.com/43724913/147629433-3d58a94a-cd7b-4ed1-8932-ef30d2fce46b.png">


Co-authored-by: Taiki Endo <te316e89@gmail.com>
  • Loading branch information
bors[bot] and taiki-e committed Dec 29, 2021
2 parents cbf2bdc + 75e2598 commit 55fd69a
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 57 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com

- Fix an error when using old cargo with a dependency graph containing 2021 edition crates. ([#138](https://github.com/taiki-e/cargo-hack/pull/138))

- Support omitting lower bound of --version-range. ([#139](https://github.com/taiki-e/cargo-hack/pull/139))
- Support omitting lower bound of `--version-range`. ([#139](https://github.com/taiki-e/cargo-hack/pull/139))

- Add `--keep-going` flag. ([#140](https://github.com/taiki-e/cargo-hack/pull/140))

## [0.5.8] - 2021-10-13

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ OPTIONS:

This flag can only be used together with --version-range flag.

--keep-going
Keep going on failure.

-v, --verbose
Use verbose output.

Expand Down
6 changes: 6 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub(crate) struct Args<'a> {
pub(crate) clean_per_run: bool,
/// --clean-per-version
pub(crate) clean_per_version: bool,
/// --keep-going
pub(crate) keep_going: bool,
/// --version-range
pub(crate) version_range: Option<&'a str>,
/// --version-step
Expand Down Expand Up @@ -130,6 +132,7 @@ pub(crate) fn parse_args<'a>(raw: &'a [String], cargo: &OsStr) -> Result<Args<'a
let mut ignore_unknown_features = false;
let mut clean_per_run = false;
let mut clean_per_version = false;
let mut keep_going = false;
let mut version_range = None;
let mut version_step = None;

Expand Down Expand Up @@ -261,6 +264,7 @@ pub(crate) fn parse_args<'a>(raw: &'a [String], cargo: &OsStr) -> Result<Args<'a
"--include-deps-features" => parse_flag!(include_deps_features),
"--clean-per-run" => parse_flag!(clean_per_run),
"--clean-per-version" => parse_flag!(clean_per_version),
"--keep-going" => parse_flag!(keep_going),
"--ignore-unknown-features" => parse_flag!(ignore_unknown_features),
// allow multiple uses
"--verbose" | "-v" | "-vv" => {
Expand Down Expand Up @@ -496,6 +500,7 @@ pub(crate) fn parse_args<'a>(raw: &'a [String], cargo: &OsStr) -> Result<Args<'a
optional_deps,
clean_per_run,
clean_per_version,
keep_going,
include_features: include_features.into_iter().map(Into::into).collect(),
include_deps_features,
version_range,
Expand Down Expand Up @@ -648,6 +653,7 @@ const HELP: &[HelpText<'_>] = &[
"Note that dependencies artifacts will also be removed.",
"This flag can only be used together with --version-range flag.",
]),
("", "--keep-going", "", "Keep going on failure", &[]),
("-v", "--verbose", "", "Use verbose output", &[]),
("", "--color", "<WHEN>", "Coloring: auto, always, never", &[
"This flag will be propagated to cargo.",
Expand Down
7 changes: 2 additions & 5 deletions src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use std::{
slice,
};

use crate::{
metadata::{Dependency, Metadata},
PackageId,
};
use crate::{metadata::Metadata, PackageId};

pub(crate) struct Features {
features: Vec<Feature>,
Expand All @@ -24,7 +21,7 @@ impl Features {
for name in package.features.keys() {
features.push(name.into());
}
for name in package.dependencies.iter().filter_map(Dependency::as_feature) {
for name in package.optional_deps() {
features.push(name.into());
}
let len = [package.features.len(), features.len()];
Expand Down
113 changes: 89 additions & 24 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ mod restore;
mod rustup;
mod version;

use std::fmt::Write;
use std::{
collections::BTreeMap,
fmt::{self, Write},
};

use anyhow::{bail, Result};

Expand Down Expand Up @@ -61,6 +64,7 @@ fn exec_on_workspace(cx: &Context<'_>) -> Result<()> {

let mut progress = Progress::default();
let packages = determine_package_list(cx, &mut progress)?;
let mut keep_going = KeepGoing::default();
if let Some(range) = &cx.version_range {
progress.total *= range.len();
let line = cmd!("cargo");
Expand Down Expand Up @@ -97,17 +101,22 @@ fn exec_on_workspace(cx: &Context<'_>) -> Result<()> {
let mut line = line.clone();
line.leading_arg(toolchain);
line.apply_context(cx);
packages
.iter()
.try_for_each(|(id, kind)| exec_on_package(cx, id, kind, &line, &mut progress))
})
packages.iter().try_for_each(|(id, kind)| {
exec_on_package(cx, id, kind, &line, &mut progress, &mut keep_going)
})
})?;
} else {
let mut line = cx.cargo();
line.apply_context(cx);
packages
.iter()
.try_for_each(|(id, kind)| exec_on_package(cx, id, kind, &line, &mut progress))
packages.iter().try_for_each(|(id, kind)| {
exec_on_package(cx, id, kind, &line, &mut progress, &mut keep_going)
})?;
}
if keep_going.count > 0 {
eprintln!();
error!("{}", keep_going);
}
Ok(())
}

#[derive(Default)]
Expand Down Expand Up @@ -189,7 +198,9 @@ fn determine_kind<'a>(cx: &'a Context<'_>, id: &PackageId, progress: &mut Progre
} else {
progress.total += features.len()
+ !cx.exclude_no_default_features as usize
+ !cx.exclude_all_features as usize;
+ (!cx.exclude_all_features
&& package.features.len() + package.optional_deps().count() > 1)
as usize;
Kind::Each { features }
}
} else if cx.feature_powerset {
Expand All @@ -202,7 +213,9 @@ fn determine_kind<'a>(cx: &'a Context<'_>, id: &PackageId, progress: &mut Progre
// -1: the first element of a powerset is `[]`
progress.total += features.len() - 1
+ !cx.exclude_no_default_features as usize
+ !cx.exclude_all_features as usize;
+ (!cx.exclude_all_features
&& package.features.len() + package.optional_deps().count() > 1)
as usize;
Kind::Powerset { features }
}
} else {
Expand Down Expand Up @@ -259,6 +272,7 @@ fn exec_on_package(
kind: &Kind<'_>,
line: &ProcessBuilder<'_>,
progress: &mut Progress,
keep_going: &mut KeepGoing,
) -> Result<()> {
if let Kind::SkipAsPrivate = kind {
return Ok(());
Expand All @@ -278,11 +292,11 @@ fn exec_on_package(

fs::write(&package.manifest_path, new)?;

exec_actual(cx, id, kind, &mut line, progress)?;
exec_actual(cx, id, kind, &mut line, progress, keep_going)?;

handle.close()
} else {
exec_actual(cx, id, kind, &mut line, progress)
exec_actual(cx, id, kind, &mut line, progress, keep_going)
}
}

Expand All @@ -292,13 +306,14 @@ fn exec_actual(
kind: &Kind<'_>,
line: &mut ProcessBuilder<'_>,
progress: &mut Progress,
keep_going: &mut KeepGoing,
) -> Result<()> {
match kind {
Kind::NoSubcommand => return Ok(()),
Kind::SkipAsPrivate => unreachable!(),
Kind::Normal => {
// only run with default features
return exec_cargo(cx, id, line, progress);
return exec_cargo(cx, id, line, progress, keep_going);
}
Kind::Each { .. } | Kind::Powerset { .. } => {}
}
Expand All @@ -316,29 +331,30 @@ fn exec_actual(

if !cx.exclude_no_default_features {
// run with no default features if the package has other features
exec_cargo(cx, id, &mut line, progress)?;
exec_cargo(cx, id, &mut line, progress, keep_going)?;
}

match kind {
Kind::Each { features } => {
features
.iter()
.try_for_each(|f| exec_cargo_with_features(cx, id, &line, progress, Some(f)))?;
features.iter().try_for_each(|f| {
exec_cargo_with_features(cx, id, &line, progress, keep_going, Some(f))
})?;
}
Kind::Powerset { features } => {
// The first element of a powerset is `[]` so it should be skipped.
features
.iter()
.skip(1)
.try_for_each(|f| exec_cargo_with_features(cx, id, &line, progress, f))?;
features.iter().skip(1).try_for_each(|f| {
exec_cargo_with_features(cx, id, &line, progress, keep_going, f)
})?;
}
_ => unreachable!(),
}

if !cx.exclude_all_features {
let pkg = cx.packages(id);
if !cx.exclude_all_features && pkg.features.len() + pkg.optional_deps().count() > 1 {
// run with all features
// https://github.com/taiki-e/cargo-hack/issues/42
line.arg("--all-features");
exec_cargo(cx, id, &mut line, progress)?;
exec_cargo(cx, id, &mut line, progress, keep_going)?;
}

Ok(())
Expand All @@ -349,19 +365,68 @@ fn exec_cargo_with_features(
id: &PackageId,
line: &ProcessBuilder<'_>,
progress: &mut Progress,
keep_going: &mut KeepGoing,
features: impl IntoIterator<Item = impl AsRef<str>>,
) -> Result<()> {
let mut line = line.clone();
line.append_features(features);
exec_cargo(cx, id, &mut line, progress)
exec_cargo(cx, id, &mut line, progress, keep_going)
}

#[derive(Default)]
struct KeepGoing {
count: u64,
failed_commands: BTreeMap<String, Vec<String>>,
}

impl fmt::Display for KeepGoing {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "failed to run {} commands", self.count)?;
writeln!(f)?;
writeln!(f, "failed commands:")?;
for (pkg, commands) in &self.failed_commands {
writeln!(f, " {}:", pkg)?;
for cmd in commands {
writeln!(f, " {}", cmd)?;
}
}
Ok(())
}
}

fn exec_cargo(
cx: &Context<'_>,
id: &PackageId,
line: &mut ProcessBuilder<'_>,
progress: &mut Progress,
keep_going: &mut KeepGoing,
) -> Result<()> {
let res = exec_cargo_inner(cx, id, line, progress);
if cx.keep_going {
if let Err(e) = res {
error!("{:#}", e);
keep_going.count = keep_going.count.saturating_add(1);
let name = cx.packages(id).name.clone();
if !keep_going.failed_commands.contains_key(&name) {
keep_going.failed_commands.insert(name.clone(), vec![]);
}
keep_going.failed_commands.get_mut(&name).unwrap().push(format!("{:#}", line));
}
Ok(())
} else {
res
}
}

fn exec_cargo_inner(
cx: &Context<'_>,
id: &PackageId,
line: &mut ProcessBuilder<'_>,
progress: &mut Progress,
) -> Result<()> {
if progress.count != 0 {
eprintln!();
}
progress.count += 1;

if cx.clean_per_run {
Expand Down
4 changes: 4 additions & 0 deletions src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@ impl Package {
},
}))
}

pub(crate) fn optional_deps(&self) -> impl Iterator<Item = &str> + '_ {
self.dependencies.iter().filter_map(Dependency::as_feature)
}
}

/// A dependency of the main crate.
Expand Down
Loading

0 comments on commit 55fd69a

Please sign in to comment.