parent: Option<&Summary>,
candidate: Candidate,
method: &Method)
- -> CargoResult<Option<(DepsFrame, Duration)>> {
+ -> ActivateResult<Option<(DepsFrame, Duration)>> {
if let Some(parent) = parent {
cx.resolve_graph.push(GraphNode::Link(parent.package_id().clone(),
candidate.summary.package_id().clone()));
enum ConflictReason {
Semver,
Links(String),
+ MissingFeatures(String),
+}
+
+enum ActivateError {
+ Error(::failure::Error),
+ Conflict(PackageId, ConflictReason),
+}
+type ActivateResult<T> = Result<T, ActivateError>;
+
+impl From<::failure::Error> for ActivateError {
+ fn from(t: ::failure::Error) -> Self {
+ ActivateError::Error(t)
+ }
+}
+
+impl From<(PackageId, ConflictReason)> for ActivateError {
+ fn from(t: (PackageId, ConflictReason)) -> Self {
+ ActivateError::Conflict(t.0, t.1)
+ }
}
impl ConflictReason {
fn is_links(&self) -> bool {
- match *self {
- ConflictReason::Semver => false,
- ConflictReason::Links(_) => true,
+ if let ConflictReason::Links(_) = *self {
+ return true;
+ }
+ false
+ }
+
+ fn is_missing_features(&self) -> bool {
+ if let ConflictReason::MissingFeatures(_) = *self {
+ return true;
}
+ false
}
}
parent: Summary,
dep: Dependency,
features: Rc<Vec<String>>,
+ conflicting_activations: HashMap<PackageId, ConflictReason>,
}
#[derive(Clone)]
summary: summary.clone(),
replace: None,
};
- let res = activate(&mut cx, registry, None, candidate, method)?;
- if let Some((frame, _)) = res {
- remaining_deps.push(frame);
+ let res = activate(&mut cx, registry, None, candidate, method);
+ match res {
+ Ok(Some((frame, _))) => remaining_deps.push(frame),
+ Ok(None) => (),
+ Err(ActivateError::Error(e)) => return Err(e),
+ Err(ActivateError::Conflict(id, reason)) => {
+ match reason {
+ ConflictReason::MissingFeatures(features) => bail!("Package `{}` does not have these features: `{}`", id, features),
+ _ => panic!("bad error from activate"),
+ }
+ }
}
}
let mut remaining_candidates = RemainingCandidates::new(&candidates);
let mut successfully_activated = false;
+ let mut conflicting_activations = HashMap::new();
while !successfully_activated {
let next = remaining_candidates.next(cx.prev_active(&dep), &cx.links);
// This means that we're going to attempt to activate each candidate in
// turn. We could possibly fail to activate each candidate, so we try
// each one in turn.
- let (candidate, has_another) = next.or_else(|mut conflicting| {
+ let (candidate, has_another) = next.or_else(|conflicting| {
+ conflicting_activations.extend(conflicting);
// This dependency has no valid candidate. Backtrack until we
// find a dependency that does have a candidate to try, and try
// to activate that one. This resets the `remaining_deps` to
&mut dep,
&mut features,
&mut remaining_candidates,
- &mut conflicting,
+ &mut conflicting_activations,
).ok_or_else(|| {
+ // if we hit an activation error and we are out of other combinations
+ // then just report that error
activation_error(
&cx,
registry,
&parent,
&dep,
- &conflicting,
+ &conflicting_activations,
&candidates,
config,
)
parent: Summary::clone(&parent),
dep: Dependency::clone(&dep),
features: Rc::clone(&features),
+ conflicting_activations: conflicting_activations.clone(),
});
}
trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(), candidate.summary.version());
let res = activate(&mut cx, registry, Some(&parent), candidate, &method);
successfully_activated = res.is_ok();
- // TODO: disable fast-backtracking
- // TODO: save that disable status in backtrack frame
- // TODO: integrate with error messages
- if let Ok(Some((frame, dur))) = res {
- remaining_deps.push(frame);
- deps_time += dur;
+ match res {
+ Ok(Some((frame, dur))) => {
+ remaining_deps.push(frame);
+ deps_time += dur;
+ }
+ Ok(None) => (),
+ Err(ActivateError::Error(e)) => return Err(e),
+ Err(ActivateError::Conflict(id, reason)) => { conflicting_activations.insert(id, reason); },
}
}
}
*dep = frame.dep;
*features = frame.features;
*remaining_candidates = frame.remaining_candidates;
+ *conflicting_activations = frame.conflicting_activations;
return Some((candidate, has_another));
}
}
let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
conflicting_activations.sort_unstable();
- let (links_errors, other_errors): (Vec<_>, Vec<_>) = conflicting_activations.drain(..).rev().partition(|&(_, r)| r.is_links());
+ let (links_errors, mut other_errors): (Vec<_>, Vec<_>) = conflicting_activations.drain(..).rev().partition(|&(_, r)| r.is_links());
- for &(p, r) in &links_errors {
+ for &(p, r) in links_errors.iter() {
if let ConflictReason::Links(ref link) = *r {
msg.push_str("\n\nthe package `");
msg.push_str(dep.name());
msg.push_str(&describe_path(p));
}
+ let (features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors.drain(..).partition(|&(_, r)| r.is_missing_features());
+
+ for &(p, r) in features_errors.iter() {
+ if let ConflictReason::MissingFeatures(ref features) = *r {
+ msg.push_str("\n\nthe package `");
+ msg.push_str(dep.name());
+ msg.push_str("` depends on `");
+ msg.push_str(p.name());
+ msg.push_str("`, with features: `");
+ msg.push_str(features);
+ msg.push_str("` but it does not have these features.\n");
+ }
+ msg.push_str(&describe_path(p));
+ }
+
if links_errors.is_empty() {
msg.push_str("\n\nall possible versions conflict with \
previously selected packages.");
}
- for &(p, _) in &other_errors {
+ for &(p, _) in other_errors.iter() {
msg.push_str("\n\n previously selected ");
msg.push_str(&describe_path(p));
}
fn build_deps(&mut self,
registry: &mut Registry,
candidate: &Summary,
- method: &Method) -> CargoResult<Vec<DepInfo>> {
+ method: &Method) -> ActivateResult<Vec<DepInfo>> {
// First, figure out our set of dependencies based on the requested set
// of features. This also calculates what features we're going to enable
// for our own dependencies.
fn resolve_features<'b>(&mut self,
s: &'b Summary,
method: &'b Method)
- -> CargoResult<Vec<(Dependency, Vec<String>)>> {
+ -> ActivateResult<Vec<(Dependency, Vec<String>)>> {
let dev_deps = match *method {
Method::Everything => true,
Method::Required { dev_deps, .. } => dev_deps,
base.extend(dep.features().iter().cloned());
for feature in base.iter() {
if feature.contains('/') {
- bail!("feature names may not contain slashes: `{}`", feature);
+ return Err(format_err!("feature names may not contain slashes: `{}`", feature).into());
}
}
ret.push((dep.clone(), base));
.map(|s| &s[..])
.collect::<Vec<&str>>();
let features = unknown.join(", ");
- bail!("Package `{}` does not have these features: `{}`",
- s.package_id(), features)
+ return Err((s.package_id().clone(), ConflictReason::MissingFeatures(features)))?;
}
// Record what list of features is active for this package.