Skip to content

Commit 1f74bdf

Browse files
committed
Auto merge of #7186 - ehuss:resolve-method, r=alexcrichton
Refactor resolve `Method` This changes the enum `Method` to a struct called `ResolveOpts`. The intent is to try to make it a little easier to read and understand. If this doesn't actually look better to anyone, I'm fine with discarding this (I can resubmit just with documentation). It only seems marginally better, but I have had difficulty with understanding the subtleties of `Method` for a while. I wasn't able to measure any performance difference. This also has a few other changes: - Adds some documentation. - Removes `resolve_ws_precisely`, cutting down the number of resolve functions from 4 to 3.
2 parents df1c2c1 + abf2bb4 commit 1f74bdf

17 files changed

+247
-210
lines changed

crates/resolver-tests/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::rc::Rc;
88
use std::time::Instant;
99

1010
use cargo::core::dependency::Kind;
11-
use cargo::core::resolver::{self, Method};
11+
use cargo::core::resolver::{self, ResolveOpts};
1212
use cargo::core::source::{GitReference, SourceId};
1313
use cargo::core::Resolve;
1414
use cargo::core::{Dependency, PackageId, Registry, Summary};
@@ -175,10 +175,10 @@ pub fn resolve_with_config_raw(
175175
false,
176176
)
177177
.unwrap();
178-
let method = Method::Everything;
178+
let opts = ResolveOpts::everything();
179179
let start = Instant::now();
180180
let resolve = resolver::resolve(
181-
&[(summary, method)],
181+
&[(summary, opts)],
182182
&[],
183183
&mut registry,
184184
&HashSet::new(),

src/cargo/core/compiler/layout.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! The directory layout is a little tricky at times, hence a separate file to
44
//! house this logic. The current layout looks like this:
55
//!
6-
//! ```ignore
6+
//! ```text
77
//! # This is the root directory for all output, the top-level package
88
//! # places all of its output here.
99
//! target/

src/cargo/core/resolver/context.rs

+13-17
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::util::CargoResult;
1313
use crate::util::Graph;
1414

1515
use super::dep_cache::RegistryQueryer;
16-
use super::types::{ConflictMap, FeaturesSet, Method};
16+
use super::types::{ConflictMap, FeaturesSet, ResolveOpts};
1717

1818
pub use super::encode::Metadata;
1919
pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
@@ -103,11 +103,11 @@ impl Context {
103103
/// cased `summary` to get activated. This may not be present for the root
104104
/// crate, for example.
105105
///
106-
/// Returns `true` if this summary with the given method is already activated.
106+
/// Returns `true` if this summary with the given features is already activated.
107107
pub fn flag_activated(
108108
&mut self,
109109
summary: &Summary,
110-
method: &Method,
110+
opts: &ResolveOpts,
111111
parent: Option<(&Summary, &Dependency)>,
112112
) -> CargoResult<bool> {
113113
let id = summary.package_id();
@@ -158,25 +158,21 @@ impl Context {
158158
}
159159
}
160160
debug!("checking if {} is already activated", summary.package_id());
161-
let (features, use_default) = match method {
162-
Method::Everything
163-
| Method::Required {
164-
all_features: true, ..
165-
} => return Ok(false),
166-
Method::Required {
167-
features,
168-
uses_default_features,
169-
..
170-
} => (features, uses_default_features),
171-
};
161+
if opts.all_features {
162+
return Ok(false);
163+
}
172164

173165
let has_default_feature = summary.features().contains_key("default");
174166
Ok(match self.resolve_features.get(&id) {
175167
Some(prev) => {
176-
features.is_subset(prev)
177-
&& (!use_default || prev.contains("default") || !has_default_feature)
168+
opts.features.is_subset(prev)
169+
&& (!opts.uses_default_features
170+
|| prev.contains("default")
171+
|| !has_default_feature)
172+
}
173+
None => {
174+
opts.features.is_empty() && (!opts.uses_default_features || !has_default_feature)
178175
}
179-
None => features.is_empty() && (!use_default || !has_default_feature),
180176
})
181177
}
182178

src/cargo/core/resolver/dep_cache.rs

+26-49
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry,
2020
use crate::util::errors::CargoResult;
2121

2222
use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
23-
use crate::core::resolver::{ActivateResult, Method};
23+
use crate::core::resolver::{ActivateResult, ResolveOpts};
2424

2525
pub struct RegistryQueryer<'a> {
2626
pub registry: &'a mut (dyn Registry + 'a),
@@ -34,7 +34,7 @@ pub struct RegistryQueryer<'a> {
3434
registry_cache: HashMap<Dependency, Rc<Vec<Summary>>>,
3535
/// a cache of `Dependency`s that are required for a `Summary`
3636
summary_cache: HashMap<
37-
(Option<PackageId>, Summary, Method),
37+
(Option<PackageId>, Summary, ResolveOpts),
3838
Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
3939
>,
4040
/// all the cases we ended up using a supplied replacement
@@ -192,28 +192,28 @@ impl<'a> RegistryQueryer<'a> {
192192
}
193193

194194
/// Find out what dependencies will be added by activating `candidate`,
195-
/// with features described in `method`. Then look up in the `registry`
195+
/// with features described in `opts`. Then look up in the `registry`
196196
/// the candidates that will fulfil each of these dependencies, as it is the
197197
/// next obvious question.
198198
pub fn build_deps(
199199
&mut self,
200200
parent: Option<PackageId>,
201201
candidate: &Summary,
202-
method: &Method,
202+
opts: &ResolveOpts,
203203
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
204204
// if we have calculated a result before, then we can just return it,
205205
// as it is a "pure" query of its arguments.
206206
if let Some(out) = self
207207
.summary_cache
208-
.get(&(parent, candidate.clone(), method.clone()))
208+
.get(&(parent, candidate.clone(), opts.clone()))
209209
.cloned()
210210
{
211211
return Ok(out);
212212
}
213213
// First, figure out our set of dependencies based on the requested set
214214
// of features. This also calculates what features we're going to enable
215215
// for our own dependencies.
216-
let (used_features, deps) = resolve_features(parent, candidate, method)?;
216+
let (used_features, deps) = resolve_features(parent, candidate, opts)?;
217217

218218
// Next, transform all dependencies into a list of possible candidates
219219
// which can satisfy that dependency.
@@ -236,7 +236,7 @@ impl<'a> RegistryQueryer<'a> {
236236
// If we succeed we add the result to the cache so we can use it again next time.
237237
// We dont cache the failure cases as they dont impl Clone.
238238
self.summary_cache
239-
.insert((parent, candidate.clone(), method.clone()), out.clone());
239+
.insert((parent, candidate.clone(), opts.clone()), out.clone());
240240

241241
Ok(out)
242242
}
@@ -247,18 +247,13 @@ impl<'a> RegistryQueryer<'a> {
247247
pub fn resolve_features<'b>(
248248
parent: Option<PackageId>,
249249
s: &'b Summary,
250-
method: &'b Method,
250+
opts: &'b ResolveOpts,
251251
) -> ActivateResult<(HashSet<InternedString>, Vec<(Dependency, FeaturesSet)>)> {
252-
let dev_deps = match *method {
253-
Method::Everything => true,
254-
Method::Required { dev_deps, .. } => dev_deps,
255-
};
256-
257252
// First, filter by dev-dependencies.
258253
let deps = s.dependencies();
259-
let deps = deps.iter().filter(|d| d.is_transitive() || dev_deps);
254+
let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps);
260255

261-
let reqs = build_requirements(s, method)?;
256+
let reqs = build_requirements(s, opts)?;
262257
let mut ret = Vec::new();
263258
let mut used_features = HashSet::new();
264259
let default_dep = (false, BTreeSet::new());
@@ -336,52 +331,34 @@ pub fn resolve_features<'b>(
336331
Ok((reqs.into_used(), ret))
337332
}
338333

339-
/// Takes requested features for a single package from the input `Method` and
334+
/// Takes requested features for a single package from the input `ResolveOpts` and
340335
/// recurses to find all requested features, dependencies and requested
341336
/// dependency features in a `Requirements` object, returning it to the resolver.
342337
fn build_requirements<'a, 'b: 'a>(
343338
s: &'a Summary,
344-
method: &'b Method,
339+
opts: &'b ResolveOpts,
345340
) -> CargoResult<Requirements<'a>> {
346341
let mut reqs = Requirements::new(s);
347342

348-
match method {
349-
Method::Everything
350-
| Method::Required {
351-
all_features: true, ..
352-
} => {
353-
for key in s.features().keys() {
354-
reqs.require_feature(*key)?;
355-
}
356-
for dep in s.dependencies().iter().filter(|d| d.is_optional()) {
357-
reqs.require_dependency(dep.name_in_toml());
358-
}
343+
if opts.all_features {
344+
for key in s.features().keys() {
345+
reqs.require_feature(*key)?;
359346
}
360-
Method::Required {
361-
all_features: false,
362-
features: requested,
363-
..
364-
} => {
365-
for &f in requested.iter() {
366-
reqs.require_value(&FeatureValue::new(f, s))?;
367-
}
347+
for dep in s.dependencies().iter().filter(|d| d.is_optional()) {
348+
reqs.require_dependency(dep.name_in_toml());
349+
}
350+
} else {
351+
for &f in opts.features.iter() {
352+
reqs.require_value(&FeatureValue::new(f, s))?;
368353
}
369354
}
370-
match *method {
371-
Method::Everything
372-
| Method::Required {
373-
uses_default_features: true,
374-
..
375-
} => {
376-
if s.features().contains_key("default") {
377-
reqs.require_feature(InternedString::new("default"))?;
378-
}
355+
356+
if opts.uses_default_features {
357+
if s.features().contains_key("default") {
358+
reqs.require_feature(InternedString::new("default"))?;
379359
}
380-
Method::Required {
381-
uses_default_features: false,
382-
..
383-
} => {}
384360
}
361+
385362
Ok(reqs)
386363
}
387364

src/cargo/core/resolver/encode.rs

+9
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ use crate::util::{internal, Graph};
105105

106106
use super::{Resolve, ResolveVersion};
107107

108+
/// The `Cargo.lock` structure.
108109
#[derive(Serialize, Deserialize, Debug)]
109110
pub struct EncodableResolve {
110111
package: Option<Vec<EncodableDependency>>,
@@ -123,6 +124,14 @@ struct Patch {
123124
pub type Metadata = BTreeMap<String, String>;
124125

125126
impl EncodableResolve {
127+
/// Convert a `Cargo.lock` to a Resolve.
128+
///
129+
/// Note that this `Resolve` is not "complete". For example, the
130+
/// dependencies do not know the difference between regular/dev/build
131+
/// dependencies, so they are not filled in. It also does not include
132+
/// `features`. Care should be taken when using this Resolve. One of the
133+
/// primary uses is to be used with `resolve_with_previous` to guide the
134+
/// resolver to create a complete Resolve.
126135
pub fn into_resolve(self, ws: &Workspace<'_>) -> CargoResult<Resolve> {
127136
let path_deps = build_path_deps(ws);
128137
let mut checksums = HashMap::new();

src/cargo/core/resolver/mod.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pub use self::encode::Metadata;
6969
pub use self::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
7070
pub use self::errors::{ActivateError, ActivateResult, ResolveError};
7171
pub use self::resolve::{Resolve, ResolveVersion};
72-
pub use self::types::Method;
72+
pub use self::types::ResolveOpts;
7373

7474
mod conflict_cache;
7575
mod context;
@@ -120,7 +120,7 @@ mod types;
120120
/// When we have a decision for how to implement is without breaking existing functionality
121121
/// this flag can be removed.
122122
pub fn resolve(
123-
summaries: &[(Summary, Method)],
123+
summaries: &[(Summary, ResolveOpts)],
124124
replacements: &[(PackageIdSpec, Dependency)],
125125
registry: &mut dyn Registry,
126126
try_to_use: &HashSet<PackageId>,
@@ -169,7 +169,7 @@ pub fn resolve(
169169
fn activate_deps_loop(
170170
mut cx: Context,
171171
registry: &mut RegistryQueryer<'_>,
172-
summaries: &[(Summary, Method)],
172+
summaries: &[(Summary, ResolveOpts)],
173173
config: Option<&Config>,
174174
) -> CargoResult<Context> {
175175
let mut backtrack_stack = Vec::new();
@@ -180,9 +180,9 @@ fn activate_deps_loop(
180180
let mut past_conflicting_activations = conflict_cache::ConflictCache::new();
181181

182182
// Activate all the initial summaries to kick off some work.
183-
for &(ref summary, ref method) in summaries {
183+
for &(ref summary, ref opts) in summaries {
184184
debug!("initial activation: {}", summary.package_id());
185-
let res = activate(&mut cx, registry, None, summary.clone(), method.clone());
185+
let res = activate(&mut cx, registry, None, summary.clone(), opts.clone());
186186
match res {
187187
Ok(Some((frame, _))) => remaining_deps.push(frame),
188188
Ok(None) => (),
@@ -366,7 +366,7 @@ fn activate_deps_loop(
366366
};
367367

368368
let pid = candidate.package_id();
369-
let method = Method::Required {
369+
let opts = ResolveOpts {
370370
dev_deps: false,
371371
features: Rc::clone(&features),
372372
all_features: false,
@@ -379,7 +379,7 @@ fn activate_deps_loop(
379379
dep.package_name(),
380380
candidate.version()
381381
);
382-
let res = activate(&mut cx, registry, Some((&parent, &dep)), candidate, method);
382+
let res = activate(&mut cx, registry, Some((&parent, &dep)), candidate, opts);
383383

384384
let successfully_activated = match res {
385385
// Success! We've now activated our `candidate` in our context
@@ -583,15 +583,15 @@ fn activate_deps_loop(
583583
/// Attempts to activate the summary `candidate` in the context `cx`.
584584
///
585585
/// This function will pull dependency summaries from the registry provided, and
586-
/// the dependencies of the package will be determined by the `method` provided.
586+
/// the dependencies of the package will be determined by the `opts` provided.
587587
/// If `candidate` was activated, this function returns the dependency frame to
588588
/// iterate through next.
589589
fn activate(
590590
cx: &mut Context,
591591
registry: &mut RegistryQueryer<'_>,
592592
parent: Option<(&Summary, &Dependency)>,
593593
candidate: Summary,
594-
method: Method,
594+
opts: ResolveOpts,
595595
) -> ActivateResult<Option<(DepsFrame, Duration)>> {
596596
let candidate_pid = candidate.package_id();
597597
if let Some((parent, dep)) = parent {
@@ -652,7 +652,7 @@ fn activate(
652652
}
653653
}
654654

655-
let activated = cx.flag_activated(&candidate, &method, parent)?;
655+
let activated = cx.flag_activated(&candidate, &opts, parent)?;
656656

657657
let candidate = match registry.replacement_summary(candidate_pid) {
658658
Some(replace) => {
@@ -661,7 +661,7 @@ fn activate(
661661
// does. TBH it basically cause panics in the test suite if
662662
// `parent` is passed through here and `[replace]` is otherwise
663663
// on life support so it's not critical to fix bugs anyway per se.
664-
if cx.flag_activated(replace, &method, None)? && activated {
664+
if cx.flag_activated(replace, &opts, None)? && activated {
665665
return Ok(None);
666666
}
667667
trace!(
@@ -682,7 +682,7 @@ fn activate(
682682

683683
let now = Instant::now();
684684
let (used_features, deps) =
685-
&*registry.build_deps(parent.map(|p| p.0.package_id()), &candidate, &method)?;
685+
&*registry.build_deps(parent.map(|p| p.0.package_id()), &candidate, &opts)?;
686686

687687
// Record what list of features is active for this package.
688688
if !used_features.is_empty() {

0 commit comments

Comments
 (0)