As pointed out by Jacob, we can display the ordered cycle in the error message.
authorGiles Cope <gilescope@gmail.com>
Wed, 7 Mar 2018 22:56:47 +0000 (22:56 +0000)
committerGiles Cope <gilescope@gmail.com>
Wed, 7 Mar 2018 22:56:47 +0000 (22:56 +0000)
src/cargo/core/resolver/mod.rs
tests/testsuite/build.rs

index 234646b3a4a6f27b85885061aeec9bde5fe185bc..dfcdb71fb7124068504f640bdc633a01da9f4ffa 100644 (file)
@@ -985,6 +985,19 @@ fn find_candidate(
     None
 }
 
+/// Returns String representation of dependency chain for a particular `pkgid`.
+fn describe_path(graph: &Graph<PackageId>, pkgid: &PackageId) -> String {
+    use std::fmt::Write;
+    let dep_path = graph.path_to_top(pkgid);
+    let mut dep_path_desc = format!("package `{}`", dep_path[0]);
+    for dep in dep_path.iter().skip(1) {
+        write!(dep_path_desc,
+               "\n    ... which is depended on by `{}`",
+               dep).unwrap();
+    }
+    dep_path_desc
+}
+
 fn activation_error(cx: &Context,
                     registry: &mut Registry,
                     parent: &Summary,
@@ -993,21 +1006,10 @@ fn activation_error(cx: &Context,
                     candidates: &[Candidate],
                     config: Option<&Config>) -> CargoError {
     let graph = cx.graph();
-    let describe_path = |pkgid: &PackageId| -> String {
-        use std::fmt::Write;
-        let dep_path = graph.path_to_top(pkgid);
-        let mut dep_path_desc = format!("package `{}`", dep_path[0]);
-        for dep in dep_path.iter().skip(1) {
-            write!(dep_path_desc,
-                   "\n    ... which is depended on by `{}`",
-                   dep).unwrap();
-        }
-        dep_path_desc
-    };
     if !candidates.is_empty() {
         let mut msg = format!("failed to select a version for `{}`.", dep.name());
         msg.push_str("\n    ... required by ");
-        msg.push_str(&describe_path(parent.package_id()));
+        msg.push_str(&describe_path(&graph, parent.package_id()));
 
         msg.push_str("\nversions that meet the requirements `");
         msg.push_str(&dep.version_req().to_string());
@@ -1032,7 +1034,7 @@ fn activation_error(cx: &Context,
                 msg.push_str(link);
                 msg.push_str("` as well:\n");
             }
-            msg.push_str(&describe_path(p));
+            msg.push_str(&describe_path(&graph, p));
         }
 
         let (features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors.drain(..).partition(|&(_, r)| r.is_missing_features());
@@ -1059,7 +1061,7 @@ fn activation_error(cx: &Context,
 
         for &(p, _) in other_errors.iter() {
             msg.push_str("\n\n  previously selected ");
-            msg.push_str(&describe_path(p));
+            msg.push_str(&describe_path(&graph, p));
         }
 
         msg.push_str("\n\nfailed to select a version for `");
@@ -1108,7 +1110,7 @@ fn activation_error(cx: &Context,
                               dep.source_id(),
                               versions);
         msg.push_str("required by ");
-        msg.push_str(&describe_path(parent.package_id()));
+        msg.push_str(&describe_path(&graph, parent.package_id()));
 
         // If we have a path dependency with a locked version, then this may
         // indicate that we updated a sub-package and forgot to run `cargo
@@ -1125,7 +1127,7 @@ fn activation_error(cx: &Context,
                  location searched: {}\n",
                 dep.name(), dep.source_id());
         msg.push_str("required by ");
-        msg.push_str(&describe_path(parent.package_id()));
+        msg.push_str(&describe_path(&graph, parent.package_id()));
 
         msg
     };
@@ -1491,12 +1493,8 @@ fn check_cycles(resolve: &Resolve, activations: &Activations)
                  -> CargoResult<()> {
         // See if we visited ourselves
         if !visited.insert(id) {
-            let mut cycle = String::new();
-            for package_id in visited.iter() {
-                 cycle += &format!("\n    {}", package_id);
-            }
-            bail!("cyclic package dependency: package `{}` depends on itself. Cycle (not in order):{}",
-                  id, cycle);
+            bail!("cyclic package dependency: package `{}` depends on itself. Cycle:\n{}",
+                  id, describe_path(&resolve.graph, id));
         }
 
         // If we've already checked this node no need to recurse again as we'll
index f7f2d532bcebba3508041804ce497522a59b6029..9e5dead5e636c45c052ea2fa29aa73f88bfd5b83 100644 (file)
@@ -1532,9 +1532,8 @@ fn self_dependency() {
     assert_that(p.cargo("build"),
                 execs().with_status(101)
                        .with_stderr_contains("\
-[ERROR] cyclic package dependency: package `test v0.0.0 ([..])` depends on itself. Cycle (not in order):
-    test v0.0.0 ([..][/]foo)
-"));
+[ERROR] cyclic package dependency: package `test v0.0.0 ([..])` depends on itself. Cycle:
+package `test v0.0.0 ([..]foo)`"));
 }
 
 #[test]
@@ -2653,12 +2652,10 @@ fn cyclic_deps_rejected() {
 
     assert_that(p.cargo("build").arg("-v"),
                 execs().with_status(101)
-                       .with_stderr_contains("\
-[ERROR] cyclic package dependency: package `a v0.0.1 ([..]")
-                       .with_stderr_contains("[..]depends on itself[..]")
-                       .with_stderr_contains("    foo v0.0.1 ([..][/]foo)")
-                       .with_stderr_contains("    a v0.0.1 ([..][/]a)"));
-
+                       .with_stderr_contains(
+r#"[ERROR] cyclic package dependency: package `a v0.0.1 ([..])` depends on itself. Cycle:
+package `a v0.0.1 ([..]a)`
+    ... which is depended on by `foo v0.0.1 ([..]foo)`[..]"#));
 }
 
 #[test]