blob: dd86170035e119d5c4c6554c60cf84437f8801f7 [file] [log] [blame]
//! There are 2 sources of facts for the resolver:
//!
//! - The `Registry` tells us for a `Dependency` what versions are available to fulfil it.
//! - The `Summary` tells us for a version (and features) what dependencies need to be fulfilled for it to be activated.
//!
//! These constitute immutable facts, the soled ground truth that all other inference depends on.
//! Theoretically this could all be enumerated ahead of time, but we want to be lazy and only
//! look up things we need to. The compromise is to cache the results as they are computed.
//!
//! This module impl that cache in all the gory details
use crate::core::resolver::context::Context;
use crate::core::resolver::errors::describe_path_in_context;
use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
use crate::core::resolver::{
ActivateError, ActivateResult, CliFeatures, RequestedFeatures, ResolveOpts, VersionOrdering,
VersionPreferences,
};
use crate::core::{
Dependency, FeatureValue, PackageId, PackageIdSpec, QueryKind, Registry, Summary,
};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use anyhow::Context as _;
use log::debug;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::rc::Rc;
use std::task::Poll;
pub struct RegistryQueryer<'a> {
pub registry: &'a mut (dyn Registry + 'a),
replacements: &'a [(PackageIdSpec, Dependency)],
version_prefs: &'a VersionPreferences,
/// 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 dependency versions to be used.
minimal_versions: bool,
/// a cache of `Candidate`s that fulfil a `Dependency`
registry_cache: HashMap<Dependency, Poll<Rc<Vec<Summary>>>>,
/// a cache of `Dependency`s that are required for a `Summary`
summary_cache: HashMap<
(Option<PackageId>, Summary, ResolveOpts),
(Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>, bool),
>,
/// all the cases we ended up using a supplied replacement
used_replacements: HashMap<PackageId, Summary>,
}
impl<'a> RegistryQueryer<'a> {
pub fn new(
registry: &'a mut dyn Registry,
replacements: &'a [(PackageIdSpec, Dependency)],
version_prefs: &'a VersionPreferences,
minimal_versions: bool,
) -> Self {
RegistryQueryer {
registry,
replacements,
version_prefs,
minimal_versions,
registry_cache: HashMap::new(),
summary_cache: HashMap::new(),
used_replacements: HashMap::new(),
}
}
pub fn reset_pending(&mut self) -> bool {
let mut all_ready = true;
self.registry_cache.retain(|_, r| {
if !r.is_ready() {
all_ready = false;
}
r.is_ready()
});
self.summary_cache.retain(|_, (_, r)| {
if !*r {
all_ready = false;
}
*r
});
all_ready
}
pub fn used_replacement_for(&self, p: PackageId) -> Option<(PackageId, PackageId)> {
self.used_replacements.get(&p).map(|r| (p, r.package_id()))
}
pub fn replacement_summary(&self, p: PackageId) -> Option<&Summary> {
self.used_replacements.get(&p)
}
/// 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) -> Poll<CargoResult<Rc<Vec<Summary>>>> {
if let Some(out) = self.registry_cache.get(dep).cloned() {
return out.map(Result::Ok);
}
let mut ret = Vec::new();
let ready = self.registry.query(dep, QueryKind::Exact, &mut |s| {
ret.push(s);
})?;
if ready.is_pending() {
self.registry_cache.insert(dep.clone(), Poll::Pending);
return Poll::Pending;
}
for summary in ret.iter() {
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.package_name(),
dep.version_req()
);
let mut summaries = match self.registry.query_vec(dep, QueryKind::Exact)? {
Poll::Ready(s) => s.into_iter(),
Poll::Pending => {
self.registry_cache.insert(dep.clone(), Poll::Pending);
return Poll::Pending;
}
};
let s = summaries.next().ok_or_else(|| {
anyhow::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::<Vec<_>>();
if !summaries.is_empty() {
let bullets = summaries
.iter()
.map(|s| format!(" * {}", s.package_id()))
.collect::<Vec<_>>();
return Poll::Ready(Err(anyhow::anyhow!(
"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() {
return Poll::Ready(Err(anyhow::anyhow!(
"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.package_name(), dep.version_req());
}
if let Some(r) = replace {
self.used_replacements.insert(summary.package_id(), r);
}
}
// When we attempt versions for a package we'll want to do so in a sorted fashion to pick
// the "best candidates" first. VersionPreferences implements this notion.
self.version_prefs.sort_summaries(
&mut ret,
if self.minimal_versions {
VersionOrdering::MinimumVersionsFirst
} else {
VersionOrdering::MaximumVersionsFirst
},
);
let out = Poll::Ready(Rc::new(ret));
self.registry_cache.insert(dep.clone(), out.clone());
out.map(Result::Ok)
}
/// Find out what dependencies will be added by activating `candidate`,
/// with features described in `opts`. Then look up in the `registry`
/// the candidates that will fulfil each of these dependencies, as it is the
/// next obvious question.
pub fn build_deps(
&mut self,
cx: &Context,
parent: Option<PackageId>,
candidate: &Summary,
opts: &ResolveOpts,
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
// if we have calculated a result before, then we can just return it,
// as it is a "pure" query of its arguments.
if let Some(out) = self
.summary_cache
.get(&(parent, candidate.clone(), opts.clone()))
{
return Ok(out.0.clone());
}
// 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 (used_features, deps) = resolve_features(parent, candidate, opts)?;
// Next, transform all dependencies into a list of possible candidates
// which can satisfy that dependency.
let mut all_ready = true;
let mut deps = deps
.into_iter()
.filter_map(|(dep, features)| match self.query(&dep) {
Poll::Ready(Ok(candidates)) => Some(Ok((dep, candidates, features))),
Poll::Pending => {
all_ready = false;
// we can ignore Pending deps, resolve will be repeatedly called
// until there are none to ignore
None
}
Poll::Ready(Err(e)) => Some(Err(e).with_context(|| {
format!(
"failed to get `{}` as a dependency of {}",
dep.package_name(),
describe_path_in_context(cx, &candidate.package_id()),
)
})),
})
.collect::<CargoResult<Vec<DepInfo>>>()?;
// 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());
let out = Rc::new((used_features, Rc::new(deps)));
// If we succeed we add the result to the cache so we can use it again next time.
// We don't cache the failure cases as they don't impl Clone.
self.summary_cache.insert(
(parent, candidate.clone(), opts.clone()),
(out.clone(), all_ready),
);
Ok(out)
}
}
/// Returns the features we ended up using and
/// all dependencies and the features we want from each of them.
pub fn resolve_features<'b>(
parent: Option<PackageId>,
s: &'b Summary,
opts: &'b ResolveOpts,
) -> ActivateResult<(HashSet<InternedString>, Vec<(Dependency, FeaturesSet)>)> {
// First, filter by dev-dependencies.
let deps = s.dependencies();
let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps);
let reqs = build_requirements(parent, s, opts)?;
let mut ret = Vec::new();
let default_dep = BTreeSet::new();
let mut valid_dep_names = HashSet::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_in_toml()) {
continue;
}
valid_dep_names.insert(dep.name_in_toml());
// So we want this dependency. Move the features we want from
// `feature_deps` to `ret` and register ourselves as using this
// name.
let mut base = reqs
.deps
.get(&dep.name_in_toml())
.unwrap_or(&default_dep)
.clone();
base.extend(dep.features().iter());
ret.push((dep.clone(), Rc::new(base)));
}
// This is a special case for command-line `--features
// dep_name/feat_name` where `dep_name` does not exist. All other
// validation is done either in `build_requirements` or
// `build_feature_map`.
if parent.is_none() {
for dep_name in reqs.deps.keys() {
if !valid_dep_names.contains(dep_name) {
let e = RequirementError::MissingDependency(*dep_name);
return Err(e.into_activate_error(parent, s));
}
}
}
Ok((reqs.into_features(), ret))
}
/// Takes requested features for a single package from the input `ResolveOpts` 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>(
parent: Option<PackageId>,
s: &'a Summary,
opts: &'b ResolveOpts,
) -> ActivateResult<Requirements<'a>> {
let mut reqs = Requirements::new(s);
let handle_default = |uses_default_features, reqs: &mut Requirements<'_>| {
if uses_default_features && s.features().contains_key("default") {
if let Err(e) = reqs.require_feature(InternedString::new("default")) {
return Err(e.into_activate_error(parent, s));
}
}
Ok(())
};
match &opts.features {
RequestedFeatures::CliFeatures(CliFeatures {
features,
all_features,
uses_default_features,
}) => {
if *all_features {
for key in s.features().keys() {
if let Err(e) = reqs.require_feature(*key) {
return Err(e.into_activate_error(parent, s));
}
}
}
for fv in features.iter() {
if let Err(e) = reqs.require_value(fv) {
return Err(e.into_activate_error(parent, s));
}
}
handle_default(*uses_default_features, &mut reqs)?;
}
RequestedFeatures::DepFeatures {
features,
uses_default_features,
} => {
for feature in features.iter() {
if let Err(e) = reqs.require_feature(*feature) {
return Err(e.into_activate_error(parent, s));
}
}
handle_default(*uses_default_features, &mut reqs)?;
}
}
Ok(reqs)
}
/// Set of feature and dependency requirements for a package.
#[derive(Debug)]
struct Requirements<'a> {
summary: &'a Summary,
/// The deps map is a mapping of dependency name to list of features enabled.
///
/// The resolver will activate all of these dependencies, with the given
/// features enabled.
deps: HashMap<InternedString, BTreeSet<InternedString>>,
/// The set of features enabled on this package which is later used when
/// compiling to instruct the code what features were enabled.
features: HashSet<InternedString>,
}
/// An error for a requirement.
///
/// This will later be converted to an `ActivateError` depending on whether or
/// not this is a dependency or a root package.
enum RequirementError {
/// The package does not have the requested feature.
MissingFeature(InternedString),
/// The package does not have the requested dependency.
MissingDependency(InternedString),
/// A feature has a direct cycle to itself.
///
/// Note that cycles through multiple features are allowed (but perhaps
/// they shouldn't be?).
Cycle(InternedString),
}
impl Requirements<'_> {
fn new(summary: &Summary) -> Requirements<'_> {
Requirements {
summary,
deps: HashMap::new(),
features: HashSet::new(),
}
}
fn into_features(self) -> HashSet<InternedString> {
self.features
}
fn require_dep_feature(
&mut self,
package: InternedString,
feat: InternedString,
weak: bool,
) -> Result<(), RequirementError> {
// If `package` is indeed an optional dependency then we activate the
// feature named `package`, but otherwise if `package` is a required
// dependency then there's no feature associated with it.
if !weak
&& self
.summary
.dependencies()
.iter()
.any(|dep| dep.name_in_toml() == package && dep.is_optional())
{
self.require_feature(package)?;
}
self.deps.entry(package).or_default().insert(feat);
Ok(())
}
fn require_dependency(&mut self, pkg: InternedString) {
self.deps.entry(pkg).or_default();
}
fn require_feature(&mut self, feat: InternedString) -> Result<(), RequirementError> {
if !self.features.insert(feat) {
// Already seen this feature.
return Ok(());
}
let fvs = match self.summary.features().get(&feat) {
Some(fvs) => fvs,
None => return Err(RequirementError::MissingFeature(feat)),
};
for fv in fvs {
if let FeatureValue::Feature(dep_feat) = fv {
if *dep_feat == feat {
return Err(RequirementError::Cycle(feat));
}
}
self.require_value(fv)?;
}
Ok(())
}
fn require_value(&mut self, fv: &FeatureValue) -> Result<(), RequirementError> {
match fv {
FeatureValue::Feature(feat) => self.require_feature(*feat)?,
FeatureValue::Dep { dep_name } => self.require_dependency(*dep_name),
FeatureValue::DepFeature {
dep_name,
dep_feature,
// Weak features are always activated in the dependency
// resolver. They will be narrowed inside the new feature
// resolver.
weak,
} => self.require_dep_feature(*dep_name, *dep_feature, *weak)?,
};
Ok(())
}
}
impl RequirementError {
fn into_activate_error(self, parent: Option<PackageId>, summary: &Summary) -> ActivateError {
match self {
RequirementError::MissingFeature(feat) => {
let deps: Vec<_> = summary
.dependencies()
.iter()
.filter(|dep| dep.name_in_toml() == feat)
.collect();
if deps.is_empty() {
return match parent {
None => ActivateError::Fatal(anyhow::format_err!(
"Package `{}` does not have the feature `{}`",
summary.package_id(),
feat
)),
Some(p) => ActivateError::Conflict(
p,
ConflictReason::MissingFeatures(feat.to_string()),
),
};
}
if deps.iter().any(|dep| dep.is_optional()) {
match parent {
None => ActivateError::Fatal(anyhow::format_err!(
"Package `{}` does not have feature `{}`. It has an optional dependency \
with that name, but that dependency uses the \"dep:\" \
syntax in the features table, so it does not have an implicit feature with that name.",
summary.package_id(),
feat
)),
Some(p) => ActivateError::Conflict(
p,
ConflictReason::NonImplicitDependencyAsFeature(feat),
),
}
} else {
match parent {
None => ActivateError::Fatal(anyhow::format_err!(
"Package `{}` does not have feature `{}`. It has a required dependency \
with that name, but only optional dependencies can be used as features.",
summary.package_id(),
feat
)),
Some(p) => ActivateError::Conflict(
p,
ConflictReason::RequiredDependencyAsFeature(feat),
),
}
}
}
RequirementError::MissingDependency(dep_name) => {
match parent {
None => ActivateError::Fatal(anyhow::format_err!(
"package `{}` does not have a dependency named `{}`",
summary.package_id(),
dep_name
)),
// This code path currently isn't used, since `foo/bar`
// and `dep:` syntax is not allowed in a dependency.
Some(p) => ActivateError::Conflict(
p,
ConflictReason::MissingFeatures(dep_name.to_string()),
),
}
}
RequirementError::Cycle(feat) => ActivateError::Fatal(anyhow::format_err!(
"cyclic feature dependency: feature `{}` depends on itself",
feat
)),
}
}
}