Skip to content

Commit 2f88a70

Browse files
committed
Fix optional deps in multiple sections
This commit fixes an issue where an optional dependency was listed multiple times in a manifest (multiple sections). This regression was introduced by #5415 and happened because in the resolver we didn't record a `Dependency` as it was accidentally deduplicated too soon. The fix here was to ensure that all `Dependency` annotations make their way into `Resolve` now that we rely on the listed `Dependency` values for correctness. Closes #5475
1 parent 1ca44da commit 2f88a70

File tree

2 files changed

+86
-12
lines changed

2 files changed

+86
-12
lines changed

src/cargo/core/resolver/context.rs

+21-12
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,23 @@ impl Context {
187187
} else {
188188
vec![]
189189
};
190-
let mut reqs = build_requirements(s, method, &values)?;
190+
let reqs = build_requirements(s, method, &values)?;
191191
let mut ret = Vec::new();
192+
let mut used_features = HashSet::new();
193+
let default_dep = (false, Vec::new());
192194

193195
// Next, collect all actually enabled dependencies and their features.
194196
for dep in deps {
195-
// Skip optional dependencies, but not those enabled through a feature
197+
// Skip optional dependencies, but not those enabled through a
198+
// feature
196199
if dep.is_optional() && !reqs.deps.contains_key(&*dep.name()) {
197200
continue;
198201
}
199-
// So we want this dependency. Move the features we want from `feature_deps`
200-
// to `ret`.
201-
let base = reqs.deps.remove(&*dep.name()).unwrap_or((false, vec![]));
202+
// So we want this dependency. Move the features we want from
203+
// `feature_deps` to `ret` and register ourselves as using this
204+
// name.
205+
let base = reqs.deps.get(&*dep.name()).unwrap_or(&default_dep);
206+
used_features.insert(dep.name().as_str());
202207
if !dep.is_optional() && base.0 {
203208
self.warnings.push(format!(
204209
"Package `{}` does not have feature `{}`. It has a required dependency \
@@ -209,7 +214,7 @@ impl Context {
209214
dep.name()
210215
));
211216
}
212-
let mut base = base.1;
217+
let mut base = base.1.clone();
213218
base.extend(dep.features().iter());
214219
for feature in base.iter() {
215220
if feature.contains('/') {
@@ -221,12 +226,16 @@ impl Context {
221226
ret.push((dep.clone(), base));
222227
}
223228

224-
// Any remaining entries in feature_deps are bugs in that the package does not actually
225-
// have those dependencies. We classified them as dependencies in the first place
226-
// because there is no such feature, either.
227-
if !reqs.deps.is_empty() {
228-
let unknown = reqs.deps.keys().map(|s| &s[..]).collect::<Vec<&str>>();
229-
let features = unknown.join(", ");
229+
// Any entries in `reqs.dep` which weren't used are bugs in that the
230+
// package does not actually have those dependencies. We classified
231+
// them as dependencies in the first place because there is no such
232+
// feature, either.
233+
let remaining = reqs.deps.keys()
234+
.cloned()
235+
.filter(|s| !used_features.contains(s))
236+
.collect::<Vec<_>>();
237+
if !remaining.is_empty() {
238+
let features = remaining.join(", ");
230239
return Err(match parent {
231240
None => format_err!(
232241
"Package `{}` does not have these features: `{}`",

tests/testsuite/build_script.rs

+65
Original file line numberDiff line numberDiff line change
@@ -3833,3 +3833,68 @@ fn rename_with_link_search_path() {
38333833
),
38343834
);
38353835
}
3836+
3837+
#[test]
3838+
fn optional_build_script_dep() {
3839+
let p = project("foo")
3840+
.file(
3841+
"Cargo.toml",
3842+
r#"
3843+
[project]
3844+
name = "foo"
3845+
version = "0.5.0"
3846+
authors = []
3847+
3848+
[dependencies]
3849+
bar = { path = "bar", optional = true }
3850+
3851+
[build-dependencies]
3852+
bar = { path = "bar", optional = true }
3853+
"#,
3854+
)
3855+
.file("build.rs", r#"
3856+
#[cfg(feature = "bar")]
3857+
extern crate bar;
3858+
3859+
fn main() {
3860+
#[cfg(feature = "bar")] {
3861+
println!("cargo:rustc-env=FOO={}", bar::bar());
3862+
return
3863+
}
3864+
println!("cargo:rustc-env=FOO=0");
3865+
}
3866+
"#)
3867+
.file(
3868+
"src/main.rs",
3869+
r#"
3870+
#[cfg(feature = "bar")]
3871+
extern crate bar;
3872+
3873+
fn main() {
3874+
println!("{}", env!("FOO"));
3875+
}
3876+
"#,
3877+
)
3878+
.file(
3879+
"bar/Cargo.toml",
3880+
r#"
3881+
[project]
3882+
name = "bar"
3883+
version = "0.5.0"
3884+
authors = []
3885+
"#,
3886+
)
3887+
.file(
3888+
"bar/src/lib.rs",
3889+
r#"
3890+
pub fn bar() -> u32 { 1 }
3891+
"#,
3892+
);
3893+
let p = p.build();
3894+
3895+
assert_that(p.cargo("run"), execs().with_status(0).with_stdout("0\n"));
3896+
assert_that(
3897+
p.cargo("run --features bar"),
3898+
execs().with_status(0).with_stdout("1\n"),
3899+
);
3900+
}

0 commit comments

Comments
 (0)