From 515aa461aff1b1de5538a0d8e786c2774a4fdaa8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 5 Feb 2016 15:14:17 -0800 Subject: [PATCH] Implement a local registry type This flavor of registry is intended to behave very similarly to the standard remote registry, except everything is contained locally on the filesystem instead. There are a few components to this new flavor of registry: 1. The registry itself is rooted at a particular directory, owning all structure beneath it. 2. There is an `index` folder with the same structure as the crates.io index describing the local registry (e.g. contents, versions, checksums, etc). 3. Inside the root will also be a list of `.crate` files which correspond to those described in the index. All crates must be of the form `name-version.crate` and be the same `.crate` files from crates.io itself. This support can currently be used via the previous implementation of source overrides with the new type: ```toml [source.crates-io] replace-with = 'my-awesome-registry' [source.my-awesome-registry] local-registry = 'path/to/registry' ``` I will soon follow up with a tool which can be used to manage these local registries externally. --- src/cargo/core/resolver/mod.rs | 14 +- src/cargo/core/source.rs | 26 ++- src/cargo/sources/config.rs | 14 +- src/cargo/sources/registry/local.rs | 89 ++++++++ src/cargo/sources/registry/mod.rs | 9 + tests/support/mod.rs | 1 + tests/support/registry.rs | 53 +++-- tests/test_bad_config.rs | 60 +++-- tests/test_cargo_cfg.rs | 4 +- tests/test_cargo_local_registry.rs | 333 ++++++++++++++++++++++++++++ tests/test_cargo_publish.rs | 6 - tests/test_cargo_registry.rs | 18 +- tests/test_lockfile_compat.rs | 18 +- tests/tests.rs | 1 + 14 files changed, 570 insertions(+), 76 deletions(-) create mode 100644 src/cargo/sources/registry/local.rs create mode 100644 tests/test_cargo_local_registry.rs diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index e44df368f98..fa2481d180b 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -217,7 +217,6 @@ struct Context { activations: HashMap<(String, SourceId), Vec>>, resolve_graph: Graph, resolve_features: HashMap>, - visited: HashSet, } /// Builds the list of all packages required to build the first argument. @@ -233,6 +232,7 @@ pub fn resolve(summary: &Summary, method: &Method, registry: &mut Registry) let _p = profile::start(format!("resolving: {}", summary.package_id())); let cx = try!(activate_deps_loop(cx, registry, Rc::new(summary.clone()), method)); + try!(check_cycles(&cx, summary.package_id())); let mut resolve = Resolve { graph: cx.resolve_graph, @@ -247,8 +247,6 @@ pub fn resolve(summary: &Summary, method: &Method, registry: &mut Registry) resolve.checksums.insert(summary.package_id().clone(), cksum); } - try!(check_cycles(&cx)); - trace!("resolved: {:?}", resolve); Ok(resolve) } @@ -841,18 +839,18 @@ impl Context { } } -fn check_cycles(cx: &Context) -> CargoResult<()> { +fn check_cycles(cx: &Context, root: &PackageId) -> CargoResult<()> { let mut summaries = HashMap::new(); for summary in cx.activations.values().flat_map(|v| v) { summaries.insert(summary.package_id(), &**summary); } - return visit(&cx.resolve, - cx.resolve.root(), + return visit(&cx.resolve_graph, + root, &summaries, &mut HashSet::new(), &mut HashSet::new()); - fn visit<'a>(resolve: &'a Resolve, + fn visit<'a>(resolve: &'a Graph, id: &'a PackageId, summaries: &HashMap<&'a PackageId, &Summary>, visited: &mut HashSet<&'a PackageId>, @@ -873,7 +871,7 @@ fn check_cycles(cx: &Context) -> CargoResult<()> { // dependencies. if checked.insert(id) { let summary = summaries[id]; - for dep in resolve.deps(id).into_iter().flat_map(|a| a) { + for dep in resolve.edges(id).into_iter().flat_map(|a| a) { let is_transitive = summary.dependencies().iter().any(|d| { d.matches_id(dep) && d.is_transitive() }); diff --git a/src/cargo/core/source.rs b/src/cargo/core/source.rs index eb0aab68780..86cd296bfc3 100644 --- a/src/cargo/core/source.rs +++ b/src/cargo/core/source.rs @@ -63,6 +63,8 @@ enum Kind { Path, /// represents the central registry Registry, + /// represents a local filesystem-based registry + LocalRegistry, } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -107,7 +109,7 @@ impl SourceId { /// use cargo::core::SourceId; /// SourceId::from_url("git+https://github.com/alexcrichton/\ /// libssh2-static-sys#80e71a3021618eb05\ - /// 656c58fb7c5ef5f12bc747f".to_string()); + /// 656c58fb7c5ef5f12bc747f"); /// ``` pub fn from_url(string: &str) -> CargoResult { let mut parts = string.splitn(2, '+'); @@ -169,6 +171,9 @@ impl SourceId { SourceIdInner { kind: Kind::Registry, ref url, .. } => { format!("registry+{}", url) } + SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => { + format!("local-registry+{}", url) + } } } @@ -186,6 +191,11 @@ impl SourceId { SourceId::new(Kind::Registry, url.clone()) } + pub fn for_local_registry(path: &Path) -> CargoResult { + let url = try!(path.to_url()); + Ok(SourceId::new(Kind::LocalRegistry, url)) + } + /// Returns the `SourceId` corresponding to the main repository. /// /// This is the main cargo registry by default, but it can be overridden in @@ -210,7 +220,9 @@ impl SourceId { pub fn url(&self) -> &Url { &self.inner.url } pub fn is_path(&self) -> bool { self.inner.kind == Kind::Path } - pub fn is_registry(&self) -> bool { self.inner.kind == Kind::Registry } + pub fn is_registry(&self) -> bool { + self.inner.kind == Kind::Registry || self.inner.kind == Kind::LocalRegistry + } pub fn is_git(&self) -> bool { match self.inner.kind { @@ -232,6 +244,13 @@ impl SourceId { Box::new(PathSource::new(&path, self, config)) } Kind::Registry => Box::new(RegistrySource::remote(self, config)), + Kind::LocalRegistry => { + let path = match self.inner.url.to_file_path() { + Ok(p) => p, + Err(()) => panic!("path sources cannot be remote"), + }; + Box::new(RegistrySource::local(self, &path, config)) + } } } @@ -317,7 +336,8 @@ impl fmt::Display for SourceId { } Ok(()) } - SourceIdInner { kind: Kind::Registry, ref url, .. } => { + SourceIdInner { kind: Kind::Registry, ref url, .. } | + SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => { write!(f, "registry {}", url) } } diff --git a/src/cargo/sources/config.rs b/src/cargo/sources/config.rs index 0cde9e5e13f..3be5156452c 100644 --- a/src/cargo/sources/config.rs +++ b/src/cargo/sources/config.rs @@ -128,11 +128,21 @@ a lock file compatible with `{orig}` cannot be generated in this situation let url = try!(url(val, &format!("source.{}.registry", name))); srcs.push(SourceId::for_registry(&url)); } + if let Some(val) = table.get("local-registry") { + let (s, path) = try!(val.string(&format!("source.{}.local-registry", + name))); + let mut path = path.to_path_buf(); + path.pop(); + path.pop(); + path.push(s); + srcs.push(try!(SourceId::for_local_registry(&path))); + } let mut srcs = srcs.into_iter(); let src = try!(srcs.next().chain_error(|| { - human(format!("no source URL specified for `source.{}`, needs \ - `registry` defined", name)) + human(format!("no source URL specified for `source.{}`, need \ + either `registry` or `local-registry` defined", + name)) })); if srcs.next().is_some() { return Err(human(format!("more than one source URL specified for \ diff --git a/src/cargo/sources/registry/local.rs b/src/cargo/sources/registry/local.rs new file mode 100644 index 00000000000..aa44e4cbc0d --- /dev/null +++ b/src/cargo/sources/registry/local.rs @@ -0,0 +1,89 @@ +use std::fs::{self, File}; +use std::io::prelude::*; +use std::path::{PathBuf, Path}; + +use rustc_serialize::hex::ToHex; + +use core::PackageId; +use sources::registry::{RegistryData, RegistryConfig}; +use util::{Config, CargoResult, ChainError, human, Sha256}; + +pub struct LocalRegistry<'cfg> { + index_path: PathBuf, + root: PathBuf, + src_path: PathBuf, + config: &'cfg Config, +} + +impl<'cfg> LocalRegistry<'cfg> { + pub fn new(root: &Path, + config: &'cfg Config, + name: &str) -> LocalRegistry<'cfg> { + LocalRegistry { + src_path: config.registry_source_path().join(name), + index_path: root.join("index"), + root: root.to_path_buf(), + config: config, + } + } +} + +impl<'cfg> RegistryData for LocalRegistry<'cfg> { + fn index_path(&self) -> &Path { + &self.index_path + } + + fn config(&self) -> CargoResult> { + // Local registries don't have configuration for remote APIs or anything + // like that + Ok(None) + } + + fn update_index(&mut self) -> CargoResult<()> { + // Nothing to update, we just use what's on disk. Verify it actually + // exists though + if !self.root.is_dir() { + bail!("local registry path is not a directory: {}", + self.root.display()) + } + if !self.index_path.is_dir() { + bail!("local registry index path is not a directory: {}", + self.index_path.display()) + } + Ok(()) + } + + fn download(&mut self, pkg: &PackageId, checksum: &str) + -> CargoResult { + let crate_file = format!("{}-{}.crate", pkg.name(), pkg.version()); + let crate_file = self.root.join(&crate_file); + + // If we've already got an unpacked version of this crate, then skip the + // checksum below as it is in theory already verified. + let dst = format!("{}-{}", pkg.name(), pkg.version()); + let dst = self.src_path.join(&dst); + if fs::metadata(&dst).is_ok() { + return Ok(crate_file) + } + + try!(self.config.shell().status("Unpacking", pkg)); + + // We don't actually need to download anything per-se, we just need to + // verify the checksum matches the .crate file itself. + let mut file = try!(File::open(&crate_file).chain_error(|| { + human(format!("failed to read `{}` for `{}`", crate_file.display(), + pkg)) + })); + let mut data = Vec::new(); + try!(file.read_to_end(&mut data).chain_error(|| { + human(format!("failed to read `{}`", crate_file.display())) + })); + let mut state = Sha256::new(); + state.update(&data); + if state.finish().to_hex() != checksum { + bail!("failed to verify the checksum of `{}`", pkg) + } + + Ok(crate_file) + } +} diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 4a1deaa5df8..5d408e513fe 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -226,6 +226,7 @@ pub trait RegistryData { mod index; mod remote; +mod local; fn short_name(id: &SourceId) -> String { let hash = hex::short_hash(id); @@ -241,6 +242,14 @@ impl<'cfg> RegistrySource<'cfg> { RegistrySource::new(source_id, config, &name, Box::new(ops)) } + pub fn local(source_id: &SourceId, + path: &Path, + config: &'cfg Config) -> RegistrySource<'cfg> { + let name = short_name(source_id); + let ops = local::LocalRegistry::new(path, config, &name); + RegistrySource::new(source_id, config, &name, Box::new(ops)) + } + fn new(source_id: &SourceId, config: &'cfg Config, name: &str, diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 1135849184a..b689da99722 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -666,3 +666,4 @@ pub static UPLOADING: &'static str = " Uploading"; pub static VERIFYING: &'static str = " Verifying"; pub static ARCHIVING: &'static str = " Archiving"; pub static INSTALLING: &'static str = " Installing"; +pub static UNPACKING: &'static str = " Unpacking"; diff --git a/tests/support/registry.rs b/tests/support/registry.rs index ee57354309f..f5fde354d65 100644 --- a/tests/support/registry.rs +++ b/tests/support/registry.rs @@ -24,6 +24,7 @@ pub struct Package { deps: Vec<(String, String, &'static str, String)>, files: Vec<(String, String)>, yanked: bool, + local: bool, } pub fn init() { @@ -62,9 +63,15 @@ impl Package { deps: Vec::new(), files: Vec::new(), yanked: false, + local: false, } } + pub fn local(&mut self, local: bool) -> &mut Package { + self.local = local; + self + } + pub fn file(&mut self, name: &str, contents: &str) -> &mut Package { self.files.push((name.to_string(), contents.to_string())); self @@ -96,7 +103,6 @@ impl Package { self } - #[allow(deprecated)] // connect => join in 1.3 pub fn publish(&self) { self.make_archive(); @@ -109,7 +115,7 @@ impl Package { \"target\":{},\ \"optional\":false,\ \"kind\":\"{}\"}}", name, req, target, kind) - }).collect::>().connect(","); + }).collect::>().join(","); let cksum = { let mut c = Vec::new(); File::open(&self.archive_dst()).unwrap() @@ -128,7 +134,11 @@ impl Package { }; // Write file/line in the index - let dst = registry_path().join(&file); + let dst = if self.local { + registry_path().join("index").join(&file) + } else { + registry_path().join(&file) + }; let mut prev = String::new(); let _ = File::open(&dst).and_then(|mut f| f.read_to_string(&mut prev)); fs::create_dir_all(dst.parent().unwrap()).unwrap(); @@ -136,20 +146,22 @@ impl Package { .write_all((prev + &line[..] + "\n").as_bytes()).unwrap(); // Add the new file to the index - let repo = git2::Repository::open(®istry_path()).unwrap(); - let mut index = repo.index().unwrap(); - index.add_path(Path::new(&file)).unwrap(); - index.write().unwrap(); - let id = index.write_tree().unwrap(); - - // Commit this change - let tree = repo.find_tree(id).unwrap(); - let sig = repo.signature().unwrap(); - let parent = repo.refname_to_id("refs/heads/master").unwrap(); - let parent = repo.find_commit(parent).unwrap(); - repo.commit(Some("HEAD"), &sig, &sig, - "Another commit", &tree, - &[&parent]).unwrap(); + if !self.local { + let repo = git2::Repository::open(®istry_path()).unwrap(); + let mut index = repo.index().unwrap(); + index.add_path(Path::new(&file)).unwrap(); + index.write().unwrap(); + let id = index.write_tree().unwrap(); + + // Commit this change + let tree = repo.find_tree(id).unwrap(); + let sig = repo.signature().unwrap(); + let parent = repo.refname_to_id("refs/heads/master").unwrap(); + let parent = repo.find_commit(parent).unwrap(); + repo.commit(Some("HEAD"), &sig, &sig, + "Another commit", &tree, + &[&parent]).unwrap(); + } } fn make_archive(&self) { @@ -199,7 +211,12 @@ impl Package { } pub fn archive_dst(&self) -> PathBuf { - dl_path().join(&self.name).join(&self.vers).join("download") + if self.local { + registry_path().join(format!("{}-{}.crate", self.name, + self.vers)) + } else { + dl_path().join(&self.name).join(&self.vers).join("download") + } } } diff --git a/tests/test_bad_config.rs b/tests/test_bad_config.rs index b0fd3e3b990..a1008f30ff3 100644 --- a/tests/test_bad_config.rs +++ b/tests/test_bad_config.rs @@ -438,9 +438,9 @@ test!(bad_source_config1 { "#); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ -no source URL specified for `source.foo`, needs [..] -")); + execs().with_status(101).with_stderr(&format!("\ +{error} no source URL specified for `source.foo`, need [..] +", error = ERROR))); }); test!(bad_source_config2 { @@ -462,13 +462,13 @@ test!(bad_source_config2 { "#); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ -Unable to update registry https://[..] + execs().with_status(101).with_stderr(&format!("\ +{error} Unable to update registry https://[..] Caused by: could not find a configured source with the name `bar` \ when attempting to lookup `crates-io` (configuration in [..]) -")); +", error = ERROR))); }); test!(bad_source_config3 { @@ -490,12 +490,12 @@ test!(bad_source_config3 { "#); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ -Unable to update registry https://[..] + execs().with_status(101).with_stderr(&format!("\ +{error} Unable to update registry https://[..] Caused by: detected a cycle of `replace-with` sources, [..] -")); +", error = ERROR))); }); test!(bad_source_config4 { @@ -521,13 +521,13 @@ test!(bad_source_config4 { "#); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ -Unable to update registry https://[..] + execs().with_status(101).with_stderr(&format!("\ +{error} Unable to update registry https://[..] Caused by: detected a cycle of `replace-with` sources, the source `crates-io` is \ eventually replaced with itself (configuration in [..]) -")); +", error = ERROR))); }); test!(bad_source_config5 { @@ -552,12 +552,12 @@ test!(bad_source_config5 { "#); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ -configuration key `source.bar.registry` specified an invalid URL (in [..]) + execs().with_status(101).with_stderr(&format!("\ +{error} configuration key `source.bar.registry` specified an invalid URL (in [..]) Caused by: invalid url `not a url`: [..] -")); +", error = ERROR))); }); test!(bad_source_config6 { @@ -579,7 +579,31 @@ test!(bad_source_config6 { "#); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ -expected a string, but found a array for `source.crates-io.replace-with` in [..] -")); + execs().with_status(101).with_stderr(&format!("\ +{error} expected a string, but found a array for `source.crates-io.replace-with` in [..] +", error = ERROR))); +}); + +test!(bad_source_config7 { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#) + .file("src/lib.rs", "") + .file(".cargo/config", r#" + [source.foo] + registry = 'http://example.com' + local-registry = 'file:///another/file' + "#); + + assert_that(p.cargo_process("build"), + execs().with_status(101).with_stderr(&format!("\ +{error} more than one source URL specified for `source.foo` +", error = ERROR))); }); diff --git a/tests/test_cargo_cfg.rs b/tests/test_cargo_cfg.rs index dbb2fb58335..c58906ae411 100644 --- a/tests/test_cargo_cfg.rs +++ b/tests/test_cargo_cfg.rs @@ -215,8 +215,8 @@ test!(works_through_the_registry { {updating} registry [..] {downloading} [..] {downloading} [..] -{compiling} foo v0.1.0 ([..]) -{compiling} bar v0.1.0 ([..]) +{compiling} foo v0.1.0 +{compiling} bar v0.1.0 {compiling} a v0.0.1 ([..]) ", compiling = COMPILING, updating = UPDATING, downloading = DOWNLOADING))); }); diff --git a/tests/test_cargo_local_registry.rs b/tests/test_cargo_local_registry.rs new file mode 100644 index 00000000000..b59fc66854f --- /dev/null +++ b/tests/test_cargo_local_registry.rs @@ -0,0 +1,333 @@ +use std::fs::{self, File}; +use std::io::prelude::*; + +use hamcrest::assert_that; + +use support::{project, execs, COMPILING, UPDATING, UNPACKING, ERROR}; +use support::paths::{self, CargoPathExt}; +use support::registry::Package; + +fn setup() { + let root = paths::root(); + fs::create_dir(&root.join(".cargo")).unwrap(); + File::create(root.join(".cargo/config")).unwrap().write_all(br#" + [source.crates-io] + registry = 'https://wut' + replace-with = 'my-awesome-local-directory' + + [source.my-awesome-local-directory] + local-registry = 'registry' + "#).unwrap(); +} + +test!(simple { + Package::new("foo", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + + let p = project("bar") + .file("Cargo.toml", r#" + [project] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "0.0.1" + "#) + .file("src/lib.rs", r#" + extern crate foo; + pub fn bar() { + foo::foo(); + } + "#); + + assert_that(p.cargo_process("build"), + execs().with_status(0).with_stdout(&format!("\ +{unpacking} foo v0.0.1 ([..]) +{compiling} foo v0.0.1 +{compiling} bar v0.0.1 ({dir}) +", + compiling = COMPILING, + unpacking = UNPACKING, + dir = p.url()))); + assert_that(p.cargo("build"), execs().with_status(0).with_stdout("")); + assert_that(p.cargo("test"), execs().with_status(0)); +}); + +test!(multiple_versions { + Package::new("foo", "0.0.1").local(true).publish(); + Package::new("foo", "0.1.0") + .local(true) + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + + let p = project("bar") + .file("Cargo.toml", r#" + [project] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "*" + "#) + .file("src/lib.rs", r#" + extern crate foo; + pub fn bar() { + foo::foo(); + } + "#); + + assert_that(p.cargo_process("build"), + execs().with_status(0).with_stdout(&format!("\ +{unpacking} foo v0.1.0 ([..]) +{compiling} foo v0.1.0 +{compiling} bar v0.0.1 ({dir}) +", + compiling = COMPILING, + unpacking = UNPACKING, + dir = p.url()))); + + Package::new("foo", "0.2.0") + .local(true) + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + + assert_that(p.cargo("update").arg("-v"), + execs().with_status(0).with_stdout(&format!("\ +{updating} foo v0.1.0 -> v0.2.0 +", + updating = UPDATING))); +}); + +test!(multiple_names { + Package::new("foo", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("bar", "0.1.0") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + + let p = project("local") + .file("Cargo.toml", r#" + [project] + name = "local" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "*" + bar = "*" + "#) + .file("src/lib.rs", r#" + extern crate foo; + extern crate bar; + pub fn local() { + foo::foo(); + bar::bar(); + } + "#); + + assert_that(p.cargo_process("build"), + execs().with_status(0).with_stdout(&format!("\ +{unpacking} [..] +{unpacking} [..] +{compiling} [..] +{compiling} [..] +{compiling} local v0.0.1 ({dir}) +", + compiling = COMPILING, + unpacking = UNPACKING, + dir = p.url()))); +}); + +test!(interdependent { + Package::new("foo", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("bar", "0.1.0") + .local(true) + .dep("foo", "*") + .file("src/lib.rs", "extern crate foo; pub fn bar() {}") + .publish(); + + let p = project("local") + .file("Cargo.toml", r#" + [project] + name = "local" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "*" + bar = "*" + "#) + .file("src/lib.rs", r#" + extern crate foo; + extern crate bar; + pub fn local() { + foo::foo(); + bar::bar(); + } + "#); + + assert_that(p.cargo_process("build"), + execs().with_status(0).with_stdout(&format!("\ +{unpacking} [..] +{unpacking} [..] +{compiling} foo v0.0.1 +{compiling} bar v0.1.0 +{compiling} local v0.0.1 ({dir}) +", + compiling = COMPILING, + unpacking = UNPACKING, + dir = p.url()))); +}); + +test!(path_dep_rewritten { + Package::new("foo", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("bar", "0.1.0") + .local(true) + .dep("foo", "*") + .file("Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { path = "foo", version = "*" } + "#) + .file("src/lib.rs", "extern crate foo; pub fn bar() {}") + .file("foo/Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("foo/src/lib.rs", "pub fn foo() {}") + .publish(); + + let p = project("local") + .file("Cargo.toml", r#" + [project] + name = "local" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "*" + bar = "*" + "#) + .file("src/lib.rs", r#" + extern crate foo; + extern crate bar; + pub fn local() { + foo::foo(); + bar::bar(); + } + "#); + + assert_that(p.cargo_process("build"), + execs().with_status(0).with_stdout(&format!("\ +{unpacking} [..] +{unpacking} [..] +{compiling} foo v0.0.1 +{compiling} bar v0.1.0 +{compiling} local v0.0.1 ({dir}) +", + compiling = COMPILING, + unpacking = UNPACKING, + dir = p.url()))); +}); + +test!(invalid_dir_bad { + let p = project("local") + .file("Cargo.toml", r#" + [project] + name = "local" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "*" + "#) + .file("src/lib.rs", "") + .file(".cargo/config", r#" + [source.crates-io] + registry = 'https://wut' + replace-with = 'my-awesome-local-directory' + + [source.my-awesome-local-directory] + local-registry = '/path/to/nowhere' + "#); + + + assert_that(p.cargo_process("build"), + execs().with_status(101).with_stderr(&format!("\ +{error} Unable to update registry https://[..] + +Caused by: + failed to update replaced source `registry https://[..]` + +Caused by: + local registry path is not a directory: [..]path[..]to[..]nowhere +", error = ERROR))); +}); + +test!(different_directory_replacing_the_registry_is_bad { + // Move our test's .cargo/config to a temporary location and publish a + // registry package we're going to use first. + let config = paths::root().join(".cargo"); + let config_tmp = paths::root().join(".cargo-old"); + fs::rename(&config, &config_tmp).unwrap(); + + let p = project("local") + .file("Cargo.toml", r#" + [project] + name = "local" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "*" + "#) + .file("src/lib.rs", ""); + p.build(); + + // Generate a lock file against the crates.io registry + Package::new("foo", "0.0.1").publish(); + assert_that(p.cargo("build"), execs().with_status(0)); + + // Switch back to our directory source, and now that we're replacing + // crates.io make sure that this fails because we're replacing with a + // different checksum + let _ = config.rm_rf(); + fs::rename(&config_tmp, &config).unwrap(); + Package::new("foo", "0.0.1") + .file("src/lib.rs", "invalid") + .local(true) + .publish(); + + assert_that(p.cargo("build"), + execs().with_status(101).with_stderr(&format!("\ +{error} checksum for `foo v0.0.1` changed between lock files + +this could be indicative of a few possible errors: + + * the lock file is corrupt + * a replacement source in use (e.g. a mirror) returned a different checksum + * the source itself may be corrupt in one way or another + +unable to verify that `foo v0.0.1` was the same as before in any situation + +", error = ERROR))); +}); diff --git a/tests/test_cargo_publish.rs b/tests/test_cargo_publish.rs index 194a0fd6ad2..1aaf0cfc4d2 100644 --- a/tests/test_cargo_publish.rs +++ b/tests/test_cargo_publish.rs @@ -6,14 +6,8 @@ use flate2::read::GzDecoder; use tar::Archive; use support::{project, execs}; -<<<<<<< HEAD use support::{UPDATING, PACKAGING, UPLOADING, ERROR}; -use support::paths; -use support::git::repo; -======= -use support::{UPDATING, PACKAGING, UPLOADING}; use support::registry; ->>>>>>> Implement source redirection use hamcrest::assert_that; diff --git a/tests/test_cargo_registry.rs b/tests/test_cargo_registry.rs index 5790fd172d9..2fccf98633e 100644 --- a/tests/test_cargo_registry.rs +++ b/tests/test_cargo_registry.rs @@ -100,7 +100,7 @@ test!(nonexistent { .file("src/main.rs", "fn main() {}"); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ + execs().with_status(101).with_stderr(&format!("\ {error} no matching package named `nonexistent` found (required by `foo`) location searched: registry [..] version required: >= 0.0.0 @@ -125,7 +125,7 @@ test!(wrong_version { Package::new("foo", "0.0.2").publish(); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ + execs().with_status(101).with_stderr(&format!("\ {error} no matching package named `foo` found (required by `foo`) location searched: registry [..] version required: >= 1.0.0 @@ -137,7 +137,7 @@ error = ERROR))); Package::new("foo", "0.0.4").publish(); assert_that(p.cargo_process("build"), - execs().with_status(101).with_stderr("\ + execs().with_status(101).with_stderr(&format!("\ {error} no matching package named `foo` found (required by `foo`) location searched: registry [..] version required: >= 1.0.0 @@ -193,7 +193,6 @@ test!(update_registry { assert_that(p.cargo_process("build"), execs().with_status(101).with_stderr(&format!("\ - execs().with_status(101).with_stderr("\ {error} no matching package named `notyet` found (required by `foo`) location searched: registry [..] version required: >= 0.0.0 @@ -393,7 +392,6 @@ test!(relying_on_a_yank_is_bad { assert_that(p.cargo("build"), execs().with_status(101).with_stderr(&format!("\ - execs().with_status(101).with_stderr("\ {error} no matching package named `baz` found (required by `bar`) location searched: registry [..] version required: = 0.0.2 @@ -421,7 +419,7 @@ test!(yanks_in_lockfiles_are_ok { assert_that(p.cargo("build"), execs().with_status(0)); - fs::remove_dir_all(®istry::registry_path().join("3")).unwrap(); + registry::registry_path().join("3").rm_rf().unwrap(); Package::new("bar", "0.0.1").yanked(true).publish(); @@ -429,7 +427,7 @@ test!(yanks_in_lockfiles_are_ok { execs().with_status(0).with_stdout("")); assert_that(p.cargo("update"), - execs().with_status(101).with_stderr("\ + execs().with_status(101).with_stderr(&format!("\ {error} no matching package named `bar` found (required by `foo`) location searched: registry [..] version required: * @@ -746,7 +744,7 @@ test!(update_publish_then_update { .file("src/main.rs", "fn main() {}"); assert_that(p2.cargo_process("build"), execs().with_status(0)); - fs::remove_dir_all(®istry).unwrap(); + registry.rm_rf().unwrap(); fs::rename(&backup, ®istry).unwrap(); fs::rename(p2.root().join("Cargo.lock"), p.root().join("Cargo.lock")).unwrap(); @@ -1031,8 +1029,8 @@ test!(only_download_relevant { assert_that(p.cargo("build"), execs().with_status(0).with_stdout(&format!("\ {updating} registry `[..]` -{downloading} baz v0.1.0 -{compiling} baz v0.1.0 ([..]) +{downloading} baz v0.1.0 ([..]) +{compiling} baz v0.1.0 {compiling} bar v0.5.0 ([..]) ", downloading = DOWNLOADING, compiling = COMPILING, updating = UPDATING))); }); diff --git a/tests/test_lockfile_compat.rs b/tests/test_lockfile_compat.rs index 03cc3dbe7f7..c25709614ae 100644 --- a/tests/test_lockfile_compat.rs +++ b/tests/test_lockfile_compat.rs @@ -141,9 +141,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "#).unwrap(); assert_that(p.cargo("build"), - execs().with_status(101).with_stderr_contains("\ -checksum for `foo v0.1.0` changed between lock files -")); + execs().with_status(101).with_stderr_contains(&format!("\ +{error} checksum for `foo v0.1.0` changed between lock files +", error = ERROR))); }); // If the checksum is unlisted in the lockfile (e.g. ) yet we can @@ -183,8 +183,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "#).unwrap(); assert_that(p.cargo("fetch"), - execs().with_status(101).with_stderr("\ -checksum for `foo v0.1.0` was not previously calculated, but a checksum could \ + execs().with_status(101).with_stderr(&format!("\ +{error} checksum for `foo v0.1.0` was not previously calculated, but a checksum could \ now be calculated this could be indicative of a few possible situations: @@ -195,7 +195,7 @@ this could be indicative of a few possible situations: older implementation does not * the lock file is corrupt -")); +", error = ERROR))); }); // If the checksum is listed in the lockfile yet we cannot calculate it (e.g. @@ -244,8 +244,8 @@ source = "git+{0}" .write_all(lockfile.as_bytes()).unwrap(); assert_that(p.cargo("fetch"), - execs().with_status(101).with_stderr("\ -checksum for `foo v0.1.0 ([..])` could not be calculated, but a \ + execs().with_status(101).with_stderr(&format!("\ +{error} checksum for `foo v0.1.0 ([..])` could not be calculated, but a \ checksum is listed in the existing lock file[..] this could be indicative of a few possible situations: @@ -256,5 +256,5 @@ this could be indicative of a few possible situations: unable to verify that `foo v0.1.0 ([..])` was the same as before in either situation -")); +", error = ERROR))); }); diff --git a/tests/tests.rs b/tests/tests.rs index 6b2457b953d..28422efdf9e 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -72,6 +72,7 @@ mod test_cargo_version; mod test_shell; mod test_cargo_death; mod test_cargo_cfg; +mod test_cargo_local_registry; mod test_lockfile_compat; thread_local!(static RUSTC: Rustc = Rustc::new("rustc").unwrap());