Use the conflict tracking in the error messages to only show the versions that are...
authorEh2406 <YeomanYaacov@gmail.com>
Tue, 13 Feb 2018 01:54:52 +0000 (20:54 -0500)
committerEh2406 <YeomanYaacov@gmail.com>
Tue, 13 Feb 2018 03:26:55 +0000 (22:26 -0500)
src/cargo/core/resolver/mod.rs
tests/build.rs

index 3e983a23baab3124ba63f6b1525d43e4f31038ed..e354d6029c1685630caa7c744f3672105eaf5698 100644 (file)
@@ -718,7 +718,7 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
                                      &mut conflicting_activations) {
                     None => return Err(activation_error(&cx, registry, &parent,
                                                         &dep,
-                                                        cx.prev_active(&dep),
+                                                        conflicting_activations,
                                                         &candidates, config)),
                     Some(candidate) => candidate,
                 }
@@ -753,26 +753,31 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
 /// If the outcome could differ, resets `cx` and `remaining_deps` to that
 /// level and returns the next candidate.
 /// If all candidates have been exhausted, returns None.
-fn find_candidate<'a>(backtrack_stack: &mut Vec<BacktrackFrame<'a>>,
-                      cx: &mut Context<'a>,
-                      remaining_deps: &mut BinaryHeap<DepsFrame>,
-                      parent: &mut Summary,
-                      cur: &mut usize,
-                      dep: &mut Dependency,
-                      features: &mut Rc<Vec<String>>,
-                      conflicting_activations: &mut HashSet<PackageId>) -> Option<Candidate> {
+fn find_candidate<'a>(
+    backtrack_stack: &mut Vec<BacktrackFrame<'a>>,
+    cx: &mut Context<'a>,
+    remaining_deps: &mut BinaryHeap<DepsFrame>,
+    parent: &mut Summary,
+    cur: &mut usize,
+    dep: &mut Dependency,
+    features: &mut Rc<Vec<String>>,
+    conflicting_activations: &mut HashSet<PackageId>,
+) -> Option<Candidate> {
     while let Some(mut frame) = backtrack_stack.pop() {
-        conflicting_activations.insert(parent.package_id().clone());
         let (next, has_another) = {
             let prev_active = frame.context_backup.prev_active(&frame.dep);
-            (frame.remaining_candidates.next(prev_active),
-             frame.remaining_candidates.clone().next(prev_active).is_ok())
+            (
+                frame.remaining_candidates.next(prev_active),
+                frame.remaining_candidates.clone().next(prev_active).is_ok(),
+            )
         };
-        if conflicting_activations
-            .iter()
-            // note: a lot of redundant work in is_active for similar debs
-            .all(|con| frame.context_backup.is_active(con)) {
-            continue
+        if frame.context_backup.is_active(parent.package_id())
+           && conflicting_activations
+           .iter()
+           // note: a lot of redundant work in is_active for similar debs
+           .all(|con| frame.context_backup.is_active(con))
+        {
+            continue;
         }
         if let Ok(candidate) = next {
             *cur = frame.cur;
@@ -792,7 +797,7 @@ fn find_candidate<'a>(backtrack_stack: &mut Vec<BacktrackFrame<'a>>,
                 *features = frame.features;
                 *conflicting_activations = frame.conflicting_activations
             }
-            return Some(candidate)
+            return Some(candidate);
         }
     }
     None
@@ -802,7 +807,7 @@ fn activation_error(cx: &Context,
                     registry: &mut Registry,
                     parent: &Summary,
                     dep: &Dependency,
-                    prev_active: &[Summary],
+                    conflicting_activations: HashSet<PackageId>,
                     candidates: &[Candidate],
                     config: Option<&Config>) -> CargoError {
     let graph = cx.graph();
@@ -818,29 +823,31 @@ fn activation_error(cx: &Context,
         dep_path_desc
     };
     if !candidates.is_empty() {
-        let mut msg = format!("failed to select a version for `{0}`\n\
+        let mut msg = format!("failed to select a version for `{}`.\n\
                                all possible versions conflict with \
-                               previously selected versions of `{0}`\n",
+                               previously selected packages.\n",
                               dep.name());
         msg.push_str("required by ");
         msg.push_str(&describe_path(parent.package_id()));
-        for v in prev_active.iter() {
+        let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
+        conflicting_activations.sort_unstable();
+        for v in conflicting_activations.iter().rev() {
             msg.push_str("\n  previously selected ");
-            msg.push_str(&describe_path(v.package_id()));
+            msg.push_str(&describe_path(v));
         }
 
-        msg.push_str(&format!("\n  possible versions to select: {}",
-                              candidates.iter()
-                                        .map(|v| v.summary.version())
-                                        .map(|v| v.to_string())
-                                        .collect::<Vec<_>>()
-                                        .join(", ")));
+        msg.push_str("\n  possible versions to select: ");
+        msg.push_str(&candidates.iter()
+                                       .map(|v| v.summary.version())
+                                       .map(|v| v.to_string())
+                                       .collect::<Vec<_>>()
+                                       .join(", "));
 
         return format_err!("{}", msg)
     }
 
     // Once we're all the way down here, we're definitely lost in the
-    // weeds! We didn't actually use any candidates above, so we need to
+    // weeds! We didn't actually find any candidates, so we need to
     // give an error message that nothing was found.
     //
     // Note that we re-query the registry with a new dependency that
@@ -853,7 +860,7 @@ fn activation_error(cx: &Context,
         Ok(candidates) => candidates,
         Err(e) => return e,
     };
-    candidates.sort_by(|a, b| {
+    candidates.sort_unstable_by(|a, b| {
         b.version().cmp(a.version())
     });
 
index a39ea600505c23c51e882e373e7b94b8919d625d..7f3e887be49dcd332d425796a3732ea1e735e943 100644 (file)
@@ -1026,19 +1026,54 @@ fn incompatible_dependencies() {
     assert_that(p.cargo("build"),
         execs().with_status(101)
             .with_stderr_contains("\
-error: failed to select a version for `bad`
-all possible versions conflict with previously selected versions of `bad`
+error: failed to select a version for `bad`.
+all possible versions conflict with previously selected packages.
 required by package `baz v0.1.0`
     ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
-  previously selected package `bad v0.1.0`
-    ... which is depended on by `foo v0.1.0`
-    ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
   previously selected package `bad v1.0.0`
     ... which is depended on by `bar v0.1.0`
     ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
   possible versions to select: 1.0.2, 1.0.1"));
 }
 
+#[test]
+fn incompatible_dependencies_with_multi_semver() {
+    Package::new("bad", "1.0.0").publish();
+    Package::new("bad", "1.0.1").publish();
+    Package::new("bad", "2.0.0").publish();
+    Package::new("bad", "2.0.1").publish();
+    Package::new("bar", "0.1.0").dep("bad", "=1.0.0").publish();
+    Package::new("baz", "0.1.0").dep("bad", ">=2.0.1").publish();
+
+    let p = project("transitive_load_test")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "incompatible_dependencies"
+            version = "0.0.1"
+
+            [dependencies]
+            bar = "0.1.0"
+            baz = "0.1.0"
+            bad = ">=1.0.1, <=2.0.0"
+        "#)
+        .file("src/main.rs", "fn main(){}")
+        .build();
+
+    assert_that(p.cargo("build"),
+        execs().with_status(101)
+            .with_stderr_contains("\
+error: failed to select a version for `bad`.
+all possible versions conflict with previously selected packages.
+required by package `incompatible_dependencies v0.0.1 ([..])`
+  previously selected package `bad v2.0.1`
+    ... which is depended on by `baz v0.1.0`
+    ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
+  previously selected package `bad v1.0.0`
+    ... which is depended on by `bar v0.1.0`
+    ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
+  possible versions to select: 2.0.0, 1.0.1"));
+}
+
 #[test]
 fn compile_offline_while_transitive_dep_not_cached() {
     let bar = Package::new("bar", "1.0.0");