diff --git a/src/cargo/core/dependency.rs b/src/cargo/core/dependency.rs index 9401eeccbb0..0e8a9223919 100644 --- a/src/cargo/core/dependency.rs +++ b/src/cargo/core/dependency.rs @@ -62,7 +62,7 @@ impl ser::Serialize for Dependency { name: self.name(), source: &self.source_id(), req: self.version_req().to_string(), - kind: self.kind(), + kind: self.kind().clone(), optional: self.is_optional(), uses_default_features: self.uses_default_features(), features: self.features(), @@ -71,11 +71,13 @@ impl ser::Serialize for Dependency { } } -#[derive(PartialEq, Clone, Debug, Copy)] +#[derive(PartialEq, Clone, Debug)] pub enum Kind { Normal, Development, Build, + /// Binary-only dependency for a named binary target. + Bin(String), } impl ser::Serialize for Kind { @@ -84,8 +86,9 @@ impl ser::Serialize for Kind { { match *self { Kind::Normal => None, - Kind::Development => Some("dev"), - Kind::Build => Some("build"), + Kind::Development => Some("dev".to_string()), + Kind::Build => Some("build".to_string()), + Kind::Bin(ref name) => Some(format!("bin:{}", name)), }.serialize(s) } } @@ -170,7 +173,7 @@ this warning. pub fn version_req(&self) -> &VersionReq { &self.req } pub fn name(&self) -> &str { &self.name } pub fn source_id(&self) -> &SourceId { &self.source_id } - pub fn kind(&self) -> Kind { self.kind } + pub fn kind(&self) -> &Kind { &self.kind } pub fn specified_req(&self) -> bool { self.specified_req } /// If none, this dependency must be built for all platforms. @@ -231,7 +234,7 @@ this warning. /// Returns false if the dependency is only used to build the local package. pub fn is_transitive(&self) -> bool { match self.kind { - Kind::Normal | Kind::Build => true, + Kind::Normal | Kind::Build | Kind::Bin(_) => true, Kind::Development => false, } } @@ -292,7 +295,7 @@ impl Dependency { pub fn version_req(&self) -> &VersionReq { self.inner.version_req() } pub fn name(&self) -> &str { self.inner.name() } pub fn source_id(&self) -> &SourceId { self.inner.source_id() } - pub fn kind(&self) -> Kind { self.inner.kind() } + pub fn kind(&self) -> &Kind { self.inner.kind() } pub fn specified_req(&self) -> bool { self.inner.specified_req() } /// If none, this dependencies must be built for all platforms. diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs index b5d01cd3efd..267a98d4b3e 100644 --- a/src/cargo/ops/cargo_rustc/context.rs +++ b/src/cargo/ops/cargo_rustc/context.rs @@ -605,6 +605,14 @@ impl<'a, 'cfg> Context<'a, 'cfg> { return false; } + // If it's a binary-only dependency, make sure we're building the + // binary it applies to. + if let DepKind::Bin(ref name) = *d.kind() { + if unit.target.name() != name { + return false; + } + } + // If we've gotten past all that, then this dependency is // actually used! true @@ -712,10 +720,13 @@ impl<'a, 'cfg> Context<'a, 'cfg> { unit.pkg.dependencies().iter().filter(|d| { d.name() == dep.name() }).any(|dep| { - match dep.kind() { + match *dep.kind() { DepKind::Normal => self.dep_platform_activated(dep, unit.kind), - _ => false, + DepKind::Bin(ref name) => { + unit.target.is_bin() && unit.target.name() == name + } + DepKind::Build | DepKind::Development => false, } }) }).map(|dep| { diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index c2915afcf55..290fe52029b 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -104,11 +104,14 @@ fn transmit(config: &Config, features: dep.features().to_vec(), version_req: dep.version_req().to_string(), target: dep.platform().map(|s| s.to_string()), - kind: match dep.kind() { - Kind::Normal => "normal", - Kind::Build => "build", - Kind::Development => "dev", - }.to_string(), + kind: match *dep.kind() { + Kind::Normal => "normal".to_string(), + Kind::Build => "build".to_string(), + // Binary-only deps are, at least for the purpose of crates.io display, equivalent + // to dev dependencies. + Kind::Development + | Kind::Bin(_) => "dev".to_string(), + }, } }).collect::>(); let manifest = pkg.manifest(); diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 197dfca8c0c..801457438ca 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -11,7 +11,7 @@ use serde::de::{self, Deserialize}; use serde_ignored; use core::{SourceId, Profiles, PackageIdSpec, GitReference, WorkspaceConfig}; -use core::{Summary, Manifest, Target, Dependency, DependencyInner, PackageId}; +use core::{Summary, Manifest, Target, TargetKind, Dependency, DependencyInner, PackageId}; use core::{EitherManifest, VirtualManifest}; use core::dependency::{Kind, Platform}; use core::manifest::{LibKind, Profile, ManifestMetadata}; @@ -191,6 +191,7 @@ type TomlExampleTarget = TomlTarget; type TomlTestTarget = TomlTarget; type TomlBenchTarget = TomlTarget; +#[derive(Debug, Clone)] pub enum TomlDependency { Simple(String), Detailed(DetailedTomlDependency) @@ -228,7 +229,7 @@ impl de::Deserialize for TomlDependency { } } -#[derive(Deserialize, Clone, Default)] +#[derive(Debug, Deserialize, Clone, Default)] pub struct DetailedTomlDependency { version: Option, path: Option, @@ -674,13 +675,13 @@ impl TomlManifest { let new_build = self.maybe_custom_build(&project.build, &layout.root); // Get targets - let targets = normalize(&layout.root, - &lib, - &bins, - new_build, - &examples, - &tests, - &benches); + let (targets, target_deps) = normalize(&layout.root, + &lib, + &bins, + new_build, + &examples, + &tests, + &benches); if targets.is_empty() { debug!("manifest has no build targets"); @@ -718,7 +719,7 @@ impl TomlManifest { None => return Ok(()) }; for (n, v) in dependencies.iter() { - let dep = v.to_dependency(n, cx, kind)?; + let dep = v.to_dependency(n, cx, kind.clone())?; cx.deps.push(dep); } @@ -747,6 +748,26 @@ impl TomlManifest { process_dependencies(&mut cx, dev_deps, Some(Kind::Development))?; } + for (index, (target, deps)) in targets.iter().zip(target_deps.iter()).enumerate() { + if deps.is_some() { + debug!("adding target-specific deps for '{}' (#{})", + target.name(), + index); + + match *target.kind() { + TargetKind::Bin => { + process_dependencies(&mut cx, + deps.as_ref(), + Some(Kind::Bin(target.name().to_string())))?; + }, + _ => { + bail!("Target-specific dependencies are only supported for binary \ + targets."); + } + } + } + } + replace = self.replace(&mut cx)?; } @@ -1093,6 +1114,7 @@ struct TomlTarget { harness: Option, #[serde(rename = "required-features")] required_features: Option>, + dependencies: Option>, } #[derive(Clone)] @@ -1228,13 +1250,15 @@ impl fmt::Debug for PathValue { } } -fn normalize(package_root: &Path, - lib: &Option, - bins: &[TomlBinTarget], - custom_build: Option, - examples: &[TomlExampleTarget], - tests: &[TomlTestTarget], - benches: &[TomlBenchTarget]) -> Vec { +fn normalize( + package_root: &Path, + lib: &Option, + bins: &[TomlBinTarget], + custom_build: Option, + examples: &[TomlExampleTarget], + tests: &[TomlTestTarget], + benches: &[TomlBenchTarget] +) -> (Vec, Vec>>) { fn configure(toml: &TomlTarget, target: &mut Target) { let t2 = target.clone(); target.set_tested(toml.test.unwrap_or(t2.tested())) @@ -1249,7 +1273,9 @@ fn normalize(package_root: &Path, }); } - let lib_target = |dst: &mut Vec, l: &TomlLibTarget| { + let lib_target = |dst: &mut Vec, + dst_deps: &mut Vec<_>, + l: &TomlLibTarget| { let path = l.path.clone().unwrap_or_else( || PathValue(Path::new("src").join(&format!("{}.rs", l.name()))) ); @@ -1267,9 +1293,12 @@ fn normalize(package_root: &Path, package_root.join(&path.0)); configure(l, &mut target); dst.push(target); + dst_deps.push(l.dependencies.clone()); }; - let bin_targets = |dst: &mut Vec, bins: &[TomlBinTarget], + let bin_targets = |dst: &mut Vec, + dst_deps: &mut Vec<_>, + bins: &[TomlBinTarget], default: &mut FnMut(&TomlBinTarget) -> PathBuf| { for bin in bins.iter() { let path = bin.path.clone().unwrap_or_else(|| { @@ -1289,6 +1318,7 @@ fn normalize(package_root: &Path, bin.required_features.clone()); configure(bin, &mut target); dst.push(target); + dst_deps.push(bin.dependencies.clone()); } }; @@ -1300,6 +1330,7 @@ fn normalize(package_root: &Path, }; let example_targets = |dst: &mut Vec, + dst_deps: &mut Vec<_>, examples: &[TomlExampleTarget], default: &mut FnMut(&TomlExampleTarget) -> PathBuf| { for ex in examples.iter() { @@ -1321,10 +1352,12 @@ fn normalize(package_root: &Path, ); configure(ex, &mut target); dst.push(target); + dst_deps.push(ex.dependencies.clone()); } }; - let test_targets = |dst: &mut Vec, + let test_targets = |dst: &mut Vec<_>, + dst_deps: &mut Vec<_>, tests: &[TomlTestTarget], default: &mut FnMut(&TomlTestTarget) -> PathBuf| { for test in tests.iter() { @@ -1336,10 +1369,12 @@ fn normalize(package_root: &Path, test.required_features.clone()); configure(test, &mut target); dst.push(target); + dst_deps.push(test.dependencies.clone()); } }; - let bench_targets = |dst: &mut Vec, + let bench_targets = |dst: &mut Vec<_>, + dst_deps: &mut Vec<_>, benches: &[TomlBenchTarget], default: &mut FnMut(&TomlBenchTarget) -> PathBuf| { for bench in benches.iter() { @@ -1351,31 +1386,34 @@ fn normalize(package_root: &Path, bench.required_features.clone()); configure(bench, &mut target); dst.push(target); + dst_deps.push(bench.dependencies.clone()); } }; let mut ret = Vec::new(); + let mut ret_deps = Vec::new(); if let Some(ref lib) = *lib { - lib_target(&mut ret, lib); - bin_targets(&mut ret, bins, + lib_target(&mut ret, &mut ret_deps, lib); + bin_targets(&mut ret, &mut ret_deps, bins, &mut |bin| Path::new("src").join("bin") .join(&format!("{}.rs", bin.name()))); } else if bins.len() > 0 { - bin_targets(&mut ret, bins, + bin_targets(&mut ret, &mut ret_deps, bins, &mut |bin| Path::new("src") .join(&format!("{}.rs", bin.name()))); } if let Some(custom_build) = custom_build { custom_build_target(&mut ret, &custom_build); + ret_deps.push(None); // Can't have build-script-specific dependencies } - example_targets(&mut ret, examples, + example_targets(&mut ret, &mut ret_deps, examples, &mut |ex| Path::new("examples") .join(&format!("{}.rs", ex.name()))); - test_targets(&mut ret, tests, &mut |test| { + test_targets(&mut ret, &mut ret_deps, tests, &mut |test| { if test.name() == "test" { Path::new("src").join("test.rs") } else { @@ -1383,7 +1421,7 @@ fn normalize(package_root: &Path, } }); - bench_targets(&mut ret, benches, &mut |bench| { + bench_targets(&mut ret, &mut ret_deps, benches, &mut |bench| { if bench.name() == "bench" { Path::new("src").join("bench.rs") } else { @@ -1391,7 +1429,9 @@ fn normalize(package_root: &Path, } }); - ret + assert_eq!(ret.len(), ret_deps.len()); + + (ret, ret_deps) } fn build_profiles(profiles: &Option) -> Profiles { diff --git a/tests/bin-only-deps.rs b/tests/bin-only-deps.rs new file mode 100644 index 00000000000..e2541af2a3a --- /dev/null +++ b/tests/bin-only-deps.rs @@ -0,0 +1,527 @@ +extern crate cargo; +extern crate cargotest; +extern crate hamcrest; +extern crate tempdir; + +use std::env; +use std::fs::{self, File}; +use std::io::prelude::*; + +use cargo::util::process; +use cargotest::{is_nightly, rustc_host, sleep_ms}; +use cargotest::support::paths::{CargoPathExt,root}; +use cargotest::support::{ProjectBuilder}; +use cargotest::support::{project, execs, main_file, basic_bin_manifest}; +use cargotest::support::registry::Package; +use hamcrest::{assert_that, existing_file, is_not}; +use tempdir::TempDir; + +#[test] +fn can_use_bin_deps() { + // Check that the basic functionality works as expected + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "binary" + +[bin.dependencies] +testdep = { path = "testdep" } +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/binary.rs", r#" +extern crate foo; +extern crate testdep; + +fn main() { + foo::bla(); + testdep::bar(); +}"#) + .file("testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] + "#) + .file("testdep/src/lib.rs", "pub fn bar() {}"); + + assert_that(p.cargo_process("build"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] testdep v0.5.0 ([..])")); +} + +#[test] +fn can_use_long_bin_deps() { + // Long dependency specifications + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "binary" + +[bin.dependencies.testdep] +path = "testdep" +version = "=0.5.0" +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/binary.rs", r#" +extern crate testdep; +fn main() { + testdep::bar(); +} +"#) + .file("testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] + "#) + .file("testdep/src/lib.rs", "pub fn bar() {}"); + + assert_that(p.cargo_process("build").arg("--bin").arg("binary"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] testdep v0.5.0 ([..])")); +} + +#[test] +fn multiple_bins_different_deps() { + // Check that multiple bins with different dependencies work as expected + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "bin1" + +[bin.dependencies] +dep1 = { path = "dep1" } + +[[bin]] +name = "bin2" + +[bin.dependencies] +dep2 = { path = "dep2" } +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/bin1.rs", r#" +extern crate foo; +extern crate dep1; + +fn main() { + foo::bla(); + dep1::bar1(); +}"#) + .file("src/bin/bin2.rs", r#" +extern crate foo; +extern crate dep2; + +fn main() { + foo::bla(); + dep2::bar2(); +}"#) + .file("dep1/Cargo.toml", r#" +[package] +name = "dep1" +version = "0.5.0" +authors = ["wycats@example.com"] + "#) + .file("dep1/src/lib.rs", "pub fn bar1() {}") + .file("dep2/Cargo.toml", r#" +[package] +name = "dep2" +version = "0.2.0" +authors = ["wycats@example.com"] + "#) + .file("dep2/src/lib.rs", "pub fn bar2() {}"); + + p.build(); + + assert_that(p.cargo("build"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] dep1 v0.5.0 ([..])") + .with_stderr_contains("[COMPILING] dep2 v0.2.0 ([..])")); + + assert_that(p.cargo("clean"), execs() + .with_status(0)); + + assert_that(p.cargo("build").arg("--bin").arg("bin1"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] dep1 v0.5.0 ([..])") + .with_stderr_does_not_contain("[COMPILING] dep2 v0.2.0 ([..])")); + + assert_that(p.cargo("build").arg("--bin").arg("bin2"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] dep2 v0.2.0 ([..])") + .with_stderr_does_not_contain("[COMPILING] dep1 v0.5.0 ([..])")); +} + +#[test] +fn multiple_bins_common_deps() { + // Check that multiple bins with common dependencies work as expected + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "bin1" + +[bin.dependencies] +testdep = { path = "testdep" } + +[[bin]] +name = "bin2" + +[bin.dependencies] +testdep = { path = "testdep" } +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/bin1.rs", r#" +extern crate foo; +extern crate testdep; + +fn main() { + foo::bla(); + testdep::bar(); +}"#) + .file("src/bin/bin2.rs", r#" +extern crate foo; +extern crate testdep; + +fn main() { + foo::bla(); + testdep::bar(); +}"#) + .file("testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] + "#) + .file("testdep/src/lib.rs", "pub fn bar() {}"); + p.build(); + + assert_that(p.cargo("build"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] testdep v0.5.0 ([..])")); + + assert_that(p.cargo("clean"), execs() + .with_status(0)); + + assert_that(p.cargo("build").arg("--bin").arg("bin1"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] testdep v0.5.0 ([..])")); + + assert_that(p.cargo("build").arg("--bin").arg("bin2"), execs() + .with_status(0) + .with_stderr_does_not_contain("[COMPILING] testdep v0.5.0 ([..])")); +} + +#[test] +fn denies_deps_on_examples() { + // We only want binary-specific dependencies for now + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[example]] +name = "ex1" + +[example.dependencies] +libc = "*" +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("examples/ex1.rs", "fn main() {}"); + + assert_that(p.cargo_process("build"), execs() + .with_status(101) + .with_stderr("\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + Target-specific dependencies are only supported for binary targets. +")); +} + +#[test] +fn denies_deps_on_tests() { + // We only want binary-specific dependencies for now + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[test]] +name = "test1" + +[test.dependencies] +libc = "*" +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("tests/test1.rs", "#[test] fn stuff() {}"); + + assert_that(p.cargo_process("build"), execs() + .with_status(101) + .with_stderr("\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + Target-specific dependencies are only supported for binary targets. +")); +} + +#[test] +fn denies_deps_on_benches() { + // We only want binary-specific dependencies for now + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bench]] +name = "bench1" + +[bench.dependencies] +libc = "*" +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("benches/bench1.rs", "#[test] fn stuff() {}"); + + assert_that(p.cargo_process("build"), execs() + .with_status(101) + .with_stderr("\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + Target-specific dependencies are only supported for binary targets. +")); +} + +#[test] +fn doesnt_add_dep_for_lib() { + // Make sure the package's lib target doesn't require the dependency to be built + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "binary" + +[bin.dependencies] +testdep = { path = "testdep" } +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/binary.rs", "fn main() {}") + .file("testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] +"#) + .file("testdep/src/lib.rs", "pub fn bar() {}"); + + assert_that(p.cargo_process("build").arg("--lib"), execs() + .with_status(0) + .with_stderr("\ +[COMPILING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +")); +} + +#[test] +fn doesnt_add_downstream_deps() { + // Make sure dependent packages don't depend on bin-only deps + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[dependencies] +library = { path = "library" } +"#) + .file("src/lib.rs", r#" +extern crate library; +pub fn bla() { + library::foo(); +} +"#) + .file("library/src/bin/binary.rs", "fn main() {}") + .file("library/Cargo.toml", r#" +[package] +name = "library" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "binary" + +[bin.dependencies] +testdep = { path = "testdep" } +"#) + .file("library/src/lib.rs", "pub fn foo() {}") + .file("library/testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] +"#) + .file("library/testdep/src/lib.rs", "pub fn bar() {}"); + p.build(); + + // Just building the libs shouldn't build testdep + assert_that(p.cargo("build"), execs() + .with_status(0) + .with_stderr_does_not_contain("testdep")); + + // Building `binary` should build testdep + assert_that(p.cargo("build").arg("-p").arg("library").arg("--bin").arg("binary"), execs() + .with_status(0) + .with_stderr_contains("[COMPILING] testdep v0.5.0 ([..])")); +} + +#[test] +fn optional_deps() { + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "binary" + +[bin.dependencies] +testdep = { path = "testdep", optional = true } +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/binary.rs", "fn main() {}") + .file("testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] +"#) + .file("testdep/src/lib.rs", "pub fn bar() {}"); + p.build(); + + // Don't compile optional dep + assert_that(p.cargo("build").arg("--bin").arg("binary"), execs() + .with_status(0) + .with_stderr_does_not_contain("testdep")); + + // Enabling optional dep feature builds testdep + assert_that(p.cargo("build").args(&["--bin", "binary", "--features", "testdep"]), + execs() + .with_status(0) + .with_stderr_contains("[COMPILING] testdep v0.5.0 ([..])")); +} + +#[test] +fn optional_dep_collides_with_feature() { + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "binary" + +[bin.dependencies] +testdep = { path = "testdep", optional = true } + +[features] +testdep = [] +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/binary.rs", "fn main() {}") + .file("testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] +"#) + .file("testdep/src/lib.rs", "pub fn bar() {}"); + + assert_that(p.cargo_process("build"), execs() + .with_status(101) + .with_stderr("\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + Features and dependencies cannot have the same name: `testdep` +")); +} + +#[test] +fn deps_collide() { + // Having the same dep as the main package is allowed, with the same semantics as duplicate + // [dev-dependencies] and [build-dependencies] entries. + + let p = project("foo") + .file("Cargo.toml", r#" +[package] +name = "foo" +version = "0.5.0" +authors = ["wycats@example.com"] + +[[bin]] +name = "binary" + +[bin.dependencies] +testdep = { path = "testdep" } + +[dependencies] +testdep = { path = "testdep" } +"#) + .file("src/lib.rs", "pub fn bla() {}") + .file("src/bin/binary.rs", "fn main() {}") + .file("testdep/Cargo.toml", r#" +[package] +name = "testdep" +version = "0.5.0" +authors = ["wycats@example.com"] +"#) + .file("testdep/src/lib.rs", "pub fn bar() {}"); + + assert_that(p.cargo_process("build"), execs().with_status(0)); +} + +// TODO: Changes to doc(tests) I'm unsure about (+ add tests) +// TODO: Document this feature