From 59f438b9236aa912576c319e27cbcb19ef76feeb Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 21 Jan 2022 13:54:20 +0900 Subject: [PATCH] Adjust optional dependencies handling to fix breakage on nightly-2022-01-20+ --- CHANGELOG.md | 2 ++ src/features.rs | 35 +++++++++++++++++++---------------- src/main.rs | 33 ++++++++++++++++++++------------- tests/test.rs | 15 ++++++++++++--- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4595216..ddfcf749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +- Fix breakage on nightly-2022-01-20 or later. ([#146](https://github.com/taiki-e/cargo-hack/pull/146)) + ## [0.5.10] - 2022-01-05 - Fix handling of combined short flags. ([#143](https://github.com/taiki-e/cargo-hack/pull/143)) diff --git a/src/features.rs b/src/features.rs index 98ab0fab..9243a3f3 100644 --- a/src/features.rs +++ b/src/features.rs @@ -5,10 +5,11 @@ use std::{ use crate::{metadata::Metadata, PackageId}; +#[derive(Debug)] pub(crate) struct Features { features: Vec, - /// [package features len, package features + optional deps len] - len: [usize; 2], + optional_deps_start: usize, + deps_features_start: usize, } impl Features { @@ -17,14 +18,19 @@ impl Features { let node = &metadata.resolve.nodes[id]; let mut features = Vec::with_capacity(package.features.len()); + let mut optional_deps = vec![]; - for name in package.features.keys() { - features.push(name.into()); - } for name in package.optional_deps() { - features.push(name.into()); + optional_deps.push(name); + } + for name in package.features.keys() { + if !optional_deps.contains(&&**name) { + features.push(name.into()); + } } - let len = [package.features.len(), features.len()]; + let optional_deps_start = features.len(); + features.extend(optional_deps.into_iter().map(Into::into)); + let deps_features_start = features.len(); // TODO: Unpublished dependencies are not included in `node.deps`. for dep in node.deps.iter().filter(|dep| { @@ -42,19 +48,19 @@ impl Features { // TODO: Optional deps of `dep_package`. } - Self { features, len } + Self { features, optional_deps_start, deps_features_start } } pub(crate) fn normal(&self) -> &[Feature] { - &self.features[..self.len[0]] + &self.features[..self.optional_deps_start] } pub(crate) fn optional_deps(&self) -> &[Feature] { - &self.features[self.len[0]..self.len[1]] + &self.features[self.optional_deps_start..self.deps_features_start] } pub(crate) fn deps_features(&self) -> &[Feature] { - &self.features[self.len[1]..] + &self.features[self.deps_features_start..] } pub(crate) fn contains(&self, name: &str) -> bool { @@ -66,13 +72,11 @@ impl Features { #[derive(Debug)] pub(crate) enum Feature { /// A feature of the current crate. - #[allow(dead_code)] // false positive that fixed in Rust 1.44 Normal { /// Feature name. It is considered indivisible. name: String, }, /// Grouped features. - #[allow(dead_code)] // false positive that fixed in Rust 1.44 Group { /// Feature name concatenated with `,`. name: String, @@ -80,12 +84,11 @@ pub(crate) enum Feature { list: Vec, }, /// A feature of a dependency. - #[allow(dead_code)] // false positive that fixed in Rust 1.44 Path { /// Feature path separated with `/`. name: String, /// Index of `/`. - slash: usize, + _slash: usize, }, } @@ -96,7 +99,7 @@ impl Feature { } pub(crate) fn path(parent: &str, name: &str) -> Self { - Self::Path { name: format!("{}/{}", parent, name), slash: parent.len() } + Self::Path { name: format!("{}/{}", parent, name), _slash: parent.len() } } pub(crate) fn name(&self) -> &str { diff --git a/src/main.rs b/src/main.rs index a2c0a15a..a572c522 100644 --- a/src/main.rs +++ b/src/main.rs @@ -145,27 +145,26 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress) } let package = cx.packages(id); + let pkg_features = cx.pkg_features(id); let filter = |&f: &&Feature| { !cx.exclude_features.iter().any(|s| f == s) && !cx.group_features.iter().any(|g| g.matches(f.name())) }; let features = if cx.include_features.is_empty() { - let feature_list = cx.pkg_features(id); - cx.exclude_features.iter().for_each(|d| { - if !feature_list.contains(d) { + if !pkg_features.contains(d) { warn!("specified feature `{}` not found in package `{}`", d, package.name); } }); - let mut features: Vec<_> = feature_list.normal().iter().filter(filter).collect(); + let mut features: Vec<_> = pkg_features.normal().iter().filter(filter).collect(); if let Some(opt_deps) = &cx.optional_deps { if opt_deps.len() == 1 && opt_deps[0].is_empty() { // --optional-deps= } else { for d in opt_deps { - if !feature_list.optional_deps().iter().any(|f| f == d) { + if !pkg_features.optional_deps().iter().any(|f| f == d) { warn!( "specified optional dependency `{}` not found in package `{}`", d, package.name @@ -174,13 +173,13 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress) } } - features.extend(feature_list.optional_deps().iter().filter(|f| { + features.extend(pkg_features.optional_deps().iter().filter(|f| { filter(f) && (opt_deps.is_empty() || opt_deps.iter().any(|x| *f == x)) })); } if cx.include_deps_features { - features.extend(feature_list.deps_features().iter().filter(filter)); + features.extend(pkg_features.deps_features().iter().filter(filter)); } if !cx.group_features.is_empty() { @@ -193,21 +192,27 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress) }; if cx.each_feature { - if (package.features.is_empty() || !cx.include_features.is_empty()) && features.is_empty() { + if (pkg_features.normal().is_empty() && pkg_features.optional_deps().is_empty() + || !cx.include_features.is_empty()) + && features.is_empty() + { progress.total += 1; Kind::Normal } else { progress.total += features.len() + !cx.exclude_no_default_features as usize + (!cx.exclude_all_features - && package.features.len() + package.optional_deps().count() > 1) + && pkg_features.normal().len() + pkg_features.optional_deps().len() > 1) as usize; Kind::Each { features } } } else if cx.feature_powerset { let features = features::feature_powerset(features, cx.depth, &package.features); - if (package.features.is_empty() || !cx.include_features.is_empty()) && features.is_empty() { + if (pkg_features.normal().is_empty() && pkg_features.optional_deps().is_empty() + || !cx.include_features.is_empty()) + && features.is_empty() + { progress.total += 1; Kind::Normal } else { @@ -215,7 +220,7 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress) progress.total += features.len() - 1 + !cx.exclude_no_default_features as usize + (!cx.exclude_all_features - && package.features.len() + package.optional_deps().count() > 1) + && pkg_features.normal().len() + pkg_features.optional_deps().len() > 1) as usize; Kind::Powerset { features } } @@ -350,8 +355,10 @@ fn exec_actual( _ => unreachable!(), } - let pkg = cx.packages(id); - if !cx.exclude_all_features && pkg.features.len() + pkg.optional_deps().count() > 1 { + let pkg_features = cx.pkg_features(id); + if !cx.exclude_all_features + && pkg_features.normal().len() + pkg_features.optional_deps().len() > 1 + { // run with all features // https://github.com/taiki-e/cargo-hack/issues/42 line.arg("--all-features"); diff --git a/tests/test.rs b/tests/test.rs index 1092c845..83430911 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1083,10 +1083,14 @@ fn optional_deps() { cargo_hack(["check", "--each-feature"]) .assert_success2("optional_deps", Some(31)) - .stderr_contains("running `cargo check` on optional_deps (1/1)") + .stderr_contains( + " + running `cargo check --no-default-features` on optional_deps (1/2) + running `cargo check --no-default-features --all-features` on optional_deps (2/2) + ", + ) .stderr_not_contains( " - --no-default-features --features real --features renemed ", @@ -1127,7 +1131,12 @@ fn optional_deps() { cargo_hack(["check", "--each-feature", "--optional-deps="]) .assert_success2("optional_deps", Some(31)) - .stderr_contains("running `cargo check` on optional_deps (1/1)"); + .stderr_contains( + " + running `cargo check --no-default-features` on optional_deps (1/2) + running `cargo check --no-default-features --all-features` on optional_deps (2/2) + ", + ); } #[test]