diff --git a/src/cargo/core/resolver/conflict_cache.rs b/src/cargo/core/resolver/conflict_cache.rs index 91b094accbf..38a0ded781a 100644 --- a/src/cargo/core/resolver/conflict_cache.rs +++ b/src/cargo/core/resolver/conflict_cache.rs @@ -1,7 +1,8 @@ use std::collections::{HashMap, HashSet}; use core::{Dependency, PackageId}; -use core::resolver::{ConflictReason, Context}; +use core::resolver::Context; +use super::types::ConflictReason; pub(super) struct ConflictCache { // `con_from_dep` is a cache of the reasons for each time we diff --git a/src/cargo/core/resolver/context.rs b/src/cargo/core/resolver/context.rs new file mode 100644 index 00000000000..eaf7036d97f --- /dev/null +++ b/src/cargo/core/resolver/context.rs @@ -0,0 +1,409 @@ +use std::collections::{HashMap, HashSet}; +use std::rc::Rc; + +use core::{Dependency, PackageId, SourceId, Summary}; +use core::interning::InternedString; +use util::Graph; +use util::CargoResult; + +use super::types::{ActivateResult, ConflictReason, DepInfo, GraphNode, Method, RcList}; +use super::types::RegistryQueryer; + +pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve}; +pub use super::encode::{Metadata, WorkspaceResolve}; +pub use super::resolve::Resolve; + +// A `Context` is basically a bunch of local resolution information which is +// kept around for all `BacktrackFrame` instances. As a result, this runs the +// risk of being cloned *a lot* so we want to make this as cheap to clone as +// possible. +#[derive(Clone)] +pub struct Context { + // TODO: Both this and the two maps below are super expensive to clone. We should + // switch to persistent hash maps if we can at some point or otherwise + // make these much cheaper to clone in general. + pub activations: Activations, + pub resolve_features: HashMap>, + pub links: HashMap, + + // These are two cheaply-cloneable lists (O(1) clone) which are effectively + // hash maps but are built up as "construction lists". We'll iterate these + // at the very end and actually construct the map that we're making. + pub resolve_graph: RcList, + pub resolve_replacements: RcList<(PackageId, PackageId)>, + + // These warnings are printed after resolution. + pub warnings: RcList, +} + +impl Context { + pub fn new() -> Context { + Context { + resolve_graph: RcList::new(), + resolve_features: HashMap::new(), + links: HashMap::new(), + resolve_replacements: RcList::new(), + activations: HashMap::new(), + warnings: RcList::new(), + } + } + + /// Activate this summary by inserting it into our list of known activations. + /// + /// Returns true if this summary with the given method is already activated. + pub fn flag_activated(&mut self, summary: &Summary, method: &Method) -> CargoResult { + let id = summary.package_id(); + let prev = self.activations + .entry((id.name(), id.source_id().clone())) + .or_insert_with(|| Rc::new(Vec::new())); + if !prev.iter().any(|c| c == summary) { + self.resolve_graph.push(GraphNode::Add(id.clone())); + if let Some(link) = summary.links() { + ensure!( + self.links.insert(link, id.clone()).is_none(), + "Attempting to resolve a with more then one crate with the links={}. \n\ + This will not build as is. Consider rebuilding the .lock file.", + &*link + ); + } + let mut inner: Vec<_> = (**prev).clone(); + inner.push(summary.clone()); + *prev = Rc::new(inner); + return Ok(false); + } + debug!("checking if {} is already activated", summary.package_id()); + let (features, use_default) = match *method { + Method::Everything + | Method::Required { + all_features: true, .. + } => return Ok(false), + Method::Required { + features, + uses_default_features, + .. + } => (features, uses_default_features), + }; + + let has_default_feature = summary.features().contains_key("default"); + Ok(match self.resolve_features.get(id) { + Some(prev) => { + features + .iter() + .all(|f| prev.contains(&InternedString::new(f))) + && (!use_default || prev.contains(&InternedString::new("default")) + || !has_default_feature) + } + None => features.is_empty() && (!use_default || !has_default_feature), + }) + } + + pub fn build_deps( + &mut self, + registry: &mut RegistryQueryer, + parent: Option<&Summary>, + candidate: &Summary, + method: &Method, + ) -> ActivateResult> { + // 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. + let deps = self.resolve_features(parent, candidate, method)?; + + // Next, transform all dependencies into a list of possible candidates + // which can satisfy that dependency. + let mut deps = deps.into_iter() + .map(|(dep, features)| { + let candidates = registry.query(&dep)?; + Ok((dep, candidates, Rc::new(features))) + }) + .collect::>>()?; + + // Attempt to resolve dependencies with fewer candidates before trying + // dependencies with more candidates. This way if the dependency with + // only one candidate can't be resolved we don't have to do a bunch of + // work before we figure that out. + deps.sort_by_key(|&(_, ref a, _)| a.len()); + + Ok(deps) + } + + pub fn prev_active(&self, dep: &Dependency) -> &[Summary] { + self.activations + .get(&(dep.name(), dep.source_id().clone())) + .map(|v| &v[..]) + .unwrap_or(&[]) + } + + fn is_active(&self, id: &PackageId) -> bool { + self.activations + .get(&(id.name(), id.source_id().clone())) + .map(|v| v.iter().any(|s| s.package_id() == id)) + .unwrap_or(false) + } + + /// checks whether all of `parent` and the keys of `conflicting activations` + /// are still active + pub fn is_conflicting( + &self, + parent: Option<&PackageId>, + conflicting_activations: &HashMap, + ) -> bool { + conflicting_activations + .keys() + .chain(parent) + .all(|id| self.is_active(id)) + } + + /// Return all dependencies and the features we want from them. + fn resolve_features<'b>( + &mut self, + parent: Option<&Summary>, + s: &'b Summary, + method: &'b Method, + ) -> ActivateResult)>> { + let dev_deps = match *method { + Method::Everything => true, + Method::Required { dev_deps, .. } => dev_deps, + }; + + // First, filter by dev-dependencies + let deps = s.dependencies(); + let deps = deps.iter().filter(|d| d.is_transitive() || dev_deps); + + let mut reqs = build_requirements(s, method)?; + let mut ret = Vec::new(); + + // Next, collect all actually enabled dependencies and their features. + for dep in deps { + // Skip optional dependencies, but not those enabled through a feature + if dep.is_optional() && !reqs.deps.contains_key(&*dep.name()) { + continue; + } + // So we want this dependency. Move the features we want from `feature_deps` + // to `ret`. + let base = reqs.deps.remove(&*dep.name()).unwrap_or((false, vec![])); + if !dep.is_optional() && base.0 { + self.warnings.push(format!( + "Package `{}` does not have feature `{}`. It has a required dependency \ + with that name, but only optional dependencies can be used as features. \ + This is currently a warning to ease the transition, but it will become an \ + error in the future.", + s.package_id(), + dep.name() + )); + } + let mut base = base.1; + base.extend(dep.features().iter().cloned()); + for feature in base.iter() { + if feature.contains('/') { + return Err( + format_err!("feature names may not contain slashes: `{}`", feature).into(), + ); + } + } + ret.push((dep.clone(), base)); + } + + // Any remaining entries in feature_deps are bugs in that the package does not actually + // have those dependencies. We classified them as dependencies in the first place + // because there is no such feature, either. + if !reqs.deps.is_empty() { + let unknown = reqs.deps.keys().map(|s| &s[..]).collect::>(); + let features = unknown.join(", "); + return Err(match parent { + None => format_err!( + "Package `{}` does not have these features: `{}`", + s.package_id(), + features + ).into(), + Some(p) => ( + p.package_id().clone(), + ConflictReason::MissingFeatures(features), + ).into(), + }); + } + + // Record what list of features is active for this package. + if !reqs.used.is_empty() { + let pkgid = s.package_id(); + + let set = self.resolve_features + .entry(pkgid.clone()) + .or_insert_with(HashSet::new); + for feature in reqs.used { + set.insert(InternedString::new(feature)); + } + } + + Ok(ret) + } + + pub fn resolve_replacements(&self) -> HashMap { + let mut replacements = HashMap::new(); + let mut cur = &self.resolve_replacements; + while let Some(ref node) = cur.head { + let (k, v) = node.0.clone(); + replacements.insert(k, v); + cur = &node.1; + } + replacements + } + + pub fn graph(&self) -> Graph { + let mut graph = Graph::new(); + let mut cur = &self.resolve_graph; + while let Some(ref node) = cur.head { + match node.0 { + GraphNode::Add(ref p) => graph.add(p.clone(), &[]), + GraphNode::Link(ref a, ref b) => graph.link(a.clone(), b.clone()), + } + cur = &node.1; + } + graph + } +} + +/// Takes requested features for a single package from the input Method and +/// recurses to find all requested features, dependencies and requested +/// dependency features in a Requirements object, returning it to the resolver. +fn build_requirements<'a, 'b: 'a>( + s: &'a Summary, + method: &'b Method, +) -> CargoResult> { + let mut reqs = Requirements::new(s); + match *method { + Method::Everything + | Method::Required { + all_features: true, .. + } => { + for key in s.features().keys() { + reqs.require_feature(key)?; + } + for dep in s.dependencies().iter().filter(|d| d.is_optional()) { + reqs.require_dependency(dep.name().to_inner()); + } + } + Method::Required { + features: requested_features, + .. + } => for feat in requested_features.iter() { + reqs.add_feature(feat)?; + }, + } + match *method { + Method::Everything + | Method::Required { + uses_default_features: true, + .. + } => { + if s.features().get("default").is_some() { + reqs.require_feature("default")?; + } + } + Method::Required { + uses_default_features: false, + .. + } => {} + } + Ok(reqs) +} + +struct Requirements<'a> { + summary: &'a Summary, + // The deps map is a mapping of package name to list of features enabled. + // Each package should be enabled, and each package should have the + // specified set of features enabled. The boolean indicates whether this + // package was specifically requested (rather than just requesting features + // *within* this package). + deps: HashMap<&'a str, (bool, Vec)>, + // The used features set is the set of features which this local package had + // enabled, which is later used when compiling to instruct the code what + // features were enabled. + used: HashSet<&'a str>, + visited: HashSet<&'a str>, +} + +impl<'r> Requirements<'r> { + fn new<'a>(summary: &'a Summary) -> Requirements<'a> { + Requirements { + summary, + deps: HashMap::new(), + used: HashSet::new(), + visited: HashSet::new(), + } + } + + fn require_crate_feature(&mut self, package: &'r str, feat: &'r str) { + self.used.insert(package); + self.deps + .entry(package) + .or_insert((false, Vec::new())) + .1 + .push(feat.to_string()); + } + + fn seen(&mut self, feat: &'r str) -> bool { + if self.visited.insert(feat) { + self.used.insert(feat); + false + } else { + true + } + } + + fn require_dependency(&mut self, pkg: &'r str) { + if self.seen(pkg) { + return; + } + self.deps.entry(pkg).or_insert((false, Vec::new())).0 = true; + } + + fn require_feature(&mut self, feat: &'r str) -> CargoResult<()> { + if self.seen(feat) { + return Ok(()); + } + for f in self.summary + .features() + .get(feat) + .expect("must be a valid feature") + { + if f == feat { + bail!( + "Cyclic feature dependency: feature `{}` depends on itself", + feat + ); + } + self.add_feature(f)?; + } + Ok(()) + } + + fn add_feature(&mut self, feat: &'r str) -> CargoResult<()> { + if feat.is_empty() { + return Ok(()); + } + + // If this feature is of the form `foo/bar`, then we just lookup package + // `foo` and enable its feature `bar`. Otherwise this feature is of the + // form `foo` and we need to recurse to enable the feature `foo` for our + // own package, which may end up enabling more features or just enabling + // a dependency. + let mut parts = feat.splitn(2, '/'); + let feat_or_package = parts.next().unwrap(); + match parts.next() { + Some(feat) => { + self.require_crate_feature(feat_or_package, feat); + } + None => { + if self.summary.features().contains_key(feat_or_package) { + self.require_feature(feat_or_package)?; + } else { + self.require_dependency(feat_or_package); + } + } + } + Ok(()) + } +} + +pub type Activations = HashMap<(InternedString, SourceId), Rc>>; diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index 78a9afea22c..2a88bc5a1f5 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -47,343 +47,34 @@ //! that we're implementing something that probably shouldn't be allocating all //! over the place. -use std::cmp::Ordering; use std::collections::{BTreeMap, BinaryHeap, HashMap, HashSet}; -use std::fmt; -use std::iter::FromIterator; use std::mem; -use std::ops::Range; use std::rc::Rc; use std::time::{Duration, Instant}; use semver; -use url::Url; -use core::{Dependency, PackageId, Registry, SourceId, Summary}; +use core::{Dependency, PackageId, Registry, Summary}; use core::PackageIdSpec; -use core::interning::InternedString; use util::config::Config; use util::Graph; use util::errors::{CargoError, CargoResult}; use util::profile; -use util::graph::{Edges, Nodes}; + +use self::context::{Activations, Context}; +use self::types::{ActivateError, ActivateResult, Candidate, ConflictReason, DepsFrame, GraphNode}; +use self::types::{RcVecIter, RegistryQueryer}; pub use self::encode::{EncodableDependency, EncodablePackageId, EncodableResolve}; pub use self::encode::{Metadata, WorkspaceResolve}; +pub use self::resolve::Resolve; +pub use self::types::Method; +mod context; mod encode; mod conflict_cache; - -/// Represents a fully resolved package dependency graph. Each node in the graph -/// is a package and edges represent dependencies between packages. -/// -/// Each instance of `Resolve` also understands the full set of features used -/// for each package. -#[derive(PartialEq)] -pub struct Resolve { - graph: Graph, - replacements: HashMap, - empty_features: HashSet, - features: HashMap>, - checksums: HashMap>, - metadata: Metadata, - unused_patches: Vec, -} - -pub struct Deps<'a> { - edges: Option>, - resolve: &'a Resolve, -} - -pub struct DepsNotReplaced<'a> { - edges: Option>, -} - -#[derive(Clone, Copy)] -pub enum Method<'a> { - Everything, // equivalent to Required { dev_deps: true, all_features: true, .. } - Required { - dev_deps: bool, - features: &'a [String], - all_features: bool, - uses_default_features: bool, - }, -} - -impl<'r> Method<'r> { - pub fn split_features(features: &[String]) -> Vec { - features - .iter() - .flat_map(|s| s.split_whitespace()) - .flat_map(|s| s.split(',')) - .filter(|s| !s.is_empty()) - .map(|s| s.to_string()) - .collect::>() - } -} - -// Information about the dependencies for a crate, a tuple of: -// -// (dependency info, candidates, features activated) -type DepInfo = (Dependency, Rc>, Rc>); - -#[derive(Clone)] -struct Candidate { - summary: Summary, - replace: Option, -} - -impl Resolve { - /// Resolves one of the paths from the given dependent package up to - /// the root. - pub fn path_to_top<'a>(&'a self, pkg: &'a PackageId) -> Vec<&'a PackageId> { - self.graph.path_to_top(pkg) - } - pub fn register_used_patches(&mut self, patches: &HashMap>) { - for summary in patches.values().flat_map(|v| v) { - if self.iter().any(|id| id == summary.package_id()) { - continue; - } - self.unused_patches.push(summary.package_id().clone()); - } - } - - pub fn merge_from(&mut self, previous: &Resolve) -> CargoResult<()> { - // Given a previous instance of resolve, it should be forbidden to ever - // have a checksums which *differ*. If the same package id has differing - // checksums, then something has gone wrong such as: - // - // * Something got seriously corrupted - // * A "mirror" isn't actually a mirror as some changes were made - // * A replacement source wasn't actually a replacment, some changes - // were made - // - // In all of these cases, we want to report an error to indicate that - // something is awry. Normal execution (esp just using crates.io) should - // never run into this. - for (id, cksum) in previous.checksums.iter() { - if let Some(mine) = self.checksums.get(id) { - if mine == cksum { - continue; - } - - // If the previous checksum wasn't calculated, the current - // checksum is `Some`. This may indicate that a source was - // erroneously replaced or was replaced with something that - // desires stronger checksum guarantees than can be afforded - // elsewhere. - if cksum.is_none() { - bail!( - "\ -checksum for `{}` was not previously calculated, but a checksum could now \ -be calculated - -this could be indicative of a few possible situations: - - * the source `{}` did not previously support checksums, - but was replaced with one that does - * newer Cargo implementations know how to checksum this source, but this - older implementation does not - * the lock file is corrupt -", - id, - id.source_id() - ) - - // If our checksum hasn't been calculated, then it could mean - // that future Cargo figured out how to checksum something or - // more realistically we were overridden with a source that does - // not have checksums. - } else if mine.is_none() { - bail!( - "\ -checksum for `{}` could not be calculated, but a checksum is listed in \ -the existing lock file - -this could be indicative of a few possible situations: - - * the source `{}` supports checksums, - but was replaced with one that doesn't - * the lock file is corrupt - -unable to verify that `{0}` is the same as when the lockfile was generated -", - id, - id.source_id() - ) - - // If the checksums aren't equal, and neither is None, then they - // must both be Some, in which case the checksum now differs. - // That's quite bad! - } else { - bail!( - "\ -checksum for `{}` changed between lock files - -this could be indicative of a few possible errors: - - * the lock file is corrupt - * a replacement source in use (e.g. a mirror) returned a different checksum - * the source itself may be corrupt in one way or another - -unable to verify that `{0}` is the same as when the lockfile was generated -", - id - ); - } - } - } - - // Be sure to just copy over any unknown metadata. - self.metadata = previous.metadata.clone(); - Ok(()) - } - - pub fn iter(&self) -> Nodes { - self.graph.iter() - } - - pub fn deps(&self, pkg: &PackageId) -> Deps { - Deps { - edges: self.graph.edges(pkg), - resolve: self, - } - } - - pub fn deps_not_replaced(&self, pkg: &PackageId) -> DepsNotReplaced { - DepsNotReplaced { - edges: self.graph.edges(pkg), - } - } - - pub fn replacement(&self, pkg: &PackageId) -> Option<&PackageId> { - self.replacements.get(pkg) - } - - pub fn replacements(&self) -> &HashMap { - &self.replacements - } - - pub fn features(&self, pkg: &PackageId) -> &HashSet { - self.features.get(pkg).unwrap_or(&self.empty_features) - } - - pub fn features_sorted(&self, pkg: &PackageId) -> Vec<&str> { - let mut v = Vec::from_iter(self.features(pkg).iter().map(|s| s.as_ref())); - v.sort(); - v - } - - pub fn query(&self, spec: &str) -> CargoResult<&PackageId> { - PackageIdSpec::query_str(spec, self.iter()) - } - - pub fn unused_patches(&self) -> &[PackageId] { - &self.unused_patches - } -} - -impl fmt::Debug for Resolve { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "graph: {:?}\n", self.graph)?; - write!(fmt, "\nfeatures: {{\n")?; - for (pkg, features) in &self.features { - write!(fmt, " {}: {:?}\n", pkg, features)?; - } - write!(fmt, "}}") - } -} - -impl<'a> Iterator for Deps<'a> { - type Item = &'a PackageId; - - fn next(&mut self) -> Option<&'a PackageId> { - self.edges - .as_mut() - .and_then(|e| e.next()) - .map(|id| self.resolve.replacement(id).unwrap_or(id)) - } -} - -impl<'a> Iterator for DepsNotReplaced<'a> { - type Item = &'a PackageId; - - fn next(&mut self) -> Option<&'a PackageId> { - self.edges.as_mut().and_then(|e| e.next()) - } -} - -struct RcList { - head: Option)>>, -} - -impl RcList { - fn new() -> RcList { - RcList { head: None } - } - - fn push(&mut self, data: T) { - let node = Rc::new(( - data, - RcList { - head: self.head.take(), - }, - )); - self.head = Some(node); - } -} - -// Not derived to avoid `T: Clone` -impl Clone for RcList { - fn clone(&self) -> RcList { - RcList { - head: self.head.clone(), - } - } -} - -// Avoid stack overflows on drop by turning recursion into a loop -impl Drop for RcList { - fn drop(&mut self) { - let mut cur = self.head.take(); - while let Some(head) = cur { - match Rc::try_unwrap(head) { - Ok((_data, mut next)) => cur = next.head.take(), - Err(_) => break, - } - } - } -} - -enum GraphNode { - Add(PackageId), - Link(PackageId, PackageId), -} - -// A `Context` is basically a bunch of local resolution information which is -// kept around for all `BacktrackFrame` instances. As a result, this runs the -// risk of being cloned *a lot* so we want to make this as cheap to clone as -// possible. -#[derive(Clone)] -struct Context { - // TODO: Both this and the two maps below are super expensive to clone. We should - // switch to persistent hash maps if we can at some point or otherwise - // make these much cheaper to clone in general. - activations: Activations, - resolve_features: HashMap>, - links: HashMap, - - // These are two cheaply-cloneable lists (O(1) clone) which are effectively - // hash maps but are built up as "construction lists". We'll iterate these - // at the very end and actually construct the map that we're making. - resolve_graph: RcList, - resolve_replacements: RcList<(PackageId, PackageId)>, - - // These warnings are printed after resolution. - warnings: RcList, -} - -type Activations = HashMap<(InternedString, SourceId), Rc>>; +mod resolve; +mod types; /// Builds the list of all packages required to build the first argument. /// @@ -422,14 +113,7 @@ pub fn resolve( config: Option<&Config>, print_warnings: bool, ) -> CargoResult { - let cx = Context { - resolve_graph: RcList::new(), - resolve_features: HashMap::new(), - links: HashMap::new(), - resolve_replacements: RcList::new(), - activations: HashMap::new(), - warnings: RcList::new(), - }; + let cx = Context::new(); let _p = profile::start("resolving"); let minimal_versions = match config { Some(config) => config.cli_unstable().minimal_versions, @@ -438,531 +122,44 @@ pub fn resolve( let mut registry = RegistryQueryer::new(registry, replacements, try_to_use, minimal_versions); let cx = activate_deps_loop(cx, &mut registry, summaries, config)?; - let mut resolve = Resolve { - graph: cx.graph(), - empty_features: HashSet::new(), - checksums: HashMap::new(), - metadata: BTreeMap::new(), - replacements: cx.resolve_replacements(), - features: cx.resolve_features - .iter() - .map(|(k, v)| (k.clone(), v.iter().map(|x| x.to_string()).collect())) - .collect(), - unused_patches: Vec::new(), - }; - - for summary in cx.activations.values().flat_map(|v| v.iter()) { - let cksum = summary.checksum().map(|s| s.to_string()); - resolve - .checksums - .insert(summary.package_id().clone(), cksum); - } - - check_cycles(&resolve, &cx.activations)?; - trace!("resolved: {:?}", resolve); - - // If we have a shell, emit warnings about required deps used as feature. - if let Some(config) = config { - if print_warnings { - let mut shell = config.shell(); - let mut warnings = &cx.warnings; - while let Some(ref head) = warnings.head { - shell.warn(&head.0)?; - warnings = &head.1; - } - } - } - - Ok(resolve) -} - -/// Attempts to activate the summary `candidate` in the context `cx`. -/// -/// This function will pull dependency summaries from the registry provided, and -/// the dependencies of the package will be determined by the `method` provided. -/// If `candidate` was activated, this function returns the dependency frame to -/// iterate through next. -fn activate( - cx: &mut Context, - registry: &mut RegistryQueryer, - parent: Option<&Summary>, - candidate: Candidate, - method: &Method, -) -> ActivateResult> { - if let Some(parent) = parent { - cx.resolve_graph.push(GraphNode::Link( - parent.package_id().clone(), - candidate.summary.package_id().clone(), - )); - } - - let activated = cx.flag_activated(&candidate.summary, method)?; - - let candidate = match candidate.replace { - Some(replace) => { - cx.resolve_replacements.push(( - candidate.summary.package_id().clone(), - replace.package_id().clone(), - )); - if cx.flag_activated(&replace, method)? && activated { - return Ok(None); - } - trace!( - "activating {} (replacing {})", - replace.package_id(), - candidate.summary.package_id() - ); - replace - } - None => { - if activated { - return Ok(None); - } - trace!("activating {}", candidate.summary.package_id()); - candidate.summary - } - }; - - let now = Instant::now(); - let deps = cx.build_deps(registry, parent, &candidate, method)?; - let frame = DepsFrame { - parent: candidate, - just_for_error_messages: false, - remaining_siblings: RcVecIter::new(Rc::new(deps)), - }; - Ok(Some((frame, now.elapsed()))) -} - -struct RcVecIter { - vec: Rc>, - rest: Range, -} - -impl RcVecIter { - fn new(vec: Rc>) -> RcVecIter { - RcVecIter { - rest: 0..vec.len(), - vec, - } - } -} - -// Not derived to avoid `T: Clone` -impl Clone for RcVecIter { - fn clone(&self) -> RcVecIter { - RcVecIter { - vec: self.vec.clone(), - rest: self.rest.clone(), - } - } -} - -impl Iterator for RcVecIter -where - T: Clone, -{ - type Item = (usize, T); - - fn next(&mut self) -> Option<(usize, T)> { - self.rest - .next() - .and_then(|i| self.vec.get(i).map(|val| (i, val.clone()))) - } - - fn size_hint(&self) -> (usize, Option) { - self.rest.size_hint() - } -} - -#[derive(Clone)] -struct DepsFrame { - parent: Summary, - just_for_error_messages: bool, - remaining_siblings: RcVecIter, -} - -impl DepsFrame { - /// Returns the least number of candidates that any of this frame's siblings - /// has. - /// - /// The `remaining_siblings` array is already sorted with the smallest - /// number of candidates at the front, so we just return the number of - /// candidates in that entry. - fn min_candidates(&self) -> usize { - self.remaining_siblings - .clone() - .next() - .map(|(_, (_, candidates, _))| candidates.len()) - .unwrap_or(0) - } - - fn flatten<'s>(&'s self) -> Box + 's> { - // TODO: with impl Trait the Box can be removed - Box::new( - self.remaining_siblings - .clone() - .map(move |(_, (d, _, _))| (self.parent.package_id(), d)), - ) - } -} - -impl PartialEq for DepsFrame { - fn eq(&self, other: &DepsFrame) -> bool { - self.just_for_error_messages == other.just_for_error_messages - && self.min_candidates() == other.min_candidates() - } -} - -impl Eq for DepsFrame {} - -impl PartialOrd for DepsFrame { - fn partial_cmp(&self, other: &DepsFrame) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for DepsFrame { - fn cmp(&self, other: &DepsFrame) -> Ordering { - self.just_for_error_messages - .cmp(&other.just_for_error_messages) - .then_with(|| - // the frame with the sibling that has the least number of candidates - // needs to get bubbled up to the top of the heap we use below, so - // reverse comparison here. - self.min_candidates().cmp(&other.min_candidates()).reverse()) - } -} - -/// All possible reasons that a package might fail to activate. -/// -/// We maintain a list of conflicts for error reporting as well as backtracking -/// purposes. Each reason here is why candidates may be rejected or why we may -/// fail to resolve a dependency. -#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)] -enum ConflictReason { - /// There was a semver conflict, for example we tried to activate a package - /// 1.0.2 but 1.1.0 was already activated (aka a compatible semver version - /// is already activated) - Semver, - - /// The `links` key is being violated. For example one crate in the - /// dependency graph has `links = "foo"` but this crate also had that, and - /// we're only allowed one per dependency graph. - Links(String), - - /// A dependency listed features that weren't actually available on the - /// candidate. For example we tried to activate feature `foo` but the - /// candidiate we're activating didn't actually have the feature `foo`. - MissingFeatures(String), -} - -enum ActivateError { - Fatal(CargoError), - Conflict(PackageId, ConflictReason), -} - -type ActivateResult = Result; - -impl From<::failure::Error> for ActivateError { - fn from(t: ::failure::Error) -> Self { - ActivateError::Fatal(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 { - if let ConflictReason::Links(_) = *self { - return true; - } - false - } - - fn is_missing_features(&self) -> bool { - if let ConflictReason::MissingFeatures(_) = *self { - return true; - } - false - } -} - -struct RegistryQueryer<'a> { - registry: &'a mut (Registry + 'a), - replacements: &'a [(PackageIdSpec, Dependency)], - try_to_use: &'a HashSet<&'a PackageId>, - // TODO: with nll the Rc can be removed - cache: HashMap>>, - // If set the list of dependency candidates will be sorted by minimal - // versions first. That allows `cargo update -Z minimal-versions` which will - // specify minimum depedency versions to be used. - minimal_versions: bool, -} - -impl<'a> RegistryQueryer<'a> { - fn new( - registry: &'a mut Registry, - replacements: &'a [(PackageIdSpec, Dependency)], - try_to_use: &'a HashSet<&'a PackageId>, - minimal_versions: bool, - ) -> Self { - RegistryQueryer { - registry, - replacements, - cache: HashMap::new(), - try_to_use, - minimal_versions, - } - } - - /// Queries the `registry` to return a list of candidates for `dep`. - /// - /// This method is the location where overrides are taken into account. If - /// any candidates are returned which match an override then the override is - /// applied by performing a second query for what the override should - /// return. - fn query(&mut self, dep: &Dependency) -> CargoResult>> { - if let Some(out) = self.cache.get(dep).cloned() { - return Ok(out); - } - - let mut ret = Vec::new(); - self.registry.query(dep, &mut |s| { - ret.push(Candidate { - summary: s, - replace: None, - }); - })?; - for candidate in ret.iter_mut() { - let summary = &candidate.summary; - - let mut potential_matches = self.replacements - .iter() - .filter(|&&(ref spec, _)| spec.matches(summary.package_id())); - - let &(ref spec, ref dep) = match potential_matches.next() { - None => continue, - Some(replacement) => replacement, - }; - debug!("found an override for {} {}", dep.name(), dep.version_req()); - - let mut summaries = self.registry.query_vec(dep)?.into_iter(); - let s = summaries.next().ok_or_else(|| { - format_err!( - "no matching package for override `{}` found\n\ - location searched: {}\n\ - version required: {}", - spec, - dep.source_id(), - dep.version_req() - ) - })?; - let summaries = summaries.collect::>(); - if !summaries.is_empty() { - let bullets = summaries - .iter() - .map(|s| format!(" * {}", s.package_id())) - .collect::>(); - bail!( - "the replacement specification `{}` matched \ - multiple packages:\n * {}\n{}", - spec, - s.package_id(), - bullets.join("\n") - ); - } - - // The dependency should be hard-coded to have the same name and an - // exact version requirement, so both of these assertions should - // never fail. - assert_eq!(s.version(), summary.version()); - assert_eq!(s.name(), summary.name()); - - let replace = if s.source_id() == summary.source_id() { - debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s); - None - } else { - Some(s) - }; - let matched_spec = spec.clone(); - - // Make sure no duplicates - if let Some(&(ref spec, _)) = potential_matches.next() { - bail!( - "overlapping replacement specifications found:\n\n \ - * {}\n * {}\n\nboth specifications match: {}", - matched_spec, - spec, - summary.package_id() - ); - } - - for dep in summary.dependencies() { - debug!("\t{} => {}", dep.name(), dep.version_req()); - } - - candidate.replace = replace; - } - - // When we attempt versions for a package we'll want to do so in a - // sorted fashion to pick the "best candidates" first. Currently we try - // prioritized summaries (those in `try_to_use`) and failing that we - // list everything from the maximum version to the lowest version. - ret.sort_unstable_by(|a, b| { - let a_in_previous = self.try_to_use.contains(a.summary.package_id()); - let b_in_previous = self.try_to_use.contains(b.summary.package_id()); - let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse(); - match previous_cmp { - Ordering::Equal => { - let cmp = a.summary.version().cmp(&b.summary.version()); - if self.minimal_versions == true { - // Lower version ordered first. - cmp - } else { - // Higher version ordered first. - cmp.reverse() - } - } - _ => previous_cmp, - } - }); - - let out = Rc::new(ret); - - self.cache.insert(dep.clone(), out.clone()); - - Ok(out) - } -} - -#[derive(Clone)] -struct BacktrackFrame { - cur: usize, - context_backup: Context, - deps_backup: BinaryHeap, - remaining_candidates: RemainingCandidates, - parent: Summary, - dep: Dependency, - features: Rc>, - conflicting_activations: HashMap, -} - -/// A helper "iterator" used to extract candidates within a current `Context` of -/// a dependency graph. -/// -/// This struct doesn't literally implement the `Iterator` trait (requires a few -/// more inputs) but in general acts like one. Each `RemainingCandidates` is -/// created with a list of candidates to choose from. When attempting to iterate -/// over the list of candidates only *valid* candidates are returned. Validity -/// is defined within a `Context`. -/// -/// Candidates passed to `new` may not be returned from `next` as they could be -/// filtered out, and if iteration stops a map of all packages which caused -/// filtered out candidates to be filtered out will be returned. -#[derive(Clone)] -struct RemainingCandidates { - remaining: RcVecIter, - // note: change to RcList or something if clone is to expensive - conflicting_prev_active: HashMap, - // This is a inlined peekable generator - has_another: Option, -} - -impl RemainingCandidates { - fn new(candidates: &Rc>) -> RemainingCandidates { - RemainingCandidates { - remaining: RcVecIter::new(Rc::clone(candidates)), - conflicting_prev_active: HashMap::new(), - has_another: None, - } - } - - /// Attempts to find another candidate to check from this list. - /// - /// This method will attempt to move this iterator forward, returning a - /// candidate that's possible to activate. The `cx` argument is the current - /// context which determines validity for candidates returned, and the `dep` - /// is the dependency listing that we're activating for. - /// - /// If successful a `(Candidate, bool)` pair will be returned. The - /// `Candidate` is the candidate to attempt to activate, and the `bool` is - /// an indicator of whether there are remaining candidates to try of if - /// we've reached the end of iteration. - /// - /// If we've reached the end of the iterator here then `Err` will be - /// returned. The error will contain a map of package id to conflict reason, - /// where each package id caused a candidate to be filtered out from the - /// original list for the reason listed. - fn next( - &mut self, - cx: &Context, - dep: &Dependency, - ) -> Result<(Candidate, bool), HashMap> { - let prev_active = cx.prev_active(dep); - - for (_, b) in self.remaining.by_ref() { - // The `links` key in the manifest dictates that there's only one - // package in a dependency graph, globally, with that particular - // `links` key. If this candidate links to something that's already - // linked to by a different package then we've gotta skip this. - if let Some(link) = b.summary.links() { - if let Some(a) = cx.links.get(&link) { - if a != b.summary.package_id() { - self.conflicting_prev_active - .entry(a.clone()) - .or_insert_with(|| ConflictReason::Links(link.to_string())); - continue; - } - } - } + let mut resolve = Resolve { + graph: cx.graph(), + empty_features: HashSet::new(), + checksums: HashMap::new(), + metadata: BTreeMap::new(), + replacements: cx.resolve_replacements(), + features: cx.resolve_features + .iter() + .map(|(k, v)| (k.clone(), v.iter().map(|x| x.to_string()).collect())) + .collect(), + unused_patches: Vec::new(), + }; - // Otherwise the condition for being a valid candidate relies on - // semver. Cargo dictates that you can't duplicate multiple - // semver-compatible versions of a crate. For example we can't - // simultaneously activate `foo 1.0.2` and `foo 1.2.0`. We can, - // however, activate `1.0.2` and `2.0.0`. - // - // Here we throw out our candidate if it's *compatible*, yet not - // equal, to all previously activated versions. - if let Some(a) = prev_active - .iter() - .find(|a| compatible(a.version(), b.summary.version())) - { - if *a != b.summary { - self.conflicting_prev_active - .entry(a.package_id().clone()) - .or_insert(ConflictReason::Semver); - continue; - } - } + for summary in cx.activations.values().flat_map(|v| v.iter()) { + let cksum = summary.checksum().map(|s| s.to_string()); + resolve + .checksums + .insert(summary.package_id().clone(), cksum); + } - // Well if we made it this far then we've got a valid dependency. We - // want this iterator to be inherently "peekable" so we don't - // necessarily return the item just yet. Instead we stash it away to - // get returned later, and if we replaced something then that was - // actually the candidate to try first so we return that. - if let Some(r) = mem::replace(&mut self.has_another, Some(b)) { - return Ok((r, true)); + check_cycles(&resolve, &cx.activations)?; + trace!("resolved: {:?}", resolve); + + // If we have a shell, emit warnings about required deps used as feature. + if let Some(config) = config { + if print_warnings { + let mut shell = config.shell(); + let mut warnings = &cx.warnings; + while let Some(ref head) = warnings.head { + shell.warn(&head.0)?; + warnings = &head.1; } } - - // Alright we've entirely exhausted our list of candidates. If we've got - // something stashed away return that here (also indicating that there's - // nothign else). If nothing is stashed away we return the list of all - // conflicting activations, if any. - // - // TODO: can the `conflicting_prev_active` clone be avoided here? should - // panic if this is called twice and an error is already returned - self.has_another - .take() - .map(|r| (r, false)) - .ok_or_else(|| self.conflicting_prev_active.clone()) } + + Ok(resolve) } + /// Recursively activates the dependencies for `top`, in depth-first order, /// backtracking across possible candidates for each dependency as necessary. /// @@ -1337,75 +534,278 @@ fn activate_deps_loop( backtrack_stack.clear(); } - // If we don't know for a fact that we'll fail or if we're - // just here for the error message then we push this frame - // onto our list of to-be-resolve, which will generate more - // work for us later on. - // - // Otherwise we're guaranteed to fail and were not here for - // error messages, so we skip work and don't push anything - // onto our stack. - frame.just_for_error_messages = has_past_conflicting_dep; - if !has_past_conflicting_dep || activate_for_error_message { - remaining_deps.push(frame); - true - } else { - trace!( - "{}[{}]>{} skipping {} ", - parent.name(), - cur, - dep.name(), - pid.version() - ); - false + // If we don't know for a fact that we'll fail or if we're + // just here for the error message then we push this frame + // onto our list of to-be-resolve, which will generate more + // work for us later on. + // + // Otherwise we're guaranteed to fail and were not here for + // error messages, so we skip work and don't push anything + // onto our stack. + frame.just_for_error_messages = has_past_conflicting_dep; + if !has_past_conflicting_dep || activate_for_error_message { + remaining_deps.push(frame); + true + } else { + trace!( + "{}[{}]>{} skipping {} ", + parent.name(), + cur, + dep.name(), + pid.version() + ); + false + } + } + + // This candidate's already activated, so there's no extra work + // for us to do. Let's keep going. + Ok(None) => true, + + // We failed with a super fatal error (like a network error), so + // bail out as quickly as possible as we can't reliably + // backtrack from errors like these + Err(ActivateError::Fatal(e)) => return Err(e), + + // We failed due to a bland conflict, bah! Record this in our + // frame's list of conflicting activations as to why this + // candidate failed, and then move on. + Err(ActivateError::Conflict(id, reason)) => { + conflicting_activations.insert(id, reason); + false + } + }; + + // If we've successfully activated then save off the backtrack frame + // if one was created, and otherwise break out of the inner + // activation loop as we're ready to move to the next dependency + if successfully_activated { + backtrack_stack.extend(backtrack); + break; + } + + // We've failed to activate this dependency, oh dear! Our call to + // `activate` above may have altered our `cx` local variable, so + // restore it back if we've got a backtrack frame. + // + // If we don't have a backtrack frame then we're just using the `cx` + // for error messages anyway so we can live with a little + // imprecision. + if let Some(b) = backtrack { + cx = b.context_backup; + } + } + + // Ok phew, that loop was a big one! If we've broken out then we've + // successfully activated a candidate. Our stacks are all in place that + // we're ready to move on to the next dependency that needs activation, + // so loop back to the top of the function here. + } + + Ok(cx) +} + +/// Attempts to activate the summary `candidate` in the context `cx`. +/// +/// This function will pull dependency summaries from the registry provided, and +/// the dependencies of the package will be determined by the `method` provided. +/// If `candidate` was activated, this function returns the dependency frame to +/// iterate through next. +fn activate( + cx: &mut Context, + registry: &mut RegistryQueryer, + parent: Option<&Summary>, + candidate: Candidate, + method: &Method, +) -> ActivateResult> { + if let Some(parent) = parent { + cx.resolve_graph.push(GraphNode::Link( + parent.package_id().clone(), + candidate.summary.package_id().clone(), + )); + } + + let activated = cx.flag_activated(&candidate.summary, method)?; + + let candidate = match candidate.replace { + Some(replace) => { + cx.resolve_replacements.push(( + candidate.summary.package_id().clone(), + replace.package_id().clone(), + )); + if cx.flag_activated(&replace, method)? && activated { + return Ok(None); + } + trace!( + "activating {} (replacing {})", + replace.package_id(), + candidate.summary.package_id() + ); + replace + } + None => { + if activated { + return Ok(None); + } + trace!("activating {}", candidate.summary.package_id()); + candidate.summary + } + }; + + let now = Instant::now(); + let deps = cx.build_deps(registry, parent, &candidate, method)?; + let frame = DepsFrame { + parent: candidate, + just_for_error_messages: false, + remaining_siblings: RcVecIter::new(Rc::new(deps)), + }; + Ok(Some((frame, now.elapsed()))) +} + +#[derive(Clone)] +struct BacktrackFrame { + cur: usize, + context_backup: Context, + deps_backup: BinaryHeap, + remaining_candidates: RemainingCandidates, + parent: Summary, + dep: Dependency, + features: Rc>, + conflicting_activations: HashMap, +} + +/// A helper "iterator" used to extract candidates within a current `Context` of +/// a dependency graph. +/// +/// This struct doesn't literally implement the `Iterator` trait (requires a few +/// more inputs) but in general acts like one. Each `RemainingCandidates` is +/// created with a list of candidates to choose from. When attempting to iterate +/// over the list of candidates only *valid* candidates are returned. Validity +/// is defined within a `Context`. +/// +/// Candidates passed to `new` may not be returned from `next` as they could be +/// filtered out, and if iteration stops a map of all packages which caused +/// filtered out candidates to be filtered out will be returned. +#[derive(Clone)] +struct RemainingCandidates { + remaining: RcVecIter, + // note: change to RcList or something if clone is to expensive + conflicting_prev_active: HashMap, + // This is a inlined peekable generator + has_another: Option, +} + +impl RemainingCandidates { + fn new(candidates: &Rc>) -> RemainingCandidates { + RemainingCandidates { + remaining: RcVecIter::new(Rc::clone(candidates)), + conflicting_prev_active: HashMap::new(), + has_another: None, + } + } + + /// Attempts to find another candidate to check from this list. + /// + /// This method will attempt to move this iterator forward, returning a + /// candidate that's possible to activate. The `cx` argument is the current + /// context which determines validity for candidates returned, and the `dep` + /// is the dependency listing that we're activating for. + /// + /// If successful a `(Candidate, bool)` pair will be returned. The + /// `Candidate` is the candidate to attempt to activate, and the `bool` is + /// an indicator of whether there are remaining candidates to try of if + /// we've reached the end of iteration. + /// + /// If we've reached the end of the iterator here then `Err` will be + /// returned. The error will contain a map of package id to conflict reason, + /// where each package id caused a candidate to be filtered out from the + /// original list for the reason listed. + fn next( + &mut self, + cx: &Context, + dep: &Dependency, + ) -> Result<(Candidate, bool), HashMap> { + let prev_active = cx.prev_active(dep); + + for (_, b) in self.remaining.by_ref() { + // The `links` key in the manifest dictates that there's only one + // package in a dependency graph, globally, with that particular + // `links` key. If this candidate links to something that's already + // linked to by a different package then we've gotta skip this. + if let Some(link) = b.summary.links() { + if let Some(a) = cx.links.get(&link) { + if a != b.summary.package_id() { + self.conflicting_prev_active + .entry(a.clone()) + .or_insert_with(|| ConflictReason::Links(link.to_string())); + continue; } } + } - // This candidate's already activated, so there's no extra work - // for us to do. Let's keep going. - Ok(None) => true, - - // We failed with a super fatal error (like a network error), so - // bail out as quickly as possible as we can't reliably - // backtrack from errors like these - Err(ActivateError::Fatal(e)) => return Err(e), - - // We failed due to a bland conflict, bah! Record this in our - // frame's list of conflicting activations as to why this - // candidate failed, and then move on. - Err(ActivateError::Conflict(id, reason)) => { - conflicting_activations.insert(id, reason); - false + // Otherwise the condition for being a valid candidate relies on + // semver. Cargo dictates that you can't duplicate multiple + // semver-compatible versions of a crate. For example we can't + // simultaneously activate `foo 1.0.2` and `foo 1.2.0`. We can, + // however, activate `1.0.2` and `2.0.0`. + // + // Here we throw out our candidate if it's *compatible*, yet not + // equal, to all previously activated versions. + if let Some(a) = prev_active + .iter() + .find(|a| compatible(a.version(), b.summary.version())) + { + if *a != b.summary { + self.conflicting_prev_active + .entry(a.package_id().clone()) + .or_insert(ConflictReason::Semver); + continue; } - }; - - // If we've successfully activated then save off the backtrack frame - // if one was created, and otherwise break out of the inner - // activation loop as we're ready to move to the next dependency - if successfully_activated { - backtrack_stack.extend(backtrack); - break; } - // We've failed to activate this dependency, oh dear! Our call to - // `activate` above may have altered our `cx` local variable, so - // restore it back if we've got a backtrack frame. - // - // If we don't have a backtrack frame then we're just using the `cx` - // for error messages anyway so we can live with a little - // imprecision. - if let Some(b) = backtrack { - cx = b.context_backup; + // Well if we made it this far then we've got a valid dependency. We + // want this iterator to be inherently "peekable" so we don't + // necessarily return the item just yet. Instead we stash it away to + // get returned later, and if we replaced something then that was + // actually the candidate to try first so we return that. + if let Some(r) = mem::replace(&mut self.has_another, Some(b)) { + return Ok((r, true)); } } - // Ok phew, that loop was a big one! If we've broken out then we've - // successfully activated a candidate. Our stacks are all in place that - // we're ready to move on to the next dependency that needs activation, - // so loop back to the top of the function here. + // Alright we've entirely exhausted our list of candidates. If we've got + // something stashed away return that here (also indicating that there's + // nothign else). If nothing is stashed away we return the list of all + // conflicting activations, if any. + // + // TODO: can the `conflicting_prev_active` clone be avoided here? should + // panic if this is called twice and an error is already returned + self.has_another + .take() + .map(|r| (r, false)) + .ok_or_else(|| self.conflicting_prev_active.clone()) } +} - Ok(cx) +// Returns if `a` and `b` are compatible in the semver sense. This is a +// commutative operation. +// +// Versions `a` and `b` are compatible if their left-most nonzero digit is the +// same. +fn compatible(a: &semver::Version, b: &semver::Version) -> bool { + if a.major != b.major { + return false; + } + if a.major != 0 { + return true; + } + if a.minor != b.minor { + return false; + } + if a.minor != 0 { + return true; + } + a.patch == b.patch } /// Looks through the states in `backtrack_stack` for dependencies with @@ -1451,17 +851,6 @@ fn find_candidate<'a>( None } -/// Returns String representation of dependency chain for a particular `pkgid`. -fn describe_path(graph: &Graph, 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, @@ -1626,384 +1015,15 @@ fn activation_error( format_err!("{}", msg) } -// Returns if `a` and `b` are compatible in the semver sense. This is a -// commutative operation. -// -// Versions `a` and `b` are compatible if their left-most nonzero digit is the -// same. -fn compatible(a: &semver::Version, b: &semver::Version) -> bool { - if a.major != b.major { - return false; - } - if a.major != 0 { - return true; - } - if a.minor != b.minor { - return false; - } - if a.minor != 0 { - return true; - } - a.patch == b.patch -} - -struct Requirements<'a> { - summary: &'a Summary, - // The deps map is a mapping of package name to list of features enabled. - // Each package should be enabled, and each package should have the - // specified set of features enabled. The boolean indicates whether this - // package was specifically requested (rather than just requesting features - // *within* this package). - deps: HashMap<&'a str, (bool, Vec)>, - // The used features set is the set of features which this local package had - // enabled, which is later used when compiling to instruct the code what - // features were enabled. - used: HashSet<&'a str>, - visited: HashSet<&'a str>, -} - -impl<'r> Requirements<'r> { - fn new<'a>(summary: &'a Summary) -> Requirements<'a> { - Requirements { - summary, - deps: HashMap::new(), - used: HashSet::new(), - visited: HashSet::new(), - } - } - - fn require_crate_feature(&mut self, package: &'r str, feat: &'r str) { - self.used.insert(package); - self.deps - .entry(package) - .or_insert((false, Vec::new())) - .1 - .push(feat.to_string()); - } - - fn seen(&mut self, feat: &'r str) -> bool { - if self.visited.insert(feat) { - self.used.insert(feat); - false - } else { - true - } - } - - fn require_dependency(&mut self, pkg: &'r str) { - if self.seen(pkg) { - return; - } - self.deps.entry(pkg).or_insert((false, Vec::new())).0 = true; - } - - fn require_feature(&mut self, feat: &'r str) -> CargoResult<()> { - if self.seen(feat) { - return Ok(()); - } - for f in self.summary - .features() - .get(feat) - .expect("must be a valid feature") - { - if f == feat { - bail!( - "Cyclic feature dependency: feature `{}` depends on itself", - feat - ); - } - self.add_feature(f)?; - } - Ok(()) - } - - fn add_feature(&mut self, feat: &'r str) -> CargoResult<()> { - if feat.is_empty() { - return Ok(()); - } - - // If this feature is of the form `foo/bar`, then we just lookup package - // `foo` and enable its feature `bar`. Otherwise this feature is of the - // form `foo` and we need to recurse to enable the feature `foo` for our - // own package, which may end up enabling more features or just enabling - // a dependency. - let mut parts = feat.splitn(2, '/'); - let feat_or_package = parts.next().unwrap(); - match parts.next() { - Some(feat) => { - self.require_crate_feature(feat_or_package, feat); - } - None => { - if self.summary.features().contains_key(feat_or_package) { - self.require_feature(feat_or_package)?; - } else { - self.require_dependency(feat_or_package); - } - } - } - Ok(()) - } -} - -/// Takes requested features for a single package from the input Method and -/// recurses to find all requested features, dependencies and requested -/// dependency features in a Requirements object, returning it to the resolver. -fn build_requirements<'a, 'b: 'a>( - s: &'a Summary, - method: &'b Method, -) -> CargoResult> { - let mut reqs = Requirements::new(s); - match *method { - Method::Everything - | Method::Required { - all_features: true, .. - } => { - for key in s.features().keys() { - reqs.require_feature(key)?; - } - for dep in s.dependencies().iter().filter(|d| d.is_optional()) { - reqs.require_dependency(dep.name().to_inner()); - } - } - Method::Required { - features: requested_features, - .. - } => for feat in requested_features.iter() { - reqs.add_feature(feat)?; - }, - } - match *method { - Method::Everything - | Method::Required { - uses_default_features: true, - .. - } => { - if s.features().get("default").is_some() { - reqs.require_feature("default")?; - } - } - Method::Required { - uses_default_features: false, - .. - } => {} - } - Ok(reqs) -} - -impl Context { - /// Activate this summary by inserting it into our list of known activations. - /// - /// Returns true if this summary with the given method is already activated. - fn flag_activated(&mut self, summary: &Summary, method: &Method) -> CargoResult { - let id = summary.package_id(); - let prev = self.activations - .entry((id.name(), id.source_id().clone())) - .or_insert_with(|| Rc::new(Vec::new())); - if !prev.iter().any(|c| c == summary) { - self.resolve_graph.push(GraphNode::Add(id.clone())); - if let Some(link) = summary.links() { - ensure!( - self.links.insert(link, id.clone()).is_none(), - "Attempting to resolve a with more then one crate with the links={}. \n\ - This will not build as is. Consider rebuilding the .lock file.", - &*link - ); - } - let mut inner: Vec<_> = (**prev).clone(); - inner.push(summary.clone()); - *prev = Rc::new(inner); - return Ok(false); - } - debug!("checking if {} is already activated", summary.package_id()); - let (features, use_default) = match *method { - Method::Everything - | Method::Required { - all_features: true, .. - } => return Ok(false), - Method::Required { - features, - uses_default_features, - .. - } => (features, uses_default_features), - }; - - let has_default_feature = summary.features().contains_key("default"); - Ok(match self.resolve_features.get(id) { - Some(prev) => { - features - .iter() - .all(|f| prev.contains(&InternedString::new(f))) - && (!use_default || prev.contains(&InternedString::new("default")) - || !has_default_feature) - } - None => features.is_empty() && (!use_default || !has_default_feature), - }) - } - - fn build_deps( - &mut self, - registry: &mut RegistryQueryer, - parent: Option<&Summary>, - candidate: &Summary, - method: &Method, - ) -> ActivateResult> { - // 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. - let deps = self.resolve_features(parent, candidate, method)?; - - // Next, transform all dependencies into a list of possible candidates - // which can satisfy that dependency. - let mut deps = deps.into_iter() - .map(|(dep, features)| { - let candidates = registry.query(&dep)?; - Ok((dep, candidates, Rc::new(features))) - }) - .collect::>>()?; - - // Attempt to resolve dependencies with fewer candidates before trying - // dependencies with more candidates. This way if the dependency with - // only one candidate can't be resolved we don't have to do a bunch of - // work before we figure that out. - deps.sort_by_key(|&(_, ref a, _)| a.len()); - - Ok(deps) - } - - fn prev_active(&self, dep: &Dependency) -> &[Summary] { - self.activations - .get(&(dep.name(), dep.source_id().clone())) - .map(|v| &v[..]) - .unwrap_or(&[]) - } - - fn is_active(&self, id: &PackageId) -> bool { - self.activations - .get(&(id.name(), id.source_id().clone())) - .map(|v| v.iter().any(|s| s.package_id() == id)) - .unwrap_or(false) - } - - /// checks whether all of `parent` and the keys of `conflicting activations` - /// are still active - fn is_conflicting( - &self, - parent: Option<&PackageId>, - conflicting_activations: &HashMap, - ) -> bool { - conflicting_activations - .keys() - .chain(parent) - .all(|id| self.is_active(id)) - } - - /// Return all dependencies and the features we want from them. - fn resolve_features<'b>( - &mut self, - parent: Option<&Summary>, - s: &'b Summary, - method: &'b Method, - ) -> ActivateResult)>> { - let dev_deps = match *method { - Method::Everything => true, - Method::Required { dev_deps, .. } => dev_deps, - }; - - // First, filter by dev-dependencies - let deps = s.dependencies(); - let deps = deps.iter().filter(|d| d.is_transitive() || dev_deps); - - let mut reqs = build_requirements(s, method)?; - let mut ret = Vec::new(); - - // Next, collect all actually enabled dependencies and their features. - for dep in deps { - // Skip optional dependencies, but not those enabled through a feature - if dep.is_optional() && !reqs.deps.contains_key(&*dep.name()) { - continue; - } - // So we want this dependency. Move the features we want from `feature_deps` - // to `ret`. - let base = reqs.deps.remove(&*dep.name()).unwrap_or((false, vec![])); - if !dep.is_optional() && base.0 { - self.warnings.push(format!( - "Package `{}` does not have feature `{}`. It has a required dependency \ - with that name, but only optional dependencies can be used as features. \ - This is currently a warning to ease the transition, but it will become an \ - error in the future.", - s.package_id(), - dep.name() - )); - } - let mut base = base.1; - base.extend(dep.features().iter().cloned()); - for feature in base.iter() { - if feature.contains('/') { - return Err( - format_err!("feature names may not contain slashes: `{}`", feature).into(), - ); - } - } - ret.push((dep.clone(), base)); - } - - // Any remaining entries in feature_deps are bugs in that the package does not actually - // have those dependencies. We classified them as dependencies in the first place - // because there is no such feature, either. - if !reqs.deps.is_empty() { - let unknown = reqs.deps.keys().map(|s| &s[..]).collect::>(); - let features = unknown.join(", "); - return Err(match parent { - None => format_err!( - "Package `{}` does not have these features: `{}`", - s.package_id(), - features - ).into(), - Some(p) => ( - p.package_id().clone(), - ConflictReason::MissingFeatures(features), - ).into(), - }); - } - - // Record what list of features is active for this package. - if !reqs.used.is_empty() { - let pkgid = s.package_id(); - - let set = self.resolve_features - .entry(pkgid.clone()) - .or_insert_with(HashSet::new); - for feature in reqs.used { - set.insert(InternedString::new(feature)); - } - } - - Ok(ret) - } - - fn resolve_replacements(&self) -> HashMap { - let mut replacements = HashMap::new(); - let mut cur = &self.resolve_replacements; - while let Some(ref node) = cur.head { - let (k, v) = node.0.clone(); - replacements.insert(k, v); - cur = &node.1; - } - replacements - } - - fn graph(&self) -> Graph { - let mut graph = Graph::new(); - let mut cur = &self.resolve_graph; - while let Some(ref node) = cur.head { - match node.0 { - GraphNode::Add(ref p) => graph.add(p.clone(), &[]), - GraphNode::Link(ref a, ref b) => graph.link(a.clone(), b.clone()), - } - cur = &node.1; - } - graph +/// Returns String representation of dependency chain for a particular `pkgid`. +fn describe_path(graph: &Graph, 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 check_cycles(resolve: &Resolve, activations: &Activations) -> CargoResult<()> { diff --git a/src/cargo/core/resolver/resolve.rs b/src/cargo/core/resolver/resolve.rs new file mode 100644 index 00000000000..08805eba2c2 --- /dev/null +++ b/src/cargo/core/resolver/resolve.rs @@ -0,0 +1,218 @@ +use std::collections::{HashMap, HashSet}; +use std::fmt; +use std::iter::FromIterator; + +use url::Url; + +use core::{PackageId, Summary}; +use core::PackageIdSpec; +use util::Graph; +use util::errors::CargoResult; +use util::graph::{Edges, Nodes}; + +use super::encode::Metadata; + +/// Represents a fully resolved package dependency graph. Each node in the graph +/// is a package and edges represent dependencies between packages. +/// +/// Each instance of `Resolve` also understands the full set of features used +/// for each package. +#[derive(PartialEq)] +pub struct Resolve { + pub graph: Graph, + pub replacements: HashMap, + pub empty_features: HashSet, + pub features: HashMap>, + pub checksums: HashMap>, + pub metadata: Metadata, + pub unused_patches: Vec, +} + +impl Resolve { + /// Resolves one of the paths from the given dependent package up to + /// the root. + pub fn path_to_top<'a>(&'a self, pkg: &'a PackageId) -> Vec<&'a PackageId> { + self.graph.path_to_top(pkg) + } + pub fn register_used_patches(&mut self, patches: &HashMap>) { + for summary in patches.values().flat_map(|v| v) { + if self.iter().any(|id| id == summary.package_id()) { + continue; + } + self.unused_patches.push(summary.package_id().clone()); + } + } + + pub fn merge_from(&mut self, previous: &Resolve) -> CargoResult<()> { + // Given a previous instance of resolve, it should be forbidden to ever + // have a checksums which *differ*. If the same package id has differing + // checksums, then something has gone wrong such as: + // + // * Something got seriously corrupted + // * A "mirror" isn't actually a mirror as some changes were made + // * A replacement source wasn't actually a replacment, some changes + // were made + // + // In all of these cases, we want to report an error to indicate that + // something is awry. Normal execution (esp just using crates.io) should + // never run into this. + for (id, cksum) in previous.checksums.iter() { + if let Some(mine) = self.checksums.get(id) { + if mine == cksum { + continue; + } + + // If the previous checksum wasn't calculated, the current + // checksum is `Some`. This may indicate that a source was + // erroneously replaced or was replaced with something that + // desires stronger checksum guarantees than can be afforded + // elsewhere. + if cksum.is_none() { + bail!( + "\ +checksum for `{}` was not previously calculated, but a checksum could now \ +be calculated + +this could be indicative of a few possible situations: + + * the source `{}` did not previously support checksums, + but was replaced with one that does + * newer Cargo implementations know how to checksum this source, but this + older implementation does not + * the lock file is corrupt +", + id, + id.source_id() + ) + + // If our checksum hasn't been calculated, then it could mean + // that future Cargo figured out how to checksum something or + // more realistically we were overridden with a source that does + // not have checksums. + } else if mine.is_none() { + bail!( + "\ +checksum for `{}` could not be calculated, but a checksum is listed in \ +the existing lock file + +this could be indicative of a few possible situations: + + * the source `{}` supports checksums, + but was replaced with one that doesn't + * the lock file is corrupt + +unable to verify that `{0}` is the same as when the lockfile was generated +", + id, + id.source_id() + ) + + // If the checksums aren't equal, and neither is None, then they + // must both be Some, in which case the checksum now differs. + // That's quite bad! + } else { + bail!( + "\ +checksum for `{}` changed between lock files + +this could be indicative of a few possible errors: + + * the lock file is corrupt + * a replacement source in use (e.g. a mirror) returned a different checksum + * the source itself may be corrupt in one way or another + +unable to verify that `{0}` is the same as when the lockfile was generated +", + id + ); + } + } + } + + // Be sure to just copy over any unknown metadata. + self.metadata = previous.metadata.clone(); + Ok(()) + } + + pub fn iter(&self) -> Nodes { + self.graph.iter() + } + + pub fn deps(&self, pkg: &PackageId) -> Deps { + Deps { + edges: self.graph.edges(pkg), + resolve: self, + } + } + + pub fn deps_not_replaced(&self, pkg: &PackageId) -> DepsNotReplaced { + DepsNotReplaced { + edges: self.graph.edges(pkg), + } + } + + pub fn replacement(&self, pkg: &PackageId) -> Option<&PackageId> { + self.replacements.get(pkg) + } + + pub fn replacements(&self) -> &HashMap { + &self.replacements + } + + pub fn features(&self, pkg: &PackageId) -> &HashSet { + self.features.get(pkg).unwrap_or(&self.empty_features) + } + + pub fn features_sorted(&self, pkg: &PackageId) -> Vec<&str> { + let mut v = Vec::from_iter(self.features(pkg).iter().map(|s| s.as_ref())); + v.sort(); + v + } + + pub fn query(&self, spec: &str) -> CargoResult<&PackageId> { + PackageIdSpec::query_str(spec, self.iter()) + } + + pub fn unused_patches(&self) -> &[PackageId] { + &self.unused_patches + } +} + +impl fmt::Debug for Resolve { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "graph: {:?}\n", self.graph)?; + write!(fmt, "\nfeatures: {{\n")?; + for (pkg, features) in &self.features { + write!(fmt, " {}: {:?}\n", pkg, features)?; + } + write!(fmt, "}}") + } +} + +pub struct Deps<'a> { + edges: Option>, + resolve: &'a Resolve, +} + +impl<'a> Iterator for Deps<'a> { + type Item = &'a PackageId; + + fn next(&mut self) -> Option<&'a PackageId> { + self.edges + .as_mut() + .and_then(|e| e.next()) + .map(|id| self.resolve.replacement(id).unwrap_or(id)) + } +} + +pub struct DepsNotReplaced<'a> { + edges: Option>, +} + +impl<'a> Iterator for DepsNotReplaced<'a> { + type Item = &'a PackageId; + + fn next(&mut self) -> Option<&'a PackageId> { + self.edges.as_mut().and_then(|e| e.next()) + } +} diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs new file mode 100644 index 00000000000..c21822b7b32 --- /dev/null +++ b/src/cargo/core/resolver/types.rs @@ -0,0 +1,394 @@ +use std::cmp::Ordering; +use std::collections::{HashMap, HashSet}; +use std::ops::Range; +use std::rc::Rc; + +use core::{Dependency, PackageId, PackageIdSpec, Registry, Summary}; +use util::{CargoError, CargoResult}; + +pub struct RegistryQueryer<'a> { + pub registry: &'a mut (Registry + 'a), + replacements: &'a [(PackageIdSpec, Dependency)], + try_to_use: &'a HashSet<&'a PackageId>, + // TODO: with nll the Rc can be removed + cache: HashMap>>, + // If set the list of dependency candidates will be sorted by minimal + // versions first. That allows `cargo update -Z minimal-versions` which will + // specify minimum depedency versions to be used. + minimal_versions: bool, +} + +impl<'a> RegistryQueryer<'a> { + pub fn new( + registry: &'a mut Registry, + replacements: &'a [(PackageIdSpec, Dependency)], + try_to_use: &'a HashSet<&'a PackageId>, + minimal_versions: bool, + ) -> Self { + RegistryQueryer { + registry, + replacements, + cache: HashMap::new(), + try_to_use, + minimal_versions, + } + } + + /// Queries the `registry` to return a list of candidates for `dep`. + /// + /// This method is the location where overrides are taken into account. If + /// any candidates are returned which match an override then the override is + /// applied by performing a second query for what the override should + /// return. + pub fn query(&mut self, dep: &Dependency) -> CargoResult>> { + if let Some(out) = self.cache.get(dep).cloned() { + return Ok(out); + } + + let mut ret = Vec::new(); + self.registry.query(dep, &mut |s| { + ret.push(Candidate { + summary: s, + replace: None, + }); + })?; + for candidate in ret.iter_mut() { + let summary = &candidate.summary; + + let mut potential_matches = self.replacements + .iter() + .filter(|&&(ref spec, _)| spec.matches(summary.package_id())); + + let &(ref spec, ref dep) = match potential_matches.next() { + None => continue, + Some(replacement) => replacement, + }; + debug!("found an override for {} {}", dep.name(), dep.version_req()); + + let mut summaries = self.registry.query_vec(dep)?.into_iter(); + let s = summaries.next().ok_or_else(|| { + format_err!( + "no matching package for override `{}` found\n\ + location searched: {}\n\ + version required: {}", + spec, + dep.source_id(), + dep.version_req() + ) + })?; + let summaries = summaries.collect::>(); + if !summaries.is_empty() { + let bullets = summaries + .iter() + .map(|s| format!(" * {}", s.package_id())) + .collect::>(); + bail!( + "the replacement specification `{}` matched \ + multiple packages:\n * {}\n{}", + spec, + s.package_id(), + bullets.join("\n") + ); + } + + // The dependency should be hard-coded to have the same name and an + // exact version requirement, so both of these assertions should + // never fail. + assert_eq!(s.version(), summary.version()); + assert_eq!(s.name(), summary.name()); + + let replace = if s.source_id() == summary.source_id() { + debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s); + None + } else { + Some(s) + }; + let matched_spec = spec.clone(); + + // Make sure no duplicates + if let Some(&(ref spec, _)) = potential_matches.next() { + bail!( + "overlapping replacement specifications found:\n\n \ + * {}\n * {}\n\nboth specifications match: {}", + matched_spec, + spec, + summary.package_id() + ); + } + + for dep in summary.dependencies() { + debug!("\t{} => {}", dep.name(), dep.version_req()); + } + + candidate.replace = replace; + } + + // When we attempt versions for a package we'll want to do so in a + // sorted fashion to pick the "best candidates" first. Currently we try + // prioritized summaries (those in `try_to_use`) and failing that we + // list everything from the maximum version to the lowest version. + ret.sort_unstable_by(|a, b| { + let a_in_previous = self.try_to_use.contains(a.summary.package_id()); + let b_in_previous = self.try_to_use.contains(b.summary.package_id()); + let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse(); + match previous_cmp { + Ordering::Equal => { + let cmp = a.summary.version().cmp(&b.summary.version()); + if self.minimal_versions == true { + // Lower version ordered first. + cmp + } else { + // Higher version ordered first. + cmp.reverse() + } + } + _ => previous_cmp, + } + }); + + let out = Rc::new(ret); + + self.cache.insert(dep.clone(), out.clone()); + + Ok(out) + } +} + +#[derive(Clone, Copy)] +pub enum Method<'a> { + Everything, // equivalent to Required { dev_deps: true, all_features: true, .. } + Required { + dev_deps: bool, + features: &'a [String], + all_features: bool, + uses_default_features: bool, + }, +} + +impl<'r> Method<'r> { + pub fn split_features(features: &[String]) -> Vec { + features + .iter() + .flat_map(|s| s.split_whitespace()) + .flat_map(|s| s.split(',')) + .filter(|s| !s.is_empty()) + .map(|s| s.to_string()) + .collect::>() + } +} + +#[derive(Clone)] +pub struct Candidate { + pub summary: Summary, + pub replace: Option, +} + +#[derive(Clone)] +pub struct DepsFrame { + pub parent: Summary, + pub just_for_error_messages: bool, + pub remaining_siblings: RcVecIter, +} + +impl DepsFrame { + /// Returns the least number of candidates that any of this frame's siblings + /// has. + /// + /// The `remaining_siblings` array is already sorted with the smallest + /// number of candidates at the front, so we just return the number of + /// candidates in that entry. + fn min_candidates(&self) -> usize { + self.remaining_siblings + .clone() + .next() + .map(|(_, (_, candidates, _))| candidates.len()) + .unwrap_or(0) + } + + pub fn flatten<'s>(&'s self) -> Box + 's> { + // TODO: with impl Trait the Box can be removed + Box::new( + self.remaining_siblings + .clone() + .map(move |(_, (d, _, _))| (self.parent.package_id(), d)), + ) + } +} + +impl PartialEq for DepsFrame { + fn eq(&self, other: &DepsFrame) -> bool { + self.just_for_error_messages == other.just_for_error_messages + && self.min_candidates() == other.min_candidates() + } +} + +impl Eq for DepsFrame {} + +impl PartialOrd for DepsFrame { + fn partial_cmp(&self, other: &DepsFrame) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for DepsFrame { + fn cmp(&self, other: &DepsFrame) -> Ordering { + self.just_for_error_messages + .cmp(&other.just_for_error_messages) + .then_with(|| + // the frame with the sibling that has the least number of candidates + // needs to get bubbled up to the top of the heap we use below, so + // reverse comparison here. + self.min_candidates().cmp(&other.min_candidates()).reverse()) + } +} + +// Information about the dependencies for a crate, a tuple of: +// +// (dependency info, candidates, features activated) +pub type DepInfo = (Dependency, Rc>, Rc>); + +pub type ActivateResult = Result; + +pub enum ActivateError { + Fatal(CargoError), + Conflict(PackageId, ConflictReason), +} + +impl From<::failure::Error> for ActivateError { + fn from(t: ::failure::Error) -> Self { + ActivateError::Fatal(t) + } +} + +impl From<(PackageId, ConflictReason)> for ActivateError { + fn from(t: (PackageId, ConflictReason)) -> Self { + ActivateError::Conflict(t.0, t.1) + } +} + +/// All possible reasons that a package might fail to activate. +/// +/// We maintain a list of conflicts for error reporting as well as backtracking +/// purposes. Each reason here is why candidates may be rejected or why we may +/// fail to resolve a dependency. +#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub enum ConflictReason { + /// There was a semver conflict, for example we tried to activate a package + /// 1.0.2 but 1.1.0 was already activated (aka a compatible semver version + /// is already activated) + Semver, + + /// The `links` key is being violated. For example one crate in the + /// dependency graph has `links = "foo"` but this crate also had that, and + /// we're only allowed one per dependency graph. + Links(String), + + /// A dependency listed features that weren't actually available on the + /// candidate. For example we tried to activate feature `foo` but the + /// candidiate we're activating didn't actually have the feature `foo`. + MissingFeatures(String), +} + +impl ConflictReason { + pub fn is_links(&self) -> bool { + if let ConflictReason::Links(_) = *self { + return true; + } + false + } + + pub fn is_missing_features(&self) -> bool { + if let ConflictReason::MissingFeatures(_) = *self { + return true; + } + false + } +} + +pub struct RcVecIter { + vec: Rc>, + rest: Range, +} + +impl RcVecIter { + pub fn new(vec: Rc>) -> RcVecIter { + RcVecIter { + rest: 0..vec.len(), + vec, + } + } +} + +// Not derived to avoid `T: Clone` +impl Clone for RcVecIter { + fn clone(&self) -> RcVecIter { + RcVecIter { + vec: self.vec.clone(), + rest: self.rest.clone(), + } + } +} + +impl Iterator for RcVecIter +where + T: Clone, +{ + type Item = (usize, T); + + fn next(&mut self) -> Option<(usize, T)> { + self.rest + .next() + .and_then(|i| self.vec.get(i).map(|val| (i, val.clone()))) + } + + fn size_hint(&self) -> (usize, Option) { + self.rest.size_hint() + } +} + +pub struct RcList { + pub head: Option)>>, +} + +impl RcList { + pub fn new() -> RcList { + RcList { head: None } + } + + pub fn push(&mut self, data: T) { + let node = Rc::new(( + data, + RcList { + head: self.head.take(), + }, + )); + self.head = Some(node); + } +} + +// Not derived to avoid `T: Clone` +impl Clone for RcList { + fn clone(&self) -> RcList { + RcList { + head: self.head.clone(), + } + } +} + +// Avoid stack overflows on drop by turning recursion into a loop +impl Drop for RcList { + fn drop(&mut self) { + let mut cur = self.head.take(); + while let Some(head) = cur { + match Rc::try_unwrap(head) { + Ok((_data, mut next)) => cur = next.head.take(), + Err(_) => break, + } + } + } +} + +pub enum GraphNode { + Add(PackageId), + Link(PackageId, PackageId), +}