Skip to content

Commit 7d22a30

Browse files
committed
Add dep requirement info for dep-chain display
1 parent 8b5771f commit 7d22a30

File tree

3 files changed

+68
-23
lines changed

3 files changed

+68
-23
lines changed

src/cargo/core/resolver/dep_cache.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! This module impl that cache in all the gory details
1111
1212
use crate::core::resolver::context::Context;
13-
use crate::core::resolver::errors::describe_path;
13+
use crate::core::resolver::errors::describe_path_in_context;
1414
use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
1515
use crate::core::resolver::{
1616
ActivateError, ActivateResult, CliFeatures, RequestedFeatures, ResolveOpts, VersionOrdering,
@@ -216,7 +216,7 @@ impl<'a> RegistryQueryer<'a> {
216216
format!(
217217
"failed to get `{}` as a dependency of {}",
218218
dep.package_name(),
219-
describe_path(&cx.parents.path_to_bottom(&candidate.package_id())),
219+
describe_path_in_context(cx, &candidate.package_id()),
220220
)
221221
})?;
222222
Ok((dep, candidates, features))

src/cargo/core/resolver/errors.rs

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub(super) fn activation_error(
8282
cx.parents
8383
.path_to_bottom(&parent.package_id())
8484
.into_iter()
85+
.map(|(node, _)| node)
8586
.cloned()
8687
.collect(),
8788
)
@@ -90,9 +91,7 @@ pub(super) fn activation_error(
9091
if !candidates.is_empty() {
9192
let mut msg = format!("failed to select a version for `{}`.", dep.package_name());
9293
msg.push_str("\n ... required by ");
93-
msg.push_str(&describe_path(
94-
&cx.parents.path_to_bottom(&parent.package_id()),
95-
));
94+
msg.push_str(&describe_path_in_context(cx, &parent.package_id()));
9695

9796
msg.push_str("\nversions that meet the requirements `");
9897
msg.push_str(&dep.version_req().to_string());
@@ -128,7 +127,7 @@ pub(super) fn activation_error(
128127
msg.push_str("`, but it conflicts with a previous package which links to `");
129128
msg.push_str(link);
130129
msg.push_str("` as well:\n");
131-
msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
130+
msg.push_str(&describe_path_in_context(cx, p));
132131
msg.push_str("\nOnly one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. ");
133132
msg.push_str("Try to adjust your dependencies so that only one package uses the links ='");
134133
msg.push_str(&*dep.package_name());
@@ -197,7 +196,7 @@ pub(super) fn activation_error(
197196
for (p, r) in &conflicting_activations {
198197
if let ConflictReason::Semver = r {
199198
msg.push_str("\n\n previously selected ");
200-
msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
199+
msg.push_str(&describe_path_in_context(cx, p));
201200
}
202201
}
203202
}
@@ -250,9 +249,7 @@ pub(super) fn activation_error(
250249
registry.describe_source(dep.source_id()),
251250
);
252251
msg.push_str("required by ");
253-
msg.push_str(&describe_path(
254-
&cx.parents.path_to_bottom(&parent.package_id()),
255-
));
252+
msg.push_str(&describe_path_in_context(cx, &parent.package_id()));
256253

257254
// If we have a path dependency with a locked version, then this may
258255
// indicate that we updated a sub-package and forgot to run `cargo
@@ -330,9 +327,7 @@ pub(super) fn activation_error(
330327
}
331328
msg.push_str(&format!("location searched: {}\n", dep.source_id()));
332329
msg.push_str("required by ");
333-
msg.push_str(&describe_path(
334-
&cx.parents.path_to_bottom(&parent.package_id()),
335-
));
330+
msg.push_str(&describe_path_in_context(cx, &parent.package_id()));
336331

337332
msg
338333
};
@@ -351,12 +346,57 @@ pub(super) fn activation_error(
351346
to_resolve_err(anyhow::format_err!("{}", msg))
352347
}
353348

349+
/// Returns String representation of dependency chain for a particular `pkgid`
350+
/// within given context.
351+
pub(super) fn describe_path_in_context(cx: &Context, id: &PackageId) -> String {
352+
let iter = cx
353+
.parents
354+
.path_to_bottom(id)
355+
.into_iter()
356+
.map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
357+
describe_path(iter)
358+
}
359+
354360
/// Returns String representation of dependency chain for a particular `pkgid`.
355-
pub(super) fn describe_path(path: &[&PackageId]) -> String {
361+
///
362+
/// Note that all elements of `path` iterator should have `Some` dependency
363+
/// except the first one. It would look like:
364+
///
365+
/// (pkg0, None)
366+
/// -> (pkg1, dep from pkg1 satisfied by pkg0)
367+
/// -> (pkg2, dep from pkg2 satisfied by pkg1)
368+
/// -> ...
369+
pub(super) fn describe_path<'a>(
370+
mut path: impl Iterator<Item = (&'a PackageId, Option<&'a Dependency>)>,
371+
) -> String {
356372
use std::fmt::Write;
357-
let mut dep_path_desc = format!("package `{}`", path[0]);
358-
for dep in path[1..].iter() {
359-
write!(dep_path_desc, "\n ... which is depended on by `{}`", dep).unwrap();
373+
374+
if let Some(p) = path.next() {
375+
let mut dep_path_desc = format!("package `{}`", p.0);
376+
for (pkg, dep) in path {
377+
let dep = dep.unwrap();
378+
let source_kind = if dep.source_id().is_path() {
379+
"path "
380+
} else if dep.source_id().is_git() {
381+
"git "
382+
} else {
383+
""
384+
};
385+
let requirement = if source_kind.is_empty() {
386+
format!("{} = \"{}\"", dep.name_in_toml(), dep.version_req())
387+
} else {
388+
dep.name_in_toml().to_string()
389+
};
390+
write!(
391+
dep_path_desc,
392+
"\n ... which satisfies {}dependency `{}` of package `{}`",
393+
source_kind, requirement, pkg
394+
)
395+
.unwrap();
396+
}
397+
398+
return dep_path_desc;
360399
}
361-
dep_path_desc
400+
401+
String::new()
362402
}

src/cargo/util/graph.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,22 @@ impl<N: Eq + Ord + Clone, E: Default + Clone> Graph<N, E> {
8989

9090
/// Resolves one of the paths from the given dependent package down to
9191
/// a leaf.
92-
pub fn path_to_bottom<'a>(&'a self, mut pkg: &'a N) -> Vec<&'a N> {
93-
let mut result = vec![pkg];
92+
///
93+
/// Each element contains a node along with an edge except the first one.
94+
/// The representation would look like:
95+
///
96+
/// (Node0,) -> (Node1, Edge01) -> (Node2, Edge12)...
97+
pub fn path_to_bottom<'a>(&'a self, mut pkg: &'a N) -> Vec<(&'a N, Option<&'a E>)> {
98+
let mut result = vec![(pkg, None)];
9499
while let Some(p) = self.nodes.get(pkg).and_then(|p| {
95100
p.iter()
96101
// Note that we can have "cycles" introduced through dev-dependency
97102
// edges, so make sure we don't loop infinitely.
98-
.find(|(node, _)| !result.contains(node))
99-
.map(|(p, _)| p)
103+
.find(|&(node, _)| result.iter().all(|p| p.0 != node))
104+
.map(|(node, edge)| (node, Some(edge)))
100105
}) {
101106
result.push(p);
102-
pkg = p;
107+
pkg = p.0;
103108
}
104109
result
105110
}

0 commit comments

Comments
 (0)