mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
fix the todo's
needs a test where we have an activation_error the then try activate something that dose not work and backtrack to where we had the activation_error then: - Hit fast backtracking that go past the crate with the missing features - Or give a bad error message that does not mention the activation_error. The test will pass, but there is code that is not yet justified by tests
This commit is contained in:
parent
3300728a75
commit
5d4402ce17
@ -414,7 +414,7 @@ fn activate(cx: &mut Context,
|
|||||||
parent: Option<&Summary>,
|
parent: Option<&Summary>,
|
||||||
candidate: Candidate,
|
candidate: Candidate,
|
||||||
method: &Method)
|
method: &Method)
|
||||||
-> CargoResult<Option<(DepsFrame, Duration)>> {
|
-> ActivateResult<Option<(DepsFrame, Duration)>> {
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
cx.resolve_graph.push(GraphNode::Link(parent.package_id().clone(),
|
cx.resolve_graph.push(GraphNode::Link(parent.package_id().clone(),
|
||||||
candidate.summary.package_id().clone()));
|
candidate.summary.package_id().clone()));
|
||||||
@ -536,14 +536,40 @@ impl Ord for DepsFrame {
|
|||||||
enum ConflictReason {
|
enum ConflictReason {
|
||||||
Semver,
|
Semver,
|
||||||
Links(String),
|
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 {
|
impl ConflictReason {
|
||||||
fn is_links(&self) -> bool {
|
fn is_links(&self) -> bool {
|
||||||
match *self {
|
if let ConflictReason::Links(_) = *self {
|
||||||
ConflictReason::Semver => false,
|
return true;
|
||||||
ConflictReason::Links(_) => true,
|
|
||||||
}
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_missing_features(&self) -> bool {
|
||||||
|
if let ConflictReason::MissingFeatures(_) = *self {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,6 +582,7 @@ struct BacktrackFrame<'a> {
|
|||||||
parent: Summary,
|
parent: Summary,
|
||||||
dep: Dependency,
|
dep: Dependency,
|
||||||
features: Rc<Vec<String>>,
|
features: Rc<Vec<String>>,
|
||||||
|
conflicting_activations: HashMap<PackageId, ConflictReason>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -652,9 +679,17 @@ fn activate_deps_loop<'a>(
|
|||||||
summary: summary.clone(),
|
summary: summary.clone(),
|
||||||
replace: None,
|
replace: None,
|
||||||
};
|
};
|
||||||
let res = activate(&mut cx, registry, None, candidate, method)?;
|
let res = activate(&mut cx, registry, None, candidate, method);
|
||||||
if let Some((frame, _)) = res {
|
match res {
|
||||||
remaining_deps.push(frame);
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,6 +748,7 @@ fn activate_deps_loop<'a>(
|
|||||||
|
|
||||||
let mut remaining_candidates = RemainingCandidates::new(&candidates);
|
let mut remaining_candidates = RemainingCandidates::new(&candidates);
|
||||||
let mut successfully_activated = false;
|
let mut successfully_activated = false;
|
||||||
|
let mut conflicting_activations = HashMap::new();
|
||||||
|
|
||||||
while !successfully_activated {
|
while !successfully_activated {
|
||||||
let next = remaining_candidates.next(cx.prev_active(&dep), &cx.links);
|
let next = remaining_candidates.next(cx.prev_active(&dep), &cx.links);
|
||||||
@ -729,7 +765,8 @@ fn activate_deps_loop<'a>(
|
|||||||
// This means that we're going to attempt to activate each candidate in
|
// 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
|
// turn. We could possibly fail to activate each candidate, so we try
|
||||||
// each one in turn.
|
// 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
|
// This dependency has no valid candidate. Backtrack until we
|
||||||
// find a dependency that does have a candidate to try, and try
|
// find a dependency that does have a candidate to try, and try
|
||||||
// to activate that one. This resets the `remaining_deps` to
|
// to activate that one. This resets the `remaining_deps` to
|
||||||
@ -744,14 +781,16 @@ fn activate_deps_loop<'a>(
|
|||||||
&mut dep,
|
&mut dep,
|
||||||
&mut features,
|
&mut features,
|
||||||
&mut remaining_candidates,
|
&mut remaining_candidates,
|
||||||
&mut conflicting,
|
&mut conflicting_activations,
|
||||||
).ok_or_else(|| {
|
).ok_or_else(|| {
|
||||||
|
// if we hit an activation error and we are out of other combinations
|
||||||
|
// then just report that error
|
||||||
activation_error(
|
activation_error(
|
||||||
&cx,
|
&cx,
|
||||||
registry,
|
registry,
|
||||||
&parent,
|
&parent,
|
||||||
&dep,
|
&dep,
|
||||||
&conflicting,
|
&conflicting_activations,
|
||||||
&candidates,
|
&candidates,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
@ -770,6 +809,7 @@ fn activate_deps_loop<'a>(
|
|||||||
parent: Summary::clone(&parent),
|
parent: Summary::clone(&parent),
|
||||||
dep: Dependency::clone(&dep),
|
dep: Dependency::clone(&dep),
|
||||||
features: Rc::clone(&features),
|
features: Rc::clone(&features),
|
||||||
|
conflicting_activations: conflicting_activations.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,13 +821,15 @@ fn activate_deps_loop<'a>(
|
|||||||
trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(), candidate.summary.version());
|
trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(), candidate.summary.version());
|
||||||
let res = activate(&mut cx, registry, Some(&parent), candidate, &method);
|
let res = activate(&mut cx, registry, Some(&parent), candidate, &method);
|
||||||
successfully_activated = res.is_ok();
|
successfully_activated = res.is_ok();
|
||||||
// TODO: disable fast-backtracking
|
match res {
|
||||||
// TODO: save that disable status in backtrack frame
|
Ok(Some((frame, dur))) => {
|
||||||
// TODO: integrate with error messages
|
|
||||||
if let Ok(Some((frame, dur))) = res {
|
|
||||||
remaining_deps.push(frame);
|
remaining_deps.push(frame);
|
||||||
deps_time += dur;
|
deps_time += dur;
|
||||||
}
|
}
|
||||||
|
Ok(None) => (),
|
||||||
|
Err(ActivateError::Error(e)) => return Err(e),
|
||||||
|
Err(ActivateError::Conflict(id, reason)) => { conflicting_activations.insert(id, reason); },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,6 +876,7 @@ fn find_candidate<'a>(
|
|||||||
*dep = frame.dep;
|
*dep = frame.dep;
|
||||||
*features = frame.features;
|
*features = frame.features;
|
||||||
*remaining_candidates = frame.remaining_candidates;
|
*remaining_candidates = frame.remaining_candidates;
|
||||||
|
*conflicting_activations = frame.conflicting_activations;
|
||||||
return Some((candidate, has_another));
|
return Some((candidate, has_another));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -875,9 +918,9 @@ fn activation_error(cx: &Context,
|
|||||||
|
|
||||||
let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
|
let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
|
||||||
conflicting_activations.sort_unstable();
|
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 {
|
if let ConflictReason::Links(ref link) = *r {
|
||||||
msg.push_str("\n\nthe package `");
|
msg.push_str("\n\nthe package `");
|
||||||
msg.push_str(dep.name());
|
msg.push_str(dep.name());
|
||||||
@ -890,12 +933,27 @@ fn activation_error(cx: &Context,
|
|||||||
msg.push_str(&describe_path(p));
|
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() {
|
if links_errors.is_empty() {
|
||||||
msg.push_str("\n\nall possible versions conflict with \
|
msg.push_str("\n\nall possible versions conflict with \
|
||||||
previously selected packages.");
|
previously selected packages.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for &(p, _) in &other_errors {
|
for &(p, _) in other_errors.iter() {
|
||||||
msg.push_str("\n\n previously selected ");
|
msg.push_str("\n\n previously selected ");
|
||||||
msg.push_str(&describe_path(p));
|
msg.push_str(&describe_path(p));
|
||||||
}
|
}
|
||||||
@ -1158,7 +1216,7 @@ impl<'a> Context<'a> {
|
|||||||
fn build_deps(&mut self,
|
fn build_deps(&mut self,
|
||||||
registry: &mut Registry,
|
registry: &mut Registry,
|
||||||
candidate: &Summary,
|
candidate: &Summary,
|
||||||
method: &Method) -> CargoResult<Vec<DepInfo>> {
|
method: &Method) -> ActivateResult<Vec<DepInfo>> {
|
||||||
// First, figure out our set of dependencies based on the requested set
|
// First, figure out our set of dependencies based on the requested set
|
||||||
// of features. This also calculates what features we're going to enable
|
// of features. This also calculates what features we're going to enable
|
||||||
// for our own dependencies.
|
// for our own dependencies.
|
||||||
@ -1275,7 +1333,7 @@ impl<'a> Context<'a> {
|
|||||||
fn resolve_features<'b>(&mut self,
|
fn resolve_features<'b>(&mut self,
|
||||||
s: &'b Summary,
|
s: &'b Summary,
|
||||||
method: &'b Method)
|
method: &'b Method)
|
||||||
-> CargoResult<Vec<(Dependency, Vec<String>)>> {
|
-> ActivateResult<Vec<(Dependency, Vec<String>)>> {
|
||||||
let dev_deps = match *method {
|
let dev_deps = match *method {
|
||||||
Method::Everything => true,
|
Method::Everything => true,
|
||||||
Method::Required { dev_deps, .. } => dev_deps,
|
Method::Required { dev_deps, .. } => dev_deps,
|
||||||
@ -1310,7 +1368,7 @@ impl<'a> Context<'a> {
|
|||||||
base.extend(dep.features().iter().cloned());
|
base.extend(dep.features().iter().cloned());
|
||||||
for feature in base.iter() {
|
for feature in base.iter() {
|
||||||
if feature.contains('/') {
|
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));
|
ret.push((dep.clone(), base));
|
||||||
@ -1324,8 +1382,7 @@ impl<'a> Context<'a> {
|
|||||||
.map(|s| &s[..])
|
.map(|s| &s[..])
|
||||||
.collect::<Vec<&str>>();
|
.collect::<Vec<&str>>();
|
||||||
let features = unknown.join(", ");
|
let features = unknown.join(", ");
|
||||||
bail!("Package `{}` does not have these features: `{}`",
|
return Err((s.package_id().clone(), ConflictReason::MissingFeatures(features)))?;
|
||||||
s.package_id(), features)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record what list of features is active for this package.
|
// Record what list of features is active for this package.
|
||||||
|
@ -109,8 +109,17 @@ fn invalid4() {
|
|||||||
|
|
||||||
assert_that(p.cargo("build"),
|
assert_that(p.cargo("build"),
|
||||||
execs().with_status(101).with_stderr("\
|
execs().with_status(101).with_stderr("\
|
||||||
[ERROR] Package `bar v0.0.1 ([..])` does not have these features: `bar`
|
error: failed to select a version for `bar`.
|
||||||
"));
|
... required by package `foo v0.0.1 ([..])`
|
||||||
|
versions that meet the requirements `*` are: 0.0.1
|
||||||
|
|
||||||
|
the package `bar` depends on `bar`, with features: `bar` but it does not have these features.
|
||||||
|
package `bar v0.0.1 ([..])`
|
||||||
|
... which is depended on by `foo v0.0.1 ([..])`
|
||||||
|
|
||||||
|
all possible versions conflict with previously selected packages.
|
||||||
|
|
||||||
|
failed to select a version for `bar` which could resolve this conflict"));
|
||||||
|
|
||||||
p.change_file("Cargo.toml", r#"
|
p.change_file("Cargo.toml", r#"
|
||||||
[project]
|
[project]
|
||||||
@ -121,8 +130,7 @@ fn invalid4() {
|
|||||||
|
|
||||||
assert_that(p.cargo("build").arg("--features").arg("test"),
|
assert_that(p.cargo("build").arg("--features").arg("test"),
|
||||||
execs().with_status(101).with_stderr("\
|
execs().with_status(101).with_stderr("\
|
||||||
[ERROR] Package `foo v0.0.1 ([..])` does not have these features: `test`
|
error: Package `foo v0.0.1 ([..])` does not have these features: `test`"));
|
||||||
"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1052,8 +1060,7 @@ fn dep_feature_in_cmd_line() {
|
|||||||
// Trying to enable features of transitive dependencies is an error
|
// Trying to enable features of transitive dependencies is an error
|
||||||
assert_that(p.cargo("build").arg("--features").arg("bar/some-feat"),
|
assert_that(p.cargo("build").arg("--features").arg("bar/some-feat"),
|
||||||
execs().with_status(101).with_stderr("\
|
execs().with_status(101).with_stderr("\
|
||||||
[ERROR] Package `foo v0.0.1 ([..])` does not have these features: `bar`
|
error: Package `foo v0.0.1 ([..])` does not have these features: `bar`"));
|
||||||
"));
|
|
||||||
|
|
||||||
// Hierarchical feature specification should still be disallowed
|
// Hierarchical feature specification should still be disallowed
|
||||||
assert_that(p.cargo("build").arg("--features").arg("derived/bar/some-feat"),
|
assert_that(p.cargo("build").arg("--features").arg("derived/bar/some-feat"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user