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