Skip to content

Commit

Permalink
Auto merge of rust-lang#2135 - alexcrichton:bundle-crate-in-registry,…
Browse files Browse the repository at this point in the history
… r=huonw

Even if multiple ones are included, don't recurse!

Closes rust-lang#2132
  • Loading branch information
bors committed Nov 11, 2015
2 parents 6e639ad + 542355d commit 4bb2668
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 186 deletions.
15 changes: 11 additions & 4 deletions src/cargo/sources/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,19 @@ impl<'cfg> PathSource<'cfg> {
pub fn read_packages(&self) -> CargoResult<Vec<Package>> {
if self.updated {
Ok(self.packages.clone())
} else if self.id.is_path() && self.id.precise().is_some() {
} else if (self.id.is_path() && self.id.precise().is_some()) ||
self.id.is_registry() {
// If our source id is a path and it's listed with a precise
// version, then it means that we're not allowed to have nested
// dependencies (they've been rewritten to crates.io dependencies)
// In this case we specifically read just one package, not a list of
// packages.
// dependencies (they've been rewritten to crates.io dependencies).
//
// If our source id is a registry dependency then crates are
// published one at a time so we don't recurse as well. Note that
// cargo by default doesn't package up nested dependencies but it
// may do so for custom-crafted tarballs.
//
// In these cases we specifically read just one package, not a list
// of packages.
let path = self.path.join("Cargo.toml");
let (pkg, _) = try!(ops::read_package(&path, &self.id,
self.config));
Expand Down
232 changes: 135 additions & 97 deletions tests/support/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ use flate2::Compression::Default;
use flate2::write::GzEncoder;
use git2;
use rustc_serialize::hex::ToHex;
use tar::Archive;
use tar::{Archive, Header};
use url::Url;

use support::project;
use support::paths;
use support::git::repo;
use cargo::util::Sha256;
Expand All @@ -19,9 +18,20 @@ pub fn registry() -> Url { Url::from_file_path(&*registry_path()).ok().unwrap()
pub fn dl_path() -> PathBuf { paths::root().join("dl") }
pub fn dl_url() -> Url { Url::from_file_path(&*dl_path()).ok().unwrap() }

pub fn init() {
pub struct Package {
name: String,
vers: String,
deps: Vec<(String, String, &'static str)>,
files: Vec<(String, String)>,
yanked: bool,
}

fn init() {
let config = paths::home().join(".cargo/config");
fs::create_dir_all(config.parent().unwrap()).unwrap();
if fs::metadata(&config).is_ok() {
return
}
File::create(&config).unwrap().write_all(format!(r#"
[registry]
index = "{reg}"
Expand All @@ -36,114 +46,142 @@ pub fn init() {
.build();
}

pub fn mock_archive(name: &str, version: &str, deps: &[(&str, &str, &str)]) {
let mut manifest = format!(r#"
[package]
name = "{}"
version = "{}"
authors = []
"#, name, version);
for &(dep, req, kind) in deps.iter() {
manifest.push_str(&format!(r#"
[{}dependencies.{}]
version = "{}"
"#, match kind {
"build" => "build-",
"dev" => "dev-",
_ => ""
}, dep, req));
impl Package {
pub fn new(name: &str, vers: &str) -> Package {
init();
Package {
name: name.to_string(),
vers: vers.to_string(),
deps: Vec::new(),
files: Vec::new(),
yanked: false,
}
}
let p = project(name)
.file("Cargo.toml", &manifest)
.file("src/lib.rs", "")
.file("src/main.rs", &format!("\
extern crate {};
fn main() {{}}
", name));
p.build();

let dst = mock_archive_dst(name, version);
fs::create_dir_all(dst.parent().unwrap()).unwrap();
let f = File::create(&dst).unwrap();
let a = Archive::new(GzEncoder::new(f, Default));
a.append_file(&format!("{}-{}/Cargo.toml", name, version),
&mut File::open(&p.root().join("Cargo.toml")).unwrap()).unwrap();
a.append_file(&format!("{}-{}/src/lib.rs", name, version),
&mut File::open(&p.root().join("src/lib.rs")).unwrap()).unwrap();
a.append_file(&format!("{}-{}/src/main.rs", name, version),
&mut File::open(&p.root().join("src/main.rs")).unwrap()).unwrap();
a.finish().unwrap();
}

pub fn mock_archive_dst(name: &str, version: &str) -> PathBuf {
dl_path().join(name).join(version).join("download")
}
pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
self.files.push((name.to_string(), contents.to_string()));
self
}

pub fn mock_pkg(name: &str, version: &str, deps: &[(&str, &str, &str)]) {
mock_pkg_yank(name, version, deps, false)
}
pub fn dep(&mut self, name: &str, vers: &str) -> &mut Package {
self.deps.push((name.to_string(), vers.to_string(), "normal"));
self
}

pub fn mock_pkg_yank(name: &str, version: &str, deps: &[(&str, &str, &str)],
yanked: bool) {
mock_archive(name, version, deps);
let mut c = Vec::new();
File::open(&mock_archive_dst(name, version)).unwrap()
.read_to_end(&mut c).unwrap();
let line = pkg(name, version, deps, &cksum(&c), yanked);

let file = match name.len() {
1 => format!("1/{}", name),
2 => format!("2/{}", name),
3 => format!("3/{}/{}", &name[..1], name),
_ => format!("{}/{}/{}", &name[0..2], &name[2..4], name),
};
publish(&file, &line);
}
pub fn dev_dep(&mut self, name: &str, vers: &str) -> &mut Package {
self.deps.push((name.to_string(), vers.to_string(), "dev"));
self
}

pub fn yanked(&mut self, yanked: bool) -> &mut Package {
self.yanked = yanked;
self
}

#[allow(deprecated)] // connect => join in 1.3
pub fn publish(&self) {
self.make_archive();

pub fn publish(file: &str, line: &str) {
let repo = git2::Repository::open(&registry_path()).unwrap();
let mut index = repo.index().unwrap();
{
let dst = registry_path().join(file);
// Figure out what we're going to write into the index
let deps = self.deps.iter().map(|&(ref name, ref req, ref kind)| {
format!("{{\"name\":\"{}\",\
\"req\":\"{}\",\
\"features\":[],\
\"default_features\":false,\
\"target\":null,\
\"optional\":false,\
\"kind\":\"{}\"}}", name, req, kind)
}).collect::<Vec<_>>().connect(",");
let cksum = {
let mut c = Vec::new();
File::open(&self.archive_dst()).unwrap()
.read_to_end(&mut c).unwrap();
cksum(&c)
};
let line = format!("{{\"name\":\"{}\",\"vers\":\"{}\",\
\"deps\":[{}],\"cksum\":\"{}\",\"features\":{{}},\
\"yanked\":{}}}",
self.name, self.vers, deps, cksum, self.yanked);
let file = match self.name.len() {
1 => format!("1/{}", self.name),
2 => format!("2/{}", self.name),
3 => format!("3/{}/{}", &self.name[..1], self.name),
_ => format!("{}/{}/{}", &self.name[0..2], &self.name[2..4], self.name),
};

// Write file/line in the index
let dst = 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();
File::create(&dst).unwrap()
.write_all((prev + line + "\n").as_bytes()).unwrap();
.write_all((prev + &line[..] + "\n").as_bytes()).unwrap();

// Add the new file to the index
let repo = git2::Repository::open(&registry_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();
}
index.add_path(Path::new(file)).unwrap();
index.write().unwrap();
let id = index.write_tree().unwrap();
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();
}

#[allow(deprecated)] // connect => join in 1.3
pub fn pkg(name: &str, vers: &str, deps: &[(&str, &str, &str)], cksum: &str,
yanked: bool) -> String {
let deps = deps.iter().map(|&(a, b, c)| dep(a, b, c)).collect::<Vec<String>>();
format!("{{\"name\":\"{}\",\"vers\":\"{}\",\
\"deps\":[{}],\"cksum\":\"{}\",\"features\":{{}},\
\"yanked\":{}}}",
name, vers, deps.connect(","), cksum, yanked)
}
fn make_archive(&self) {
let mut manifest = format!(r#"
[package]
name = "{}"
version = "{}"
authors = []
"#, self.name, self.vers);
for &(ref dep, ref req, kind) in self.deps.iter() {
manifest.push_str(&format!(r#"
[{}dependencies.{}]
version = "{}"
"#, match kind {
"build" => "build-",
"dev" => "dev-",
_ => ""
}, dep, req));
}

pub fn dep(name: &str, req: &str, kind: &str) -> String {
format!("{{\"name\":\"{}\",\
\"req\":\"{}\",\
\"features\":[],\
\"default_features\":false,\
\"target\":null,\
\"optional\":false,\
\"kind\":\"{}\"}}", name, req, kind)
let dst = self.archive_dst();
fs::create_dir_all(dst.parent().unwrap()).unwrap();
let f = File::create(&dst).unwrap();
let a = Archive::new(GzEncoder::new(f, Default));
self.append(&a, "Cargo.toml", &manifest);
if self.files.len() == 0 {
self.append(&a, "src/lib.rs", "");
} else {
for &(ref name, ref contents) in self.files.iter() {
self.append(&a, name, contents);
}
}
a.finish().unwrap();
}

fn append<W: Write>(&self, ar: &Archive<W>, file: &str, contents: &str) {
let mut header = Header::new();
header.set_size(contents.len() as u64);
header.set_path(format!("{}-{}/{}", self.name, self.vers, file)).unwrap();
header.set_cksum();

ar.append(&header, &mut contents.as_bytes()).unwrap();
}

pub fn archive_dst(&self) -> PathBuf {
dl_path().join(&self.name).join(&self.vers).join("download")
}
}

pub fn cksum(s: &[u8]) -> String {
fn cksum(s: &[u8]) -> String {
let mut sha = Sha256::new();
sha.update(s);
sha.finish().to_hex()
Expand Down
33 changes: 21 additions & 12 deletions tests/test_cargo_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@ use hamcrest::{assert_that, existing_file, is_not, Matcher, MatchResult};
use support::{project, execs, cargo_dir};
use support::{UPDATING, DOWNLOADING, COMPILING, INSTALLING, REMOVING};
use support::paths;
use support::registry as r;
use support::registry::Package;
use support::git;

use self::InstalledExe as has_installed_exe;

fn setup() {
r::init();
}

fn pkg(name: &str, vers: &str) {
Package::new(name, vers)
.file("src/lib.rs", "")
.file("src/main.rs", &format!("
extern crate {};
fn main() {{}}
", name))
.publish()
}

fn cargo_process(s: &str) -> ProcessBuilder {
Expand Down Expand Up @@ -50,7 +59,7 @@ impl fmt::Display for InstalledExe {
}

test!(simple {
r::mock_pkg("foo", "0.0.1", &[]);
pkg("foo", "0.0.1");

assert_that(cargo_process("install").arg("foo"),
execs().with_status(0).with_stdout(&format!("\
Expand All @@ -76,8 +85,8 @@ test!(simple {
});

test!(pick_max_version {
r::mock_pkg("foo", "0.0.1", &[]);
r::mock_pkg("foo", "0.0.2", &[]);
pkg("foo", "0.0.1");
pkg("foo", "0.0.2");

assert_that(cargo_process("install").arg("foo"),
execs().with_status(0).with_stdout(&format!("\
Expand All @@ -95,15 +104,15 @@ test!(pick_max_version {
});

test!(missing {
r::mock_pkg("foo", "0.0.1", &[]);
pkg("foo", "0.0.1");
assert_that(cargo_process("install").arg("bar"),
execs().with_status(101).with_stderr("\
could not find `bar` in `registry file://[..]`
"));
});

test!(bad_version {
r::mock_pkg("foo", "0.0.1", &[]);
pkg("foo", "0.0.1");
assert_that(cargo_process("install").arg("foo").arg("--vers=0.2.0"),
execs().with_status(101).with_stderr("\
could not find `foo` in `registry file://[..]` with version `0.2.0`
Expand All @@ -118,7 +127,7 @@ must specify a crate to install from crates.io
});

test!(install_location_precedence {
r::mock_pkg("foo", "0.0.1", &[]);
pkg("foo", "0.0.1");

let root = paths::root();
let t1 = root.join("t1");
Expand Down Expand Up @@ -428,9 +437,9 @@ test!(git_repo {
});

test!(list {
r::mock_pkg("foo", "0.0.1", &[]);
r::mock_pkg("bar", "0.2.1", &[]);
r::mock_pkg("bar", "0.2.2", &[]);
pkg("foo", "0.0.1");
pkg("bar", "0.2.1");
pkg("bar", "0.2.2");

assert_that(cargo_process("install").arg("--list"),
execs().with_status(0).with_stdout(""));
Expand All @@ -456,7 +465,7 @@ package id specification `foo` matched no packages
});

test!(uninstall_bin_does_not_exist {
r::mock_pkg("foo", "0.0.1", &[]);
pkg("foo", "0.0.1");

assert_that(cargo_process("install").arg("foo"),
execs().with_status(0));
Expand Down
Loading

0 comments on commit 4bb2668

Please sign in to comment.