-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cargo install multiple crates #4216
Changes from 5 commits
66168ac
40226f5
f2084b7
497c297
a65fad0
daf4eab
aa201ab
f78fc7c
1800c43
1c7c88a
ce2d69d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,9 +4,10 @@ use std::env; | |
use std::ffi::OsString; | ||
use std::fs::{self, File}; | ||
use std::io::prelude::*; | ||
use std::io::SeekFrom; | ||
use std::io::{self, SeekFrom}; | ||
use std::path::{Path, PathBuf}; | ||
use std::sync::Arc; | ||
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; | ||
|
||
use semver::Version; | ||
use tempdir::TempDir; | ||
|
@@ -55,37 +56,75 @@ impl Drop for Transaction { | |
} | ||
|
||
pub fn install(root: Option<&str>, | ||
krates: Vec<&str>, | ||
source_id: &SourceId, | ||
vers: Option<&str>, | ||
opts: &ops::CompileOptions, | ||
force: bool) -> CargoResult<()> { | ||
let root = resolve_root(root, opts.config)?; | ||
let map = SourceConfigMap::new(opts.config)?; | ||
|
||
if krates.len() <= 1 { | ||
install_one(root, map, krates.into_iter().next(), source_id, vers, opts, force) | ||
} else { | ||
let mut success = vec![]; | ||
let mut errors = vec![]; | ||
for krate in krates { | ||
let root = root.clone(); | ||
let map = map.clone(); | ||
match install_one(root, map, Some(krate), source_id, vers, opts, force) { | ||
Ok(()) => success.push(krate), | ||
Err(e) => errors.push(format!("{}: {}", krate, e)) | ||
} | ||
} | ||
|
||
writeln!(io::stderr(), | ||
"\n\nSUMMARY\n\nSuccessfully installed: {}\n\nErrors:\n\t{}", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this use the standard shell methods for reporting what happened here? Also can the whitespace be trimmed down as it seems like it's quite a bit? |
||
success.join(", "), | ||
errors.join("\n\t"))?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
fn install_one(root: Filesystem, | ||
map: SourceConfigMap, | ||
krate: Option<&str>, | ||
source_id: &SourceId, | ||
vers: Option<&str>, | ||
opts: &ops::CompileOptions, | ||
force: bool) -> CargoResult<()> { | ||
|
||
static ALREADY_UPDATED: AtomicBool = ATOMIC_BOOL_INIT; | ||
let needs_update = !ALREADY_UPDATED.load(Ordering::SeqCst); | ||
ALREADY_UPDATED.store(true, Ordering::SeqCst); | ||
|
||
let config = opts.config; | ||
let root = resolve_root(root, config)?; | ||
let map = SourceConfigMap::new(config)?; | ||
|
||
let (pkg, source) = if source_id.is_git() { | ||
select_pkg(GitSource::new(source_id, config), | ||
krate, vers, config, &mut |git| git.read_packages())? | ||
krate, vers, config, needs_update, | ||
&mut |git| git.read_packages())? | ||
} else if source_id.is_path() { | ||
let path = source_id.url().to_file_path().ok() | ||
.expect("path sources must have a valid path"); | ||
let path = source_id.url().to_file_path() | ||
.map_err(|()| CargoError::from("path sources must have a valid path"))?; | ||
let mut src = PathSource::new(&path, source_id, config); | ||
src.update().chain_err(|| { | ||
format!("`{}` is not a crate root; specify a crate to \ | ||
install from crates.io, or use --path or --git to \ | ||
specify an alternate source", path.display()) | ||
})?; | ||
select_pkg(PathSource::new(&path, source_id, config), | ||
krate, vers, config, &mut |path| path.read_packages())? | ||
krate, vers, config, needs_update, | ||
&mut |path| path.read_packages())? | ||
} else { | ||
select_pkg(map.load(source_id)?, | ||
krate, vers, config, | ||
krate, vers, config, needs_update, | ||
&mut |_| Err("must specify a crate to install from \ | ||
crates.io, or use --path or --git to \ | ||
specify alternate source".into()))? | ||
}; | ||
|
||
|
||
let mut td_opt = None; | ||
let overidden_target_dir = if source_id.is_path() { | ||
None | ||
|
@@ -267,11 +306,15 @@ fn select_pkg<'a, T>(mut source: T, | |
name: Option<&str>, | ||
vers: Option<&str>, | ||
config: &Config, | ||
needs_update: bool, | ||
list_all: &mut FnMut(&mut T) -> CargoResult<Vec<Package>>) | ||
-> CargoResult<(Package, Box<Source + 'a>)> | ||
where T: Source + 'a | ||
{ | ||
source.update()?; | ||
if needs_update { | ||
source.update()?; | ||
} | ||
|
||
match name { | ||
Some(name) => { | ||
let vers = match vers { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,53 @@ warning: be sure to add `[..]` to your PATH to be able to run the installed bina | |
assert_that(cargo_home(), is_not(has_installed_exe("foo"))); | ||
} | ||
|
||
#[test] | ||
fn multiple_pkgs() { | ||
pkg("foo", "0.0.1"); | ||
pkg("bar", "0.0.1"); | ||
|
||
assert_that(cargo_process("install").arg("foo").arg("bar"), | ||
execs().with_status(0).with_stderr(&format!("\ | ||
[UPDATING] registry `[..]` | ||
[DOWNLOADING] foo v0.0.1 (registry file://[..]) | ||
[INSTALLING] foo v0.0.1 | ||
[COMPILING] foo v0.0.1 | ||
[FINISHED] release [optimized] target(s) in [..] | ||
[INSTALLING] {home}[..]bin[..]foo[..] | ||
warning: be sure to add `[..]` to your PATH to be able to run the installed binaries | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this warning be printed out only once? |
||
[DOWNLOADING] bar v0.0.1 (registry file://[..]) | ||
[INSTALLING] bar v0.0.1 | ||
[COMPILING] bar v0.0.1 | ||
[FINISHED] release [optimized] target(s) in [..] | ||
[INSTALLING] {home}[..]bin[..]bar[..] | ||
warning: be sure to add `[..]` to your PATH to be able to run the installed binaries | ||
|
||
|
||
SUMMARY | ||
|
||
Successfully installed: foo, bar | ||
|
||
Errors: | ||
<tab> | ||
", | ||
home = cargo_home().display()))); | ||
assert_that(cargo_home(), has_installed_exe("foo")); | ||
assert_that(cargo_home(), has_installed_exe("bar")); | ||
|
||
assert_that(cargo_process("uninstall").arg("foo"), | ||
execs().with_status(0).with_stderr(&format!("\ | ||
[REMOVING] {home}[..]bin[..]foo[..] | ||
", | ||
home = cargo_home().display()))); | ||
assert_that(cargo_process("uninstall").arg("bar"), | ||
execs().with_status(0).with_stderr(&format!("\ | ||
[REMOVING] {home}[..]bin[..]bar[..] | ||
", | ||
home = cargo_home().display()))); | ||
assert_that(cargo_home(), is_not(has_installed_exe("foo"))); | ||
assert_that(cargo_home(), is_not(has_installed_exe("bar"))); | ||
} | ||
|
||
#[test] | ||
fn pick_max_version() { | ||
pkg("foo", "0.0.1"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've got standard error handling mechanisms which print contextual information, and this loses the error backtrace. Can this just be propagated outwards?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically I didn't want errors to be lost in a sea of command-line output, so I thought it'd be better to collect them at the end.