+use std::collections::HashSet;
use std::io::prelude::*;
use toml;
-use core::{resolver, Resolve, Workspace};
use core::resolver::WorkspaceResolve;
-use util::Filesystem;
-use util::errors::{CargoResult, CargoResultExt};
+use core::{resolver, Resolve, Workspace};
+use util::errors::{CargoResult, CargoResultExt, Internal};
use util::toml as cargo_toml;
+use util::Filesystem;
pub fn load_pkg_lockfile(ws: &Workspace) -> CargoResult<Option<Resolve>> {
if !ws.root().join("Cargo.lock").exists() {
let resolve: toml::Value = cargo_toml::parse(&s, f.path(), ws.config())?;
let v: resolver::EncodableResolve = resolve.try_into()?;
Ok(Some(v.into_resolve(ws)?))
- })()
- .chain_err(|| format!("failed to parse lock file at: {}", f.path().display()))?;
+ })().chain_err(|| format!("failed to parse lock file at: {}", f.path().display()))?;
Ok(resolve)
}
+fn duplicate_pkg_names(resolve: &Resolve) -> Vec<&'static str> {
+ let mut unique_names = HashSet::new();
+ let mut result = HashSet::new();
+ for pkg_id in resolve.iter() {
+ if !unique_names.insert(pkg_id.name()) {
+ result.insert(pkg_id.name().as_str());
+ }
+ }
+ result.into_iter().collect()
+}
+
+fn check_duplicate_pkg_names(resolve: &Resolve) -> Result<(), Internal> {
+ let names = duplicate_pkg_names(resolve);
+ if names.is_empty() {
+ Ok(())
+ } else {
+ Err(Internal::new(format_err!(
+ "dependencies contain duplicate package(s): {}",
+ names.join(", ")
+ )))
+ }
+}
+
pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()> {
// Load the original lockfile if it exists.
let ws_root = Filesystem::new(ws.root().to_path_buf());
Ok(s)
});
+ check_duplicate_pkg_names(resolve).chain_err(|| format!("failed to generate lock file"))?;
+
let toml = toml::Value::try_from(WorkspaceResolve { ws, resolve }).unwrap();
let mut out = String::new();
use std::fs::{self, File};
use std::io::prelude::*;
-use cargotest::support::{execs, project};
use cargotest::support::registry::Package;
+use cargotest::support::{execs, project, ProjectBuilder, paths};
use cargotest::ChannelChanger;
use hamcrest::{assert_that, existing_file, is_not};
assert_that(p.cargo("update"), execs().with_status(0).with_stdout(""));
assert_that(&lockfile, existing_file());
}
+
+#[test]
+fn duplicate_entries_in_lockfile() {
+ let _a = ProjectBuilder::new("a", paths::root().join("a"))
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ authors = []
+ version = "0.0.1"
+
+ [dependencies]
+ common = {path="common"}
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ let common_toml = r#"
+ [package]
+ name = "common"
+ authors = []
+ version = "0.0.1"
+ "#;
+
+ let _common_in_a = ProjectBuilder::new("common", paths::root().join("a/common"))
+ .file("Cargo.toml", common_toml)
+ .file("src/lib.rs", "")
+ .build();
+
+ let b = ProjectBuilder::new("common", paths::root().join("b"))
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ authors = []
+ version = "0.0.1"
+
+ [dependencies]
+ common = {path="common"}
+ a = {path="../a"}
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ let _common_in_b = ProjectBuilder::new("common", paths::root().join("b/common"))
+ .file("Cargo.toml", common_toml)
+ .file("src/lib.rs", "")
+ .build();
+
+ // should fail due to a duplicate package `common` in the lockfile
+ assert_that(b.cargo("build"), execs().with_status(101));
+}