cargo/resolver/types.rs
1use super::features::{CliFeatures, RequestedFeatures};
2use crate::util::GlobalContext;
3use crate::util::errors::CargoResult;
4use crate::util::interning::InternedString;
5use crate::workspace::{Dependency, PackageId, SourceId, Summary};
6use std::cmp::Ordering;
7use std::collections::{BTreeMap, BTreeSet};
8use std::num::NonZeroU64;
9use std::rc::Rc;
10use std::time::{Duration, Instant};
11
12pub struct ResolverProgress {
13 ticks: u16,
14 start: Instant,
15 time_to_print: Duration,
16 printed: bool,
17 deps_time: Duration,
18 /// Provides an escape hatch for machine with slow CPU for debugging and
19 /// testing Cargo itself.
20 /// See [rust-lang/cargo#6596](https://github.com/rust-lang/cargo/pull/6596) for more.
21 #[cfg(debug_assertions)]
22 slow_cpu_multiplier: u64,
23}
24
25impl ResolverProgress {
26 pub fn new() -> ResolverProgress {
27 ResolverProgress {
28 ticks: 0,
29 start: Instant::now(),
30 time_to_print: Duration::from_millis(500),
31 printed: false,
32 deps_time: Duration::new(0, 0),
33 // Some CI setups are much slower then the equipment used by Cargo itself.
34 // Architectures that do not have a modern processor, hardware emulation, etc.
35 // In the test code we have `slow_cpu_multiplier`, but that is not accessible here.
36 #[cfg(debug_assertions)]
37 // ALLOWED: For testing cargo itself only. However, it was communicated as an public
38 // interface to other developers, so keep it as-is, shouldn't add `__CARGO` prefix.
39 #[allow(clippy::disallowed_methods)]
40 slow_cpu_multiplier: std::env::var("CARGO_TEST_SLOW_CPU_MULTIPLIER")
41 .ok()
42 .and_then(|m| m.parse().ok())
43 .unwrap_or(1),
44 }
45 }
46 pub fn shell_status(&mut self, gctx: &GlobalContext) -> CargoResult<()> {
47 // If we spend a lot of time here (we shouldn't in most cases) then give
48 // a bit of a visual indicator as to what we're doing. Only enable this
49 // when stderr is a tty (a human is likely to be watching) to ensure we
50 // get deterministic output otherwise when observed by tools.
51 //
52 // Also note that we hit this loop a lot, so it's fairly performance
53 // sensitive. As a result try to defer a possibly expensive operation
54 // like `Instant::now` by only checking every N iterations of this loop
55 // to amortize the cost of the current time lookup.
56 self.ticks += 1;
57 if gctx.shell().is_err_tty()
58 && !self.printed
59 && self.ticks % 1000 == 0
60 && self.start.elapsed() - self.deps_time > self.time_to_print
61 {
62 self.printed = true;
63 gctx.shell().status("Resolving", "dependency graph...")?;
64 }
65 #[cfg(debug_assertions)]
66 {
67 // The largest test in our suite takes less then 5000 ticks
68 // with all the algorithm improvements.
69 // If any of them are removed then it takes more than I am willing to measure.
70 // So lets fail the test fast if we have been running for too long.
71 assert!(
72 self.ticks < 50_000,
73 "got to 50_000 ticks in {:?}",
74 self.start.elapsed()
75 );
76 // The largest test in our suite takes less then 30 sec
77 // with all the improvements to how fast a tick can go.
78 // If any of them are removed then it takes more than I am willing to measure.
79 // So lets fail the test fast if we have been running for too long.
80 if self.ticks % 1000 == 0 {
81 assert!(
82 self.start.elapsed() - self.deps_time
83 < Duration::from_secs(self.slow_cpu_multiplier * 90)
84 );
85 }
86 }
87 Ok(())
88 }
89 pub fn elapsed(&mut self, dur: Duration) {
90 self.deps_time += dur;
91 }
92}
93
94/// The preferred way to store the set of activated features for a package.
95/// This is sorted so that it impls Hash, and owns its contents,
96/// needed so it can be part of the key for caching in the `DepsCache`.
97/// It is also cloned often as part of `Context`, hence the `RC`.
98/// `im-rs::OrdSet` was slower of small sets like this,
99/// but this can change with improvements to std, im, or llvm.
100/// Using a consistent type for this allows us to use the highly
101/// optimized comparison operators like `is_subset` at the interfaces.
102pub type FeaturesSet = Rc<BTreeSet<InternedString>>;
103
104/// Resolver behavior, used to opt-in to new behavior that is
105/// backwards-incompatible via the `resolver` field in the manifest.
106#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
107pub enum ResolveBehavior {
108 /// V1 is the original resolver behavior.
109 V1,
110 /// V2 adds the new feature resolver.
111 V2,
112 /// V3 changes version preferences
113 V3,
114}
115
116impl ResolveBehavior {
117 pub fn from_manifest(resolver: &str) -> CargoResult<ResolveBehavior> {
118 match resolver {
119 "1" => Ok(ResolveBehavior::V1),
120 "2" => Ok(ResolveBehavior::V2),
121 "3" => Ok(ResolveBehavior::V3),
122 s => anyhow::bail!(
123 "`resolver` setting `{}` is not valid, valid options are \"1\", \"2\" or \"3\"",
124 s
125 ),
126 }
127 }
128
129 pub fn to_manifest(&self) -> String {
130 match self {
131 ResolveBehavior::V1 => "1",
132 ResolveBehavior::V2 => "2",
133 ResolveBehavior::V3 => "3",
134 }
135 .to_owned()
136 }
137}
138
139/// Options for how the resolve should work.
140#[derive(Clone, Debug, Eq, PartialEq, Hash)]
141pub struct ResolveOpts {
142 /// Whether or not dev-dependencies should be included.
143 ///
144 /// This may be set to `false` by things like `cargo install` or `-Z avoid-dev-deps`.
145 /// It also gets set to `false` when activating dependencies in the resolver.
146 pub dev_deps: bool,
147 /// Set of features requested on the command-line.
148 pub features: RequestedFeatures,
149}
150
151impl ResolveOpts {
152 /// Creates a `ResolveOpts` that resolves everything.
153 pub fn everything() -> ResolveOpts {
154 ResolveOpts {
155 dev_deps: true,
156 features: RequestedFeatures::CliFeatures(CliFeatures::new_all(true)),
157 }
158 }
159
160 pub fn new(dev_deps: bool, features: RequestedFeatures) -> ResolveOpts {
161 ResolveOpts { dev_deps, features }
162 }
163}
164
165/// A key that when stord in a hash map ensures that there is only one
166/// semver compatible version of each crate.
167/// Find the activated version of a crate based on the name, source, and semver compatibility.
168#[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
169pub struct ActivationsKey(InternedString, SemverCompatibility, SourceId);
170
171impl ActivationsKey {
172 pub fn new(
173 name: InternedString,
174 ver: SemverCompatibility,
175 source_id: SourceId,
176 ) -> ActivationsKey {
177 ActivationsKey(name, ver, source_id)
178 }
179}
180
181impl std::hash::Hash for ActivationsKey {
182 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
183 self.0.hash(state);
184 self.1.hash(state);
185 // self.2.hash(state); // Packages that only differ by SourceId are rare enough to not be worth hashing
186 }
187}
188
189/// A type that represents when cargo treats two Versions as compatible.
190/// Versions `a` and `b` are compatible if their left-most nonzero digit is the
191/// same.
192#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
193pub enum SemverCompatibility {
194 Major(NonZeroU64),
195 Minor(NonZeroU64),
196 Patch(u64),
197}
198
199impl From<&semver::Version> for SemverCompatibility {
200 fn from(ver: &semver::Version) -> Self {
201 if let Some(m) = NonZeroU64::new(ver.major) {
202 return SemverCompatibility::Major(m);
203 }
204 if let Some(m) = NonZeroU64::new(ver.minor) {
205 return SemverCompatibility::Minor(m);
206 }
207 SemverCompatibility::Patch(ver.patch)
208 }
209}
210
211impl PackageId {
212 pub fn as_activations_key(self) -> ActivationsKey {
213 ActivationsKey(self.name(), self.version().into(), self.source_id())
214 }
215}
216
217#[derive(Clone)]
218pub struct DepsFrame {
219 pub parent: Summary,
220 pub just_for_error_messages: bool,
221 pub remaining_siblings: RcVecIter<DepInfo>,
222}
223
224impl DepsFrame {
225 /// Returns the least number of candidates that any of this frame's siblings
226 /// has.
227 ///
228 /// The `remaining_siblings` array is already sorted with the smallest
229 /// number of candidates at the front, so we just return the number of
230 /// candidates in that entry.
231 fn min_candidates(&self) -> usize {
232 self.remaining_siblings
233 .peek()
234 .map(|(_, candidates, _)| candidates.len())
235 .unwrap_or(0)
236 }
237
238 pub fn flatten(&self) -> impl Iterator<Item = (PackageId, &Dependency)> + '_ {
239 self.remaining_siblings
240 .remaining()
241 .map(move |(d, _, _)| (self.parent.package_id(), d))
242 }
243}
244
245impl PartialEq for DepsFrame {
246 fn eq(&self, other: &DepsFrame) -> bool {
247 self.just_for_error_messages == other.just_for_error_messages
248 && self.min_candidates() == other.min_candidates()
249 }
250}
251
252impl Eq for DepsFrame {}
253
254impl PartialOrd for DepsFrame {
255 fn partial_cmp(&self, other: &DepsFrame) -> Option<Ordering> {
256 Some(self.cmp(other))
257 }
258}
259
260impl Ord for DepsFrame {
261 fn cmp(&self, other: &DepsFrame) -> Ordering {
262 self.just_for_error_messages
263 .cmp(&other.just_for_error_messages)
264 .reverse()
265 .then_with(|| self.min_candidates().cmp(&other.min_candidates()))
266 }
267}
268
269/// Note that an `OrdSet` is used for the remaining dependencies that need
270/// activation. This set is sorted by how many candidates each dependency has.
271///
272/// This helps us get through super constrained portions of the dependency
273/// graph quickly and hopefully lock down what later larger dependencies can
274/// use (those with more candidates).
275#[derive(Clone)]
276pub struct RemainingDeps {
277 /// a monotonic counter, increased for each new insertion.
278 time: u32,
279 /// the data is augmented by the insertion time.
280 /// This insures that no two items will cmp eq.
281 /// Forcing the `OrdSet` into a multi set.
282 data: im_rc::OrdSet<(DepsFrame, u32)>,
283}
284
285impl RemainingDeps {
286 pub fn new() -> RemainingDeps {
287 RemainingDeps {
288 time: 0,
289 data: im_rc::OrdSet::new(),
290 }
291 }
292 pub fn push(&mut self, x: DepsFrame) {
293 let insertion_time = self.time;
294 self.data.insert((x, insertion_time));
295 self.time += 1;
296 }
297 pub fn pop_most_constrained(&mut self) -> Option<(bool, (Summary, DepInfo))> {
298 while let Some((mut deps_frame, insertion_time)) = self.data.remove_min() {
299 let just_here_for_the_error_messages = deps_frame.just_for_error_messages;
300
301 // Figure out what our next dependency to activate is, and if nothing is
302 // listed then we're entirely done with this frame (yay!) and we can
303 // move on to the next frame.
304 let sibling = deps_frame.remaining_siblings.iter().next().cloned();
305 if let Some(sibling) = sibling {
306 let parent = Summary::clone(&deps_frame.parent);
307 self.data.insert((deps_frame, insertion_time));
308 return Some((just_here_for_the_error_messages, (parent, sibling)));
309 }
310 }
311 None
312 }
313 pub fn iter(&mut self) -> impl Iterator<Item = (PackageId, &Dependency)> + '_ {
314 self.data.iter().flat_map(|(other, _)| other.flatten())
315 }
316}
317
318/// Information about the dependencies for a crate, a tuple of:
319///
320/// (dependency info, candidates, features activated)
321pub type DepInfo = (Dependency, Rc<Vec<Summary>>, FeaturesSet);
322
323/// All possible reasons that a package might fail to activate.
324///
325/// We maintain a list of conflicts for error reporting as well as backtracking
326/// purposes. Each reason here is why candidates may be rejected or why we may
327/// fail to resolve a dependency.
328#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
329pub enum ConflictReason {
330 /// There was a semver conflict, for example we tried to activate a package
331 /// 1.0.2 but 1.1.0 was already activated (aka a compatible semver version
332 /// is already activated)
333 Semver,
334
335 /// The `links` key is being violated. For example one crate in the
336 /// dependency graph has `links = "foo"` but this crate also had that, and
337 /// we're only allowed one per dependency graph.
338 Links(InternedString),
339
340 /// A dependency listed a feature that wasn't actually available on the
341 /// candidate. For example we tried to activate feature `foo` but the
342 /// candidate we're activating didn't actually have the feature `foo`.
343 MissingFeature(InternedString),
344
345 /// A dependency listed a feature that ended up being a required dependency.
346 /// For example we tried to activate feature `foo` but the
347 /// candidate we're activating didn't actually have the feature `foo`
348 /// it had a dependency `foo` instead.
349 RequiredDependencyAsFeature(InternedString),
350
351 /// A dependency listed a feature for an optional dependency, but that
352 /// optional dependency is "hidden" using namespaced `dep:` syntax.
353 NonImplicitDependencyAsFeature(InternedString),
354}
355
356impl ConflictReason {
357 pub fn is_links(&self) -> bool {
358 matches!(self, ConflictReason::Links(_))
359 }
360
361 pub fn is_missing_feature(&self) -> bool {
362 matches!(self, ConflictReason::MissingFeature(_))
363 }
364
365 pub fn is_required_dependency_as_features(&self) -> bool {
366 matches!(self, ConflictReason::RequiredDependencyAsFeature(_))
367 }
368}
369
370/// A list of packages that have gotten in the way of resolving a dependency.
371/// If resolving a dependency fails then this represents an incompatibility,
372/// that dependency will never be resolve while all of these packages are active.
373/// This is useless if the packages can't be simultaneously activated for other reasons.
374pub type ConflictMap = BTreeMap<PackageId, ConflictReason>;
375
376pub struct RcVecIter<T> {
377 vec: Rc<Vec<T>>,
378 offset: usize,
379}
380
381impl<T> RcVecIter<T> {
382 pub fn new(vec: Rc<Vec<T>>) -> RcVecIter<T> {
383 RcVecIter { vec, offset: 0 }
384 }
385
386 pub fn peek(&self) -> Option<&T> {
387 self.vec.get(self.offset)
388 }
389
390 pub fn remaining(&self) -> impl Iterator<Item = &T> + '_ {
391 self.vec.get(self.offset..).into_iter().flatten()
392 }
393
394 pub fn iter(&mut self) -> impl Iterator<Item = &T> + '_ {
395 let iter = self.vec.get(self.offset..).into_iter().flatten();
396 // This call to `ìnspect()` is used to increment `self.offset` when iterating the inner `Vec`,
397 // while keeping the `ExactSizeIterator` property.
398 iter.inspect(|_| self.offset += 1)
399 }
400}
401
402// Not derived to avoid `T: Clone`
403impl<T> Clone for RcVecIter<T> {
404 fn clone(&self) -> RcVecIter<T> {
405 RcVecIter {
406 vec: self.vec.clone(),
407 offset: self.offset,
408 }
409 }
410}