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}