Closes #984.
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
use std::path::Path;
use core::PackageId;
use core::registry::PackageRegistry;
-use core::{Source, Resolve};
+use core::{Source, Resolve, SourceId};
use core::resolver::Method;
use ops;
use sources::{PathSource};
Method::Everything,
Some(&previous_resolve),
Some(&to_avoid)));
+
+ // Summarize what is changing for the user.
+ let print_change = |status: &str, msg: String| {
+ opts.config.shell().status(status, msg)
+ };
+ for (removed, added) in compare_dependency_graphs(&previous_resolve, &resolve) {
+ if removed.len() == 1 && added.len() == 1 {
+ if removed[0].source_id().is_git() {
+ try!(print_change("Updating", format!("{} -> #{}",
+ removed[0],
+ &added[0].source_id().precise().unwrap()[..8])));
+ } else {
+ try!(print_change("Updating", format!("{} -> v{}",
+ removed[0],
+ added[0].version())));
+ }
+ }
+ else {
+ for package in removed.iter() {
+ try!(print_change("Removing", format!("{}", package)));
+ }
+ for package in added.iter() {
+ try!(print_change("Adding", format!("{}", package)));
+ }
+ }
+ }
+
try!(ops::write_pkg_lockfile(&package, &resolve));
return Ok(());
None => {}
}
}
+
+ fn compare_dependency_graphs<'a>(previous_resolve: &'a Resolve,
+ resolve: &'a Resolve) ->
+ Vec<(Vec<&'a PackageId>, Vec<&'a PackageId>)> {
+ // Map (package name, package source) to (removed versions, added versions).
+ fn changes_key<'a>(dep: &'a PackageId) -> (&'a str, &'a SourceId) {
+ (dep.name(), dep.source_id())
+ }
+
+ fn vec_subtract<T>(a: &[T], b: &[T]) -> Vec<T>
+ where T: Ord + Clone {
+ let mut result = a.to_owned();
+ let mut b = b.to_owned();
+ b.sort();
+ result.retain(|x| b.binary_search(x).is_err());
+ result
+ }
+
+ let mut changes = HashMap::new();
+
+ for dep in previous_resolve.iter() {
+ changes.insert(changes_key(dep), (vec![dep], vec![]));
+ }
+ for dep in resolve.iter() {
+ let (_, ref mut added) = *changes.entry(changes_key(dep))
+ .or_insert_with(|| (vec![], vec![]));
+ added.push(dep);
+ }
+
+ for (_, v) in changes.iter_mut() {
+ let (ref mut old, ref mut new) = *v;
+ let removed = vec_subtract(old, new);
+ let added = vec_subtract(new, old);
+ *old = removed;
+ *new = added;
+ }
+
+ // Sort the packages by their names.
+ let mut packages: Vec<_> = changes.keys().map(|x| *x).collect();
+ packages.sort();
+ packages.iter().map(|k| changes[k].clone()).collect()
+ }
}
pub static COMPILING: &'static str = " Compiling";
pub static FRESH: &'static str = " Fresh";
pub static UPDATING: &'static str = " Updating";
+pub static ADDING: &'static str = " Adding";
+pub static REMOVING: &'static str = " Removing";
pub static DOCTEST: &'static str = " Doc-tests";
pub static PACKAGING: &'static str = " Packaging";
pub static DOWNLOADING: &'static str = " Downloading";
// Update the dependency and carry on!
assert_that(p.cargo("update"),
- execs().with_stdout(&format!("{} git repository `{}`",
+ execs().with_stdout(&format!("{} git repository `{}`\n\
+ {} bar v0.5.0 ([..]) -> #[..]\n\
+ ",
UPDATING,
- git_project.url())));
+ git_project.url(),
+ UPDATING)));
println!("going for the last compile");
assert_that(p.cargo("build"),
execs().with_stdout(&format!("{} bar v0.5.0 ({}#[..])\n\
assert_that(p.cargo("update")
.arg("-p").arg("dep1")
.arg("--aggressive"),
- execs().with_stdout(&format!("{} git repository `{}`",
+ execs().with_stdout(&format!("{} git repository `{}`\n\
+ {} bar v0.5.0 ([..]) -> #[..]\n\
+ ",
UPDATING,
- git_project.url())));
+ git_project.url(),
+ UPDATING)));
// Make sure we still only compile one version of the git repo
println!("build");
assert_that(project.cargo("update")
.arg("-p").arg("dep1"),
execs()
- .with_stdout(&format!("{} git repository `{}`\n",
- UPDATING, git1.url()))
+ .with_stdout(&format!("{} git repository `{}`\n\
+ {} dep1 v0.5.0 ([..]) -> #[..]\n\
+ ",
+ UPDATING,
+ git1.url(),
+ UPDATING))
.with_stderr(""));
});
assert_that(project.cargo("update").arg("-v"),
execs()
.with_stderr("")
- .with_stdout(&format!("{} git repository `{}`",
+ .with_stdout(&format!("{} git repository `{}`\n\
+ {} dep1 v0.5.0 ([..]) -> #[..]\n\
+ ",
UPDATING,
- git_project.url())));
+ git_project.url(),
+ UPDATING)));
println!("last run");
assert_that(project.cargo("run"), execs()
use cargo::util::process;
use support::{project, execs, cargo_dir};
-use support::{UPDATING, DOWNLOADING, COMPILING, PACKAGING, VERIFYING};
+use support::{UPDATING, DOWNLOADING, COMPILING, PACKAGING, VERIFYING, ADDING, REMOVING};
use support::paths::{self, CargoPathExt};
use support::registry as r;
use support::git;
.arg("-p").arg("bar").arg("--precise").arg("0.0.2"),
execs().with_status(0).with_stdout(&format!("\
{updating} registry `[..]`
+{updating} bar v0.0.1 (registry file://[..]) -> v0.0.2
", updating = UPDATING)));
println!("0.0.2 build");
.arg("-p").arg("bar"),
execs().with_status(0).with_stdout(&format!("\
{updating} registry `[..]`
+{updating} bar v0.0.2 (registry file://[..]) -> v0.0.3
", updating = UPDATING)));
println!("0.0.3 build");
{compiling} foo v0.0.1 ({dir})
", downloading = DOWNLOADING, compiling = COMPILING,
dir = p.url())));
+
+ println!("new dependencies update");
+ r::mock_pkg("bar", "0.0.4", &[("spam", "0.2.5", "")]);
+ r::mock_pkg("spam", "0.2.5", &[]);
+ assert_that(p.cargo("update")
+ .arg("-p").arg("bar"),
+ execs().with_status(0).with_stdout(&format!("\
+{updating} registry `[..]`
+{updating} bar v0.0.3 (registry file://[..]) -> v0.0.4
+{adding} spam v0.2.5 (registry file://[..])
+", updating = UPDATING, adding = ADDING)));
+
+ println!("new dependencies update");
+ r::mock_pkg("bar", "0.0.5", &[]);
+ assert_that(p.cargo("update")
+ .arg("-p").arg("bar"),
+ execs().with_status(0).with_stdout(&format!("\
+{updating} registry `[..]`
+{updating} bar v0.0.4 (registry file://[..]) -> v0.0.5
+{removing} spam v0.2.5 (registry file://[..])
+", updating = UPDATING, removing = REMOVING)));
});
test!(dev_dependency_not_used {
execs().with_status(0)
.with_stdout(format!("\
{updating} registry `[..]`
+{updating} b v0.1.0 (registry [..]) -> v0.1.1
", updating = UPDATING)));
assert_that(p.cargo("build"),