Fix optional dependencies and required dev-deps

This fixes an accidental bug introduced in #5300 by ensuring a local map keeps
track of the fact that there can be multiple dependencies for one name

Closes #5453
This commit is contained in:
Alex Crichton 2018-05-01 17:03:23 -07:00
parent acea5e2f3c
commit 02b0dba36b
4 changed files with 66 additions and 21 deletions

View File

@ -340,15 +340,18 @@ fn activate_deps_loop(
backtracked = true; backtracked = true;
Ok((candidate, has_another)) Ok((candidate, has_another))
} }
None => Err(activation_error( None => {
&cx, debug!("no candidates found");
registry.registry, Err(activation_error(
&parent, &cx,
&dep, registry.registry,
&conflicting_activations, &parent,
&candidates, &dep,
config, &conflicting_activations,
)), &candidates,
config,
))
}
} }
})?; })?;

View File

@ -140,10 +140,12 @@ fn build_feature_map(
namespaced: bool, namespaced: bool,
) -> CargoResult<FeatureMap> { ) -> CargoResult<FeatureMap> {
use self::FeatureValue::*; use self::FeatureValue::*;
let dep_map: HashMap<_, _> = dependencies let mut dep_map = HashMap::new();
.iter() for dep in dependencies.iter() {
.map(|d| (d.name().as_str(), d)) dep_map.entry(dep.name().as_str())
.collect(); .or_insert(Vec::new())
.push(dep);
}
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
for (feature, list) in features.iter() { for (feature, list) in features.iter() {
@ -159,7 +161,7 @@ fn build_feature_map(
let mut dependency_found = if namespaced { let mut dependency_found = if namespaced {
match dep_map.get(feature.as_str()) { match dep_map.get(feature.as_str()) {
Some(ref dep_data) => { Some(ref dep_data) => {
if !dep_data.is_optional() { if !dep_data.iter().any(|d| d.is_optional()) {
bail!( bail!(
"Feature `{}` includes the dependency of the same name, but this is \ "Feature `{}` includes the dependency of the same name, but this is \
left implicit in the features included by this feature.\n\ left implicit in the features included by this feature.\n\
@ -196,7 +198,9 @@ fn build_feature_map(
} }
} }
}; };
let is_optional_dep = dep_data.map_or(false, |d| d.is_optional()); let is_optional_dep = dep_data.iter()
.flat_map(|d| d.iter())
.any(|d| d.is_optional());
if let FeatureValue::Crate(ref dep_name) = val { if let FeatureValue::Crate(ref dep_name) = val {
// If we have a dependency value, check if this is the dependency named // If we have a dependency value, check if this is the dependency named
// the same as the feature that we were looking for. // the same as the feature that we were looking for.

View File

@ -113,13 +113,19 @@ impl<'cfg> RegistryIndex<'cfg> {
// interpretation of each line here and older cargo will simply // interpretation of each line here and older cargo will simply
// ignore the new lines. // ignore the new lines.
ret.extend(lines.filter_map(|line| { ret.extend(lines.filter_map(|line| {
self.parse_registry_package(line).ok().and_then(|v| { let (summary, locked) = match self.parse_registry_package(line) {
if online || load.is_crate_downloaded(v.0.package_id()) { Ok(p) => p,
Some(v) Err(e) => {
} else { info!("failed to parse `{}` registry package: {}", name, e);
None trace!("line: {}", line);
return None
} }
}) };
if online || load.is_crate_downloaded(summary.package_id()) {
Some((summary, locked))
} else {
None
}
})); }));
Ok(()) Ok(())

View File

@ -1999,3 +1999,35 @@ fn namespaced_same_name() {
execs().with_status(0), execs().with_status(0),
); );
} }
#[test]
fn only_dep_is_optional() {
Package::new("bar", "0.1.0").publish();
let p = project("foo")
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
foo = ['bar']
[dependencies]
bar = { version = "0.1", optional = true }
[dev-dependencies]
bar = "0.1"
"#,
)
.file("src/main.rs", "fn main() {}")
.build();
assert_that(
p.cargo("build"),
execs().with_status(0),
);
}