resolvo/lib.rs
1//! Implements a SAT solver for dependency resolution based on the CDCL
2//! algorithm (conflict-driven clause learning)
3//!
4//! The CDCL algorithm is masterly explained in [An Extensible
5//! SAT-solver](http://minisat.se/downloads/MiniSat.pdf). Regarding the data structures used, we
6//! mostly follow the approach taken by [libsolv](https://github.com/openSUSE/libsolv). The code of
7//! libsolv is, however, very low level C, so if you are looking for an
8//! introduction to CDCL, you are encouraged to look at the paper instead or to
9//! keep reading through this codebase and its comments.
10
11#![deny(missing_docs)]
12#![deny(unnameable_types)]
13
14mod conditional_requirement;
15pub mod conflict;
16pub(crate) mod internal;
17mod requirement;
18pub mod runtime;
19pub mod snapshot;
20mod solver;
21pub mod utils;
22
23use std::{
24 any::Any,
25 fmt::{Debug, Display},
26};
27
28pub use conditional_requirement::{Condition, ConditionalRequirement, LogicalOperator};
29pub use internal::arena::ArenaId;
30pub use internal::id::{
31 ConditionId, NameId, SolvableId, SolvableOrRootId, StringId, VersionSetId, VersionSetUnionId,
32};
33use itertools::Itertools;
34pub use requirement::Requirement;
35pub use solver::{Problem, Solver, SolverCache, UnsolvableOrCancelled};
36pub use utils::{Mapping, MappingIter};
37
38/// An object that is used by the solver to query certain properties of
39/// different internalized objects.
40pub trait Interner {
41 /// Returns an object that can be used to display the given solvable in a
42 /// user-friendly way.
43 ///
44 /// When formatting the solvable, it should it include both the name of
45 /// the package and any other identifying properties.
46 fn display_solvable(&self, solvable: SolvableId) -> impl Display + '_;
47
48 /// Returns an object that can be used to display the name of a solvable in
49 /// a user-friendly way.
50 fn display_solvable_name(&self, solvable: SolvableId) -> impl Display + '_ {
51 self.display_name(self.solvable_name(solvable))
52 }
53
54 /// Returns an object that can be used to display multiple solvables in a
55 /// user-friendly way. For example the conda provider should only display
56 /// the versions (not build strings etc.) and merges multiple solvables
57 /// into one line.
58 ///
59 /// When formatting the solvables, both the name of the package and any
60 /// other identifying properties should be displayed.
61 fn display_merged_solvables(&self, solvables: &[SolvableId]) -> impl Display + '_ {
62 if solvables.is_empty() {
63 return String::new();
64 }
65
66 let versions = solvables
67 .iter()
68 .map(|&id| self.display_solvable(id).to_string())
69 .sorted()
70 .unique()
71 .format(" | ");
72
73 let name = self.display_solvable_name(solvables[0]);
74 format!("{name} {versions}")
75 }
76
77 /// Returns an object that can be used to display the given name in a
78 /// user-friendly way.
79 fn display_name(&self, name: NameId) -> impl Display + '_;
80
81 /// Returns an object that can be used to display the given version set in a
82 /// user-friendly way.
83 ///
84 /// The name of the package should *not* be included in the display. Where
85 /// appropriate, this information is added.
86 fn display_version_set(&self, version_set: VersionSetId) -> impl Display + '_;
87
88 /// Displays the string with the given id.
89 fn display_string(&self, string_id: StringId) -> impl Display + '_;
90
91 /// Returns the name of the package that the specified version set is
92 /// associated with.
93 fn version_set_name(&self, version_set: VersionSetId) -> NameId;
94
95 /// Returns the name of the package for the given solvable.
96 fn solvable_name(&self, solvable: SolvableId) -> NameId;
97
98 /// Returns the version sets comprising the given union.
99 ///
100 /// The implementor must take care that the order in which the version sets
101 /// are returned is deterministic.
102 fn version_sets_in_union(
103 &self,
104 version_set_union: VersionSetUnionId,
105 ) -> impl Iterator<Item = VersionSetId>;
106
107 /// Resolves how a condition should be represented in the solver.
108 ///
109 /// Internally, the solver uses `ConditionId` to represent conditions. This
110 /// allows implementers to have a custom representation for conditions that
111 /// differ from the representation of the solver.
112 fn resolve_condition(&self, condition: ConditionId) -> Condition;
113}
114
115/// Defines implementation specific behavior for the solver and a way for the
116/// solver to access the packages that are available in the system.
117#[allow(async_fn_in_trait)]
118pub trait DependencyProvider: Sized + Interner {
119 /// Given a set of solvables, return the candidates that match the given
120 /// version set or if `inverse` is true, the candidates that do *not* match
121 /// the version set.
122 async fn filter_candidates(
123 &self,
124 candidates: &[SolvableId],
125 version_set: VersionSetId,
126 inverse: bool,
127 ) -> Vec<SolvableId>;
128
129 /// Obtains a list of solvables that should be considered when a package
130 /// with the given name is requested.
131 async fn get_candidates(&self, name: NameId) -> Option<Candidates>;
132
133 /// Sort the specified solvables based on which solvable to try first. The
134 /// solver will iteratively try to select the highest version. If a
135 /// conflict is found with the highest version the next version is
136 /// tried. This continues until a solution is found.
137 async fn sort_candidates(&self, solver: &SolverCache<Self>, solvables: &mut [SolvableId]);
138
139 /// Returns the dependencies for the specified solvable.
140 async fn get_dependencies(&self, solvable: SolvableId) -> Dependencies;
141
142 /// Whether the solver should stop the dependency resolution algorithm.
143 ///
144 /// This method gets called at the beginning of each unit propagation round
145 /// and before potentially blocking operations (like
146 /// [Self::get_dependencies] and [Self::get_candidates]). If it returns
147 /// `Some(...)`, the solver will stop and return
148 /// [UnsolvableOrCancelled::Cancelled].
149 fn should_cancel_with_value(&self) -> Option<Box<dyn Any>> {
150 None
151 }
152}
153
154/// A list of candidate solvables for a specific package. This is returned from
155/// [`DependencyProvider::get_candidates`].
156#[derive(Default, Clone, Debug)]
157pub struct Candidates {
158 /// A list of all solvables for the package.
159 pub candidates: Vec<SolvableId>,
160
161 /// Optionally the id of the solvable that is favored over other solvables.
162 /// The solver will first attempt to solve for the specified solvable
163 /// but will fall back to other candidates if no solution could be found
164 /// otherwise.
165 ///
166 /// The same behavior can be achieved by sorting this candidate to the top
167 /// using the [`DependencyProvider::sort_candidates`] function but using
168 /// this method provides better error messages to the user.
169 pub favored: Option<SolvableId>,
170
171 /// If specified this is the Id of the only solvable that can be selected.
172 /// Although it would also be possible to simply return a single
173 /// candidate using this field provides better error messages to the
174 /// user.
175 pub locked: Option<SolvableId>,
176
177 /// A hint to the solver that the dependencies of some of the solvables are
178 /// also directly available. This allows the solver to request the
179 /// dependencies of these solvables immediately. Having the dependency
180 /// information available might make the solver much faster because it
181 /// has more information available up-front which provides the solver with a
182 /// more complete picture of the entire problem space. However, it might
183 /// also be the case that the solver doesnt actually need this
184 /// information to form a solution. In general though, if the
185 /// dependencies can easily be provided one should provide them up-front.
186 pub hint_dependencies_available: HintDependenciesAvailable,
187
188 /// A list of solvables that are available but have been excluded from the
189 /// solver. For example, a package might be excluded from the solver
190 /// because it is not compatible with the runtime. The solver will not
191 /// consider these solvables when forming a solution but will use
192 /// them in the error message if no solution could be found.
193 pub excluded: Vec<(SolvableId, StringId)>,
194}
195
196/// Defines for which candidates dependencies are available without the
197/// [`DependencyProvider`] having to perform extra work, e.g. it's cheap to
198/// request them.
199#[derive(Default, Clone, Debug)]
200pub enum HintDependenciesAvailable {
201 /// None of the dependencies are available up-front. The dependency provide
202 /// will have to do work to find the dependencies.
203 #[default]
204 None,
205
206 /// All the dependencies are available up-front. Querying them is cheap.
207 All,
208
209 /// Only the dependencies for the specified solvables are available.
210 /// Querying the dependencies for these solvables is cheap. Querying
211 /// dependencies for other solvables is expensive.
212 Some(Vec<SolvableId>),
213}
214
215/// Holds information about the dependencies of a package.
216#[derive(Debug, Clone)]
217#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
218#[cfg_attr(feature = "serde", serde(untagged))]
219pub enum Dependencies {
220 /// The dependencies are known.
221 Known(KnownDependencies),
222 /// The dependencies are unknown, so the parent solvable should be excluded
223 /// from the solution.
224 ///
225 /// The string provides more information about why the dependencies are
226 /// unknown (e.g. an error message).
227 Unknown(StringId),
228}
229
230/// Holds information about the dependencies of a package when they are known.
231#[derive(Default, Clone, Debug)]
232#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
233pub struct KnownDependencies {
234 /// Defines which packages should be installed alongside the depending
235 /// package and the constraints applied to the package.
236 #[cfg_attr(
237 feature = "serde",
238 serde(default, skip_serializing_if = "Vec::is_empty")
239 )]
240 pub requirements: Vec<ConditionalRequirement>,
241
242 /// Defines additional constraints on packages that may or may not be part
243 /// of the solution. Different from `requirements`, packages in this set
244 /// are not necessarily included in the solution. Only when one or more
245 /// packages list the package in their `requirements` is the
246 /// package also added to the solution.
247 ///
248 /// This is often useful to use for optional dependencies.
249 #[cfg_attr(
250 feature = "serde",
251 serde(default, skip_serializing_if = "Vec::is_empty")
252 )]
253 pub constrains: Vec<VersionSetId>,
254}