Use the conflict tracking in the error messages to only show the versions that are in conflict

This commit is contained in:
Eh2406 2018-02-12 20:54:52 -05:00
parent 201fdba8f3
commit a028221478
2 changed files with 78 additions and 36 deletions

View 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())
});

View 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");