Skip to content

Commit

Permalink
Implement --randomize-powerset feature
Browse files Browse the repository at this point in the history
Resolves taiki-e#265.
  • Loading branch information
vi committed Jan 3, 2025
1 parent 9017a5f commit c9a6fb6
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ anyhow = "1.0.47"
cargo-config2 = "0.1.13"
ctrlc = { version = "3.4.4", features = ["termination"] }
lexopt = "0.3"
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
same-file = "1.0.1"
serde_json = "1"
termcolor = "1"
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ OPTIONS:
--include-features and there are multiple features, this also includes runs with just
--all-features flag.

--randomize-powerset <seed>
Run feature powerset in random order with the specified seed.

--optional-deps [DEPS]...
Use optional dependencies as features.

Expand Down
13 changes: 13 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ pub(crate) struct Args {
// propagated to cargo (as a part of leading_args)
/// --no-default-features
pub(crate) no_default_features: bool,

/// --randomize-powerset
pub(crate) randomize_powerset: Option<u64>,
}

impl Args {
Expand Down Expand Up @@ -177,6 +180,8 @@ impl Args {
let mut exclude_no_default_features = false;
let mut exclude_all_features = false;

let mut randomize_powerset = None;

let mut group_features: Vec<String> = vec![];
let mut mutually_exclusive_features: Vec<String> = vec![];
let mut depth = None;
Expand Down Expand Up @@ -303,6 +308,7 @@ impl Args {
Long("remove-dev-deps") => parse_flag!(remove_dev_deps),
Long("each-feature") => parse_flag!(each_feature),
Long("feature-powerset") => parse_flag!(feature_powerset),
Long("randomize-powerset") => parse_opt!(randomize_powerset, false),
Long("at-least-one-of") => at_least_one_of.push(parser.value()?.parse()?),
Long("no-private") => parse_flag!(no_private),
Long("ignore-private") => parse_flag!(ignore_private),
Expand Down Expand Up @@ -487,6 +493,9 @@ impl Args {
if each_feature && feature_powerset {
conflicts("--each-feature", "--feature-powerset")?;
}

let randomize_powerset = randomize_powerset.as_deref().map(str::parse::<u64>).transpose()?;

if all_features {
if each_feature {
conflicts("--all-features", "--each-feature")?;
Expand Down Expand Up @@ -644,6 +653,8 @@ impl Args {

no_default_features,
target: target.into_iter().collect(),

randomize_powerset,
})
}
}
Expand Down Expand Up @@ -695,6 +706,8 @@ const HELP: &[HelpText<'_>] = &[
--include-features and there are multiple features, this also includes runs with just \
--all-features flag."
]),
("", "--randomize-powerset", "<seed>", "Run feature powerset in random order with the specified seed", &[
]),
("", "--optional-deps", "[DEPS]...", "Use optional dependencies as features", &[
"If DEPS are not specified, all optional dependencies are considered as features.",
"This flag can only be used together with either --each-feature flag or --feature-powerset \
Expand Down
29 changes: 18 additions & 11 deletions src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,12 @@ pub(crate) fn feature_powerset<'a>(
at_least_one_of: &[Feature],
mutually_exclusive_features: &[Feature],
package_features: &BTreeMap<String, Vec<String>>,
randomize: Option<u64>,
) -> Vec<Vec<&'a Feature>> {
let deps_map = feature_deps(package_features);
let at_least_one_of = at_least_one_of_for_package(at_least_one_of, &deps_map);

powerset(features, depth)
let mut result : Vec<Vec<&'a Feature>> = powerset(features, depth)
.into_iter()
.skip(1) // The first element of a powerset is `[]` so it should be skipped.
.filter(|fs| {
Expand Down Expand Up @@ -249,7 +250,13 @@ pub(crate) fn feature_powerset<'a>(
}
true
})
.collect()
.collect();
if let Some(seed) = randomize {
use rand::SeedableRng;
use rand::seq::SliceRandom;
result.shuffle(&mut rand::rngs::SmallRng::seed_from_u64(seed));
}
result
}

fn feature_deps(map: &BTreeMap<String, Vec<String>>) -> BTreeMap<&str, BTreeSet<&str>> {
Expand Down Expand Up @@ -366,22 +373,22 @@ mod tests {
let map = map![("a", v![]), ("b", v!["a"]), ("c", v!["b"]), ("d", v!["a", "b"])];

let list = v!["a", "b", "c", "d"];
let filtered = feature_powerset(&list, None, &[], &[], &map);
let filtered = feature_powerset(&list, None, &[], &[], &map, None);
assert_eq!(filtered, vec![vec!["a"], vec!["b"], vec!["c"], vec!["d"], vec!["c", "d"]]);

let filtered = feature_powerset(&list, None, &["a".into()], &[], &map);
let filtered = feature_powerset(&list, None, &["a".into()], &[], &map, None);
assert_eq!(filtered, vec![vec!["a"], vec!["b"], vec!["c"], vec!["d"], vec!["c", "d"]]);

let filtered = feature_powerset(&list, None, &["c".into()], &[], &map);
let filtered = feature_powerset(&list, None, &["c".into()], &[], &map, None);
assert_eq!(filtered, vec![vec!["c"], vec!["c", "d"]]);

let filtered = feature_powerset(&list, None, &["a".into(), "c".into()], &[], &map);
let filtered = feature_powerset(&list, None, &["a".into(), "c".into()], &[], &map, None);
assert_eq!(filtered, vec![vec!["c"], vec!["c", "d"]]);

let map = map![("tokio", v![]), ("async-std", v![]), ("a", v![]), ("b", v!["a"])];
let list = v!["a", "b", "tokio", "async-std"];
let mutually_exclusive_features = [Feature::group(["tokio", "async-std"])];
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map);
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map, None);
assert_eq!(filtered, vec![
vec!["a"],
vec!["b"],
Expand All @@ -395,7 +402,7 @@ mod tests {

let mutually_exclusive_features =
[Feature::group(["tokio", "a"]), Feature::group(["tokio", "async-std"])];
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map);
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map, None);
assert_eq!(filtered, vec![
vec!["a"],
vec!["b"],
Expand All @@ -413,7 +420,7 @@ mod tests {
];
let list = v!["a", "b", "tokio", "async-std"];
let mutually_exclusive_features = [Feature::group(["tokio", "async-std"])];
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map);
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map, None);
assert_eq!(filtered, vec![
vec!["a"],
vec!["b"],
Expand All @@ -427,7 +434,7 @@ mod tests {
let map = map![("a", v![]), ("b", v!["a"]), ("c", v![]), ("d", v!["b"])];
let list = v!["a", "b", "c", "d"];
let mutually_exclusive_features = [Feature::group(["a", "c"])];
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map);
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map, None);
assert_eq!(filtered, vec![vec!["a"], vec!["b"], vec!["c"], vec!["d"]]);
}

Expand Down Expand Up @@ -461,7 +468,7 @@ mod tests {
vec!["b", "c", "d"],
vec!["a", "b", "c", "d"],
]);
let filtered = feature_powerset(&list, None, &[], &[], &map);
let filtered = feature_powerset(&list, None, &[], &[], &map, None);
assert_eq!(filtered, vec![vec!["a"], vec!["b"], vec!["c"], vec!["d"], vec!["c", "d"]]);
}

Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ fn determine_kind<'a>(
&cx.at_least_one_of,
&cx.mutually_exclusive_features,
&package.features,
cx.randomize_powerset,
);

if (pkg_features.normal().is_empty() && pkg_features.optional_deps().is_empty()
Expand Down
3 changes: 3 additions & 0 deletions tests/long-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ OPTIONS:
--include-features and there are multiple features, this also includes runs with just
--all-features flag.

--randomize-powerset <seed>
Run feature powerset in random order with the specified seed.

--optional-deps [DEPS]...
Use optional dependencies as features.

Expand Down
2 changes: 2 additions & 0 deletions tests/short-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ OPTIONS:
-F, --features <FEATURES>... Space or comma separated list of features to activate
--each-feature Perform for each feature of the package
--feature-powerset Perform for the feature powerset of the package
--randomize-powerset <seed> Run feature powerset in random order with the specified
seed
--optional-deps [DEPS]... Use optional dependencies as features
--skip <FEATURES>... Alias for --exclude-features
--exclude-features <FEATURES>... Space or comma separated list of features to exclude
Expand Down

0 comments on commit c9a6fb6

Please sign in to comment.