Skip to content

Commit 949f49a

Browse files
committed
make resolve_features a stand alone function
1 parent 4aacb80 commit 949f49a

File tree

2 files changed

+252
-243
lines changed

2 files changed

+252
-243
lines changed

src/cargo/core/resolver/context.rs

Lines changed: 15 additions & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ use failure::{bail, ensure};
88
use log::debug;
99

1010
use crate::core::interning::InternedString;
11-
use crate::core::{Dependency, FeatureValue, PackageId, SourceId, Summary};
11+
use crate::core::{Dependency, PackageId, SourceId, Summary};
1212
use crate::util::CargoResult;
1313
use crate::util::Graph;
1414

15+
use super::dep_cache;
1516
use super::dep_cache::RegistryQueryer;
1617
use super::errors::ActivateResult;
17-
use super::types::{ConflictMap, ConflictReason, DepInfo, Method};
18+
use super::types::{ConflictMap, DepInfo, Method};
1819

1920
pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
2021
pub use super::encode::{Metadata, WorkspaceResolve};
@@ -150,15 +151,25 @@ impl Context {
150151

151152
pub fn build_deps(
152153
&mut self,
153-
registry: &mut RegistryQueryer<'_>,
154+
registry: &mut dep_cache::RegistryQueryer<'_>,
154155
parent: Option<PackageId>,
155156
candidate: &Summary,
156157
method: &Method<'_>,
157158
) -> ActivateResult<Vec<DepInfo>> {
158159
// First, figure out our set of dependencies based on the requested set
159160
// of features. This also calculates what features we're going to enable
160161
// for our own dependencies.
161-
let deps = self.resolve_features(parent, candidate, method)?;
162+
let (used_features, deps) = dep_cache::resolve_features(parent, candidate, method)?;
163+
164+
// Record what list of features is active for this package.
165+
if !used_features.is_empty() {
166+
Rc::make_mut(
167+
self.resolve_features
168+
.entry(candidate.package_id())
169+
.or_insert_with(|| Rc::new(HashSet::with_capacity(used_features.len()))),
170+
)
171+
.extend(used_features);
172+
}
162173

163174
// Next, transform all dependencies into a list of possible candidates
164175
// which can satisfy that dependency.
@@ -212,110 +223,6 @@ impl Context {
212223
Some(max)
213224
}
214225

215-
/// Returns all dependencies and the features we want from them.
216-
pub fn resolve_features<'b>(
217-
&mut self,
218-
parent: Option<PackageId>,
219-
s: &'b Summary,
220-
method: &'b Method<'_>,
221-
) -> ActivateResult<Vec<(Dependency, Vec<InternedString>)>> {
222-
let dev_deps = match *method {
223-
Method::Everything => true,
224-
Method::Required { dev_deps, .. } => dev_deps,
225-
};
226-
227-
// First, filter by dev-dependencies.
228-
let deps = s.dependencies();
229-
let deps = deps.iter().filter(|d| d.is_transitive() || dev_deps);
230-
231-
let reqs = build_requirements(s, method)?;
232-
let mut ret = Vec::new();
233-
let mut used_features = HashSet::new();
234-
let default_dep = (false, Vec::new());
235-
236-
// Next, collect all actually enabled dependencies and their features.
237-
for dep in deps {
238-
// Skip optional dependencies, but not those enabled through a
239-
// feature
240-
if dep.is_optional() && !reqs.deps.contains_key(&dep.name_in_toml()) {
241-
continue;
242-
}
243-
// So we want this dependency. Move the features we want from
244-
// `feature_deps` to `ret` and register ourselves as using this
245-
// name.
246-
let base = reqs.deps.get(&dep.name_in_toml()).unwrap_or(&default_dep);
247-
used_features.insert(dep.name_in_toml());
248-
let always_required = !dep.is_optional()
249-
&& !s
250-
.dependencies()
251-
.iter()
252-
.any(|d| d.is_optional() && d.name_in_toml() == dep.name_in_toml());
253-
if always_required && base.0 {
254-
return Err(match parent {
255-
None => failure::format_err!(
256-
"Package `{}` does not have feature `{}`. It has a required dependency \
257-
with that name, but only optional dependencies can be used as features.",
258-
s.package_id(),
259-
dep.name_in_toml()
260-
)
261-
.into(),
262-
Some(p) => (
263-
p.package_id(),
264-
ConflictReason::RequiredDependencyAsFeatures(dep.name_in_toml()),
265-
)
266-
.into(),
267-
});
268-
}
269-
let mut base = base.1.clone();
270-
base.extend(dep.features().iter());
271-
for feature in base.iter() {
272-
if feature.contains('/') {
273-
return Err(failure::format_err!(
274-
"feature names may not contain slashes: `{}`",
275-
feature
276-
)
277-
.into());
278-
}
279-
}
280-
ret.push((dep.clone(), base));
281-
}
282-
283-
// Any entries in `reqs.dep` which weren't used are bugs in that the
284-
// package does not actually have those dependencies. We classified
285-
// them as dependencies in the first place because there is no such
286-
// feature, either.
287-
let remaining = reqs
288-
.deps
289-
.keys()
290-
.cloned()
291-
.filter(|s| !used_features.contains(s))
292-
.collect::<Vec<_>>();
293-
if !remaining.is_empty() {
294-
let features = remaining.join(", ");
295-
return Err(match parent {
296-
None => failure::format_err!(
297-
"Package `{}` does not have these features: `{}`",
298-
s.package_id(),
299-
features
300-
)
301-
.into(),
302-
Some(p) => (p, ConflictReason::MissingFeatures(features)).into(),
303-
});
304-
}
305-
306-
// Record what list of features is active for this package.
307-
if !reqs.used.is_empty() {
308-
Rc::make_mut(
309-
self.resolve_features
310-
.entry(s.package_id())
311-
.or_insert_with(|| Rc::new(HashSet::with_capacity(reqs.used.len()))),
312-
)
313-
.extend(reqs.used);
314-
}
315-
316-
Ok(ret)
317-
}
318-
319226
pub fn resolve_replacements(
320227
&self,
321228
registry: &RegistryQueryer<'_>,
@@ -342,136 +249,3 @@ impl Context {
342249
graph
343250
}
344251
}
345-
346-
/// Takes requested features for a single package from the input `Method` and
347-
/// recurses to find all requested features, dependencies and requested
348-
/// dependency features in a `Requirements` object, returning it to the resolver.
349-
fn build_requirements<'a, 'b: 'a>(
350-
s: &'a Summary,
351-
method: &'b Method<'_>,
352-
) -> CargoResult<Requirements<'a>> {
353-
let mut reqs = Requirements::new(s);
354-
355-
match *method {
356-
Method::Everything
357-
| Method::Required {
358-
all_features: true, ..
359-
} => {
360-
for key in s.features().keys() {
361-
reqs.require_feature(*key)?;
362-
}
363-
for dep in s.dependencies().iter().filter(|d| d.is_optional()) {
364-
reqs.require_dependency(dep.name_in_toml());
365-
}
366-
}
367-
Method::Required {
368-
all_features: false,
369-
features: requested,
370-
..
371-
} => {
372-
for &f in requested.iter() {
373-
reqs.require_value(&FeatureValue::new(f, s))?;
374-
}
375-
}
376-
}
377-
match *method {
378-
Method::Everything
379-
| Method::Required {
380-
uses_default_features: true,
381-
..
382-
} => {
383-
if s.features().contains_key("default") {
384-
reqs.require_feature(InternedString::new("default"))?;
385-
}
386-
}
387-
Method::Required {
388-
uses_default_features: false,
389-
..
390-
} => {}
391-
}
392-
Ok(reqs)
393-
}
394-
395-
struct Requirements<'a> {
396-
summary: &'a Summary,
397-
// The deps map is a mapping of package name to list of features enabled.
398-
// Each package should be enabled, and each package should have the
399-
// specified set of features enabled. The boolean indicates whether this
400-
// package was specifically requested (rather than just requesting features
401-
// *within* this package).
402-
deps: HashMap<InternedString, (bool, Vec<InternedString>)>,
403-
// The used features set is the set of features which this local package had
404-
// enabled, which is later used when compiling to instruct the code what
405-
// features were enabled.
406-
used: HashSet<InternedString>,
407-
visited: HashSet<InternedString>,
408-
}
409-
410-
impl Requirements<'_> {
411-
fn new(summary: &Summary) -> Requirements<'_> {
412-
Requirements {
413-
summary,
414-
deps: HashMap::new(),
415-
used: HashSet::new(),
416-
visited: HashSet::new(),
417-
}
418-
}
419-
420-
fn require_crate_feature(&mut self, package: InternedString, feat: InternedString) {
421-
self.used.insert(package);
422-
self.deps
423-
.entry(package)
424-
.or_insert((false, Vec::new()))
425-
.1
426-
.push(feat);
427-
}
428-
429-
fn seen(&mut self, feat: InternedString) -> bool {
430-
if self.visited.insert(feat) {
431-
self.used.insert(feat);
432-
false
433-
} else {
434-
true
435-
}
436-
}
437-
438-
fn require_dependency(&mut self, pkg: InternedString) {
439-
if self.seen(pkg) {
440-
return;
441-
}
442-
self.deps.entry(pkg).or_insert((false, Vec::new())).0 = true;
443-
}
444-
445-
fn require_feature(&mut self, feat: InternedString) -> CargoResult<()> {
446-
if feat.is_empty() || self.seen(feat) {
447-
return Ok(());
448-
}
449-
for fv in self
450-
.summary
451-
.features()
452-
.get(feat.as_str())
453-
.expect("must be a valid feature")
454-
{
455-
match *fv {
456-
FeatureValue::Feature(ref dep_feat) if **dep_feat == *feat => failure::bail!(
457-
"cyclic feature dependency: feature `{}` depends on itself",
458-
feat
459-
),
460-
_ => {}
461-
}
462-
self.require_value(fv)?;
463-
}
464-
Ok(())
465-
}
466-
467-
fn require_value(&mut self, fv: &FeatureValue) -> CargoResult<()> {
468-
match fv {
469-
FeatureValue::Feature(feat) => self.require_feature(*feat)?,
470-
FeatureValue::Crate(dep) => self.require_dependency(*dep),
471-
FeatureValue::CrateFeature(dep, dep_feat) => {
472-
self.require_crate_feature(*dep, *dep_feat)
473-
}
474-
};
475-
Ok(())
476-
}
477-
}

0 commit comments

Comments
 (0)