Print information about updated packages on cargo update
authorGeorge Hilliard <gh403@msstate.edu>
Sun, 17 May 2015 16:45:55 +0000 (18:45 +0200)
committerGeorge Hilliard <gh403@msstate.edu>
Fri, 28 Aug 2015 03:56:24 +0000 (22:56 -0500)
Closes #984.

src/cargo/ops/cargo_generate_lockfile.rs
tests/support/mod.rs
tests/test_cargo_compile_git_deps.rs
tests/test_cargo_registry.rs

index 534a7cdc5da14b9a5eb52fa4765e7c82afa248d9..f561664b876f9e62fb5737884f0f2dd76a921b64 100644 (file)
@@ -1,9 +1,9 @@
-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};
@@ -91,6 +91,33 @@ pub fn update_lockfile(manifest_path: &Path,
                                                   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(());
 
@@ -108,4 +135,46 @@ pub fn update_lockfile(manifest_path: &Path,
             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()
+    }
 }
index d07ef594720986bf2df743f3ffca9db70092102e..0f7602e99daa1b2692420253b061d627b0179751 100644 (file)
@@ -522,6 +522,8 @@ pub static RUNNING:     &'static str = "     Running";
 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";
index 834b294bb512e0a404bb396bedfc20883c17d2f2..3cf30a2f27edfeb3ef30f23aab8ceb78a0494f65 100644 (file)
@@ -539,9 +539,12 @@ test!(recompilation {
 
     // 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\
@@ -657,9 +660,12 @@ test!(update_with_shared_deps {
     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");
@@ -782,8 +788,12 @@ test!(two_deps_only_update_one {
     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(""));
 });
 
@@ -941,9 +951,12 @@ test!(dep_with_changed_submodule {
     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()
index 9f9426c7aa2927e39deab553ba44c8dbd0ef7771..fef4fa5008af84f2f5eebd8f2e5744d04e2b2c38 100644 (file)
@@ -3,7 +3,7 @@ use std::io::prelude::*;
 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;
@@ -476,6 +476,7 @@ test!(update_lockfile {
                  .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");
@@ -492,6 +493,7 @@ test!(update_lockfile {
                  .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");
@@ -502,6 +504,27 @@ test!(update_lockfile {
 {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 {
@@ -760,6 +783,7 @@ test!(update_transitive_dependency {
                 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"),