Skip to content

Commit 105fb4f

Browse files
baszalmstrawolfv
andauthored
feat!: add support for conditional dependencies (#136)
Co-authored-by: Wolf Vollprecht <w.vollprecht@gmail.com>
1 parent 335b123 commit 105fb4f

File tree

60 files changed

+2160
-857
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2160
-857
lines changed

Cargo.lock

Lines changed: 149 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ tracing = "0.1.41"
4141
elsa = "1.11.2"
4242
bitvec = "1.0.1"
4343
serde = { version = "1.0", features = ["derive"], optional = true }
44-
futures = { version = "0.3", default-features = false, features = ["alloc"] }
44+
futures = { version = "0.3", default-features = false, features = ["alloc", "async-await"] }
4545
event-listener = "5.4"
4646
indexmap = "2"
4747
tokio = { version = "1.45", features = ["rt"], optional = true }
@@ -61,3 +61,4 @@ tracing-test = { version = "0.2.5", features = ["no-env-filter"] }
6161
tokio = { version = "1.45.1", features = ["time", "rt"] }
6262
resolvo = { path = ".", features = ["tokio", "version-ranges"] }
6363
serde_json = "1.0"
64+
chumsky = { version = "0.10.1" , features = ["pratt"]}

cpp/build.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,40 @@ fn main() -> anyhow::Result<()> {
7272
constexpr Slice(const T *ptr, uintptr_t len) : ptr(ptr ? const_cast<T*>(ptr) : reinterpret_cast<T*>(sizeof(T))), len(len) {}"
7373
.to_owned(),
7474
);
75+
config.export.body.insert(
76+
"ConditionalRequirement".to_owned(),
77+
r"
78+
/**
79+
* Constructs a new conditional requirement with the specified condition
80+
* and requirement.
81+
*/
82+
constexpr ConditionalRequirement(const ConditionId *condition, Requirement &&requirement) : condition(condition), requirement(std::forward<Requirement>(requirement)) {};
83+
/**
84+
* Constructs a new conditional requirement without a condition.
85+
*/
86+
constexpr ConditionalRequirement(Requirement &&requirement) : condition(nullptr), requirement(std::forward<Requirement>(requirement)) {};
87+
".to_owned());
88+
config.export.body.insert(
89+
"Requirement".to_owned(),
90+
r"
91+
constexpr Requirement(VersionSetId id) : tag(Tag::Single), single({id}) {};
92+
constexpr Requirement(VersionSetUnionId id) : tag(Tag::Union), union_({id}) {};
93+
94+
constexpr bool is_union() const { return tag == Tag::Union; }
95+
constexpr bool is_single() const { return tag == Tag::Single; }
96+
"
97+
.to_owned(),
98+
);
99+
100+
config.export.body.insert(
101+
"Condition".to_owned(),
102+
r"
103+
constexpr Condition(VersionSetId id) : tag(Tag::Requirement), requirement({id}) {};
104+
constexpr Condition(LogicalOperator op, ConditionId lhs, ConditionId rhs) : tag(Tag::Binary), binary({op, lhs, rhs}) {};
105+
106+
constexpr bool is_binary() const { return tag == Tag::Requirement; }
107+
constexpr bool is_requirement() const { return tag == Tag::Binary; }
108+
".to_owned());
75109

76110
cbindgen::Builder::new()
77111
.with_config(config.clone())

cpp/include/resolvo.h

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,6 @@ namespace resolvo {
77
using cbindgen_private::Problem;
88
using cbindgen_private::Requirement;
99

10-
/**
11-
* Specifies a requirement (dependency) of a single version set.
12-
*/
13-
inline Requirement requirement_single(VersionSetId id) {
14-
return cbindgen_private::resolvo_requirement_single(id);
15-
}
16-
17-
/**
18-
* Specifies a requirement (dependency) of the union (logical OR) of multiple version sets.
19-
* A solvable belonging to any of the version sets contained in the union satisfies the
20-
* requirement. This variant is typically used for requirements that can be satisfied by two
21-
* or more version sets belonging to different packages.
22-
*/
23-
inline Requirement requirement_union(VersionSetUnionId id) {
24-
return cbindgen_private::resolvo_requirement_union(id);
25-
}
26-
2710
/**
2811
* Called to solve a package problem.
2912
*
@@ -44,6 +27,7 @@ inline String solve(DependencyProvider &provider, const Problem &problem,
4427
private_api::bridge_version_set_name,
4528
private_api::bridge_solvable_name,
4629
private_api::bridge_version_sets_in_union,
30+
private_api::bridge_resolve_condition,
4731
private_api::bridge_get_candidates,
4832
private_api::bridge_sort_candidates,
4933
private_api::bridge_filter_candidates,

cpp/include/resolvo_dependency_provider.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
namespace resolvo {
99
using cbindgen_private::Candidates;
10+
using cbindgen_private::Condition;
11+
using cbindgen_private::ConditionalRequirement;
12+
using cbindgen_private::ConditionId;
1013
using cbindgen_private::Dependencies;
1114
using cbindgen_private::ExcludedSolvable;
1215
using cbindgen_private::NameId;
@@ -81,6 +84,11 @@ struct DependencyProvider {
8184
*/
8285
virtual Slice<VersionSetId> version_sets_in_union(VersionSetUnionId version_set_union_id) = 0;
8386

87+
/**
88+
* Returns the condition that the given condition id describes
89+
*/
90+
virtual Condition resolve_condition(ConditionId condition) = 0;
91+
8492
/**
8593
* Obtains a list of solvables that should be considered when a package
8694
* with the given name is requested.
@@ -138,14 +146,19 @@ extern "C" inline NameId bridge_version_set_name(void *data, VersionSetId versio
138146
extern "C" inline NameId bridge_solvable_name(void *data, SolvableId solvable_id) {
139147
return reinterpret_cast<DependencyProvider *>(data)->solvable_name(solvable_id);
140148
}
149+
extern "C" inline void bridge_resolve_condition(void *data, ConditionId solvable_id,
150+
Condition *result) {
151+
*result = reinterpret_cast<DependencyProvider *>(data)->resolve_condition(solvable_id);
152+
}
141153

142154
// HACK(clang): For some reason, clang needs this to know that the return type is complete
143155
static_assert(sizeof(Slice<VersionSetId>));
144156

145-
extern "C" inline Slice<VersionSetId> bridge_version_sets_in_union(
146-
void *data, VersionSetUnionId version_set_union_id) {
147-
return reinterpret_cast<DependencyProvider *>(data)->version_sets_in_union(
148-
version_set_union_id);
157+
extern "C" inline void bridge_version_sets_in_union(void *data,
158+
VersionSetUnionId version_set_union_id,
159+
Slice<VersionSetId> *result) {
160+
*result =
161+
reinterpret_cast<DependencyProvider *>(data)->version_sets_in_union(version_set_union_id);
149162
}
150163

151164
extern "C" inline void bridge_get_candidates(void *data, NameId package, Candidates *result) {

cpp/src/lib.rs

Lines changed: 153 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ mod slice;
22
mod string;
33
mod vector;
44

5-
use std::{ffi::c_void, fmt::Display, ptr::NonNull};
5+
use std::{ffi::c_void, fmt::Display, mem::MaybeUninit, ptr::NonNull};
66

77
use resolvo::{HintDependenciesAvailable, KnownDependencies, SolverCache};
88

99
use crate::{slice::Slice, string::String, vector::Vector};
1010

11-
/// A unique identifier for a single solvable or candidate of a package. These ids should not be
12-
/// random but rather monotonic increasing. Although it is fine to have gaps, resolvo will
13-
/// allocate some memory based on the maximum id.
11+
/// A unique identifier for a single solvable or candidate of a package. These
12+
/// ids should not be random but rather monotonic increasing. Although it is
13+
/// fine to have gaps, resolvo will allocate some memory based on the maximum
14+
/// id.
15+
///
1416
/// cbindgen:derive-eq
1517
/// cbindgen:derive-neq
1618
#[repr(C)]
@@ -41,10 +43,11 @@ pub enum Requirement {
4143
/// cbindgen:derive-eq
4244
/// cbindgen:derive-neq
4345
Single(VersionSetId),
44-
/// Specifies a dependency on the union (logical OR) of multiple version sets. A solvable
45-
/// belonging to ANY of the version sets contained in the union satisfies the requirement.
46-
/// This variant is typically used for requirements that can be satisfied by two or more
47-
/// version sets belonging to different packages.
46+
/// Specifies a dependency on the union (logical OR) of multiple version
47+
/// sets. A solvable belonging to ANY of the version sets contained in
48+
/// the union satisfies the requirement. This variant is typically used
49+
/// for requirements that can be satisfied by two or more version sets
50+
/// belonging to different packages.
4851
/// cbindgen:derive-eq
4952
/// cbindgen:derive-neq
5053
Union(VersionSetUnionId),
@@ -156,13 +159,120 @@ impl From<StringId> for resolvo::StringId {
156159
}
157160
}
158161

162+
/// A unique identifier for a single condition.
163+
/// cbindgen:derive-eq
164+
/// cbindgen:derive-neq
165+
#[repr(C)]
166+
#[derive(Copy, Clone)]
167+
pub struct ConditionId {
168+
id: u32,
169+
}
170+
171+
impl From<resolvo::ConditionId> for ConditionId {
172+
fn from(id: resolvo::ConditionId) -> Self {
173+
Self { id: id.as_u32() }
174+
}
175+
}
176+
177+
impl From<ConditionId> for resolvo::ConditionId {
178+
fn from(id: ConditionId) -> Self {
179+
Self::new(id.id)
180+
}
181+
}
182+
183+
/// Specifies the dependency of a solvable on a set of version sets.
184+
/// cbindgen:derive-eq
185+
/// cbindgen:derive-neq
186+
#[repr(C)]
187+
#[derive(Copy, Clone)]
188+
pub enum Condition {
189+
/// Specifies a dependency on a single version set.
190+
///
191+
/// cbindgen:derive-eq
192+
/// cbindgen:derive-neq
193+
Requirement(VersionSetId),
194+
195+
/// Combines two conditions using a logical operator.
196+
///
197+
/// cbindgen:derive-eq
198+
/// cbindgen:derive-neq
199+
Binary(LogicalOperator, ConditionId, ConditionId),
200+
}
201+
202+
/// Defines how multiple conditions are compared to each other.
203+
#[repr(C)]
204+
#[derive(Copy, Clone)]
205+
pub enum LogicalOperator {
206+
And,
207+
Or,
208+
}
209+
210+
impl From<resolvo::LogicalOperator> for LogicalOperator {
211+
fn from(value: resolvo::LogicalOperator) -> Self {
212+
match value {
213+
resolvo::LogicalOperator::And => LogicalOperator::And,
214+
resolvo::LogicalOperator::Or => LogicalOperator::Or,
215+
}
216+
}
217+
}
218+
219+
impl From<LogicalOperator> for resolvo::LogicalOperator {
220+
fn from(value: LogicalOperator) -> Self {
221+
match value {
222+
LogicalOperator::And => resolvo::LogicalOperator::And,
223+
LogicalOperator::Or => resolvo::LogicalOperator::Or,
224+
}
225+
}
226+
}
227+
228+
impl From<resolvo::Condition> for Condition {
229+
fn from(value: resolvo::Condition) -> Self {
230+
match value {
231+
resolvo::Condition::Requirement(id) => Condition::Requirement(id.into()),
232+
resolvo::Condition::Binary(op, lhs, rhs) => {
233+
Condition::Binary(op.into(), lhs.into(), rhs.into())
234+
}
235+
}
236+
}
237+
}
238+
239+
impl From<Condition> for resolvo::Condition {
240+
fn from(value: Condition) -> Self {
241+
match value {
242+
Condition::Requirement(id) => resolvo::Condition::Requirement(id.into()),
243+
Condition::Binary(op, lhs, rhs) => {
244+
resolvo::Condition::Binary(op.into(), lhs.into(), rhs.into())
245+
}
246+
}
247+
}
248+
}
249+
250+
#[derive(Clone)]
251+
#[repr(C)]
252+
pub struct ConditionalRequirement {
253+
/// Optionally a condition that indicates whether the requirement is enabled or not.
254+
pub condition: *const ConditionId,
255+
256+
/// A requirement on another package.
257+
pub requirement: Requirement,
258+
}
259+
260+
impl From<ConditionalRequirement> for resolvo::ConditionalRequirement {
261+
fn from(value: ConditionalRequirement) -> Self {
262+
Self {
263+
condition: unsafe { value.condition.as_ref() }.copied().map(Into::into),
264+
requirement: value.requirement.into(),
265+
}
266+
}
267+
}
268+
159269
#[derive(Default)]
160270
#[repr(C)]
161271
pub struct Dependencies {
162272
/// A pointer to the first element of a list of requirements. Requirements
163273
/// defines which packages should be installed alongside the depending
164274
/// package and the constraints applied to the package.
165-
pub requirements: Vector<Requirement>,
275+
pub requirements: Vector<ConditionalRequirement>,
166276

167277
/// Defines additional constraints on packages that may or may not be part
168278
/// of the solution. Different from `requirements`, packages in this set
@@ -294,7 +404,12 @@ pub struct DependencyProvider {
294404
pub version_sets_in_union: unsafe extern "C" fn(
295405
data: *mut c_void,
296406
version_set_union_id: VersionSetUnionId,
297-
) -> Slice<'static, VersionSetId>,
407+
result: NonNull<Slice<'static, VersionSetId>>,
408+
),
409+
410+
/// Returns the condition that the given condition id describes
411+
pub resolve_condition:
412+
unsafe extern "C" fn(data: *mut c_void, condition: ConditionId, result: NonNull<Condition>),
298413

299414
/// Obtains a list of solvables that should be considered when a package
300415
/// with the given name is requested.
@@ -387,11 +502,32 @@ impl resolvo::Interner for &DependencyProvider {
387502
&self,
388503
version_set_union: resolvo::VersionSetUnionId,
389504
) -> impl Iterator<Item = resolvo::VersionSetId> {
390-
unsafe { (self.version_sets_in_union)(self.data, version_set_union.into()) }
391-
.as_slice()
392-
.iter()
393-
.copied()
394-
.map(Into::into)
505+
let mut result = MaybeUninit::uninit();
506+
unsafe {
507+
(self.version_sets_in_union)(
508+
self.data,
509+
version_set_union.into(),
510+
NonNull::new_unchecked(result.as_mut_ptr()),
511+
);
512+
result.assume_init()
513+
}
514+
.as_slice()
515+
.iter()
516+
.copied()
517+
.map(Into::into)
518+
}
519+
520+
fn resolve_condition(&self, condition: resolvo::ConditionId) -> resolvo::Condition {
521+
let mut result = MaybeUninit::uninit();
522+
unsafe {
523+
(self.resolve_condition)(
524+
self.data,
525+
condition.into(),
526+
NonNull::new_unchecked(result.as_mut_ptr()),
527+
);
528+
result.assume_init()
529+
}
530+
.into()
395531
}
396532
}
397533

@@ -486,7 +622,7 @@ impl resolvo::DependencyProvider for &DependencyProvider {
486622

487623
#[repr(C)]
488624
pub struct Problem<'a> {
489-
pub requirements: Slice<'a, Requirement>,
625+
pub requirements: Slice<'a, ConditionalRequirement>,
490626
pub constraints: Slice<'a, VersionSetId>,
491627
pub soft_requirements: Slice<'a, SolvableId>,
492628
}
@@ -507,7 +643,7 @@ pub extern "C" fn resolvo_solve(
507643
.requirements
508644
.as_slice()
509645
.iter()
510-
.copied()
646+
.cloned()
511647
.map(Into::into)
512648
.collect(),
513649
)
@@ -545,20 +681,6 @@ pub extern "C" fn resolvo_solve(
545681
}
546682
}
547683

548-
#[unsafe(no_mangle)]
549-
#[allow(unused)]
550-
pub extern "C" fn resolvo_requirement_single(version_set_id: VersionSetId) -> Requirement {
551-
Requirement::Single(version_set_id)
552-
}
553-
554-
#[unsafe(no_mangle)]
555-
#[allow(unused)]
556-
pub extern "C" fn resolvo_requirement_union(
557-
version_set_union_id: VersionSetUnionId,
558-
) -> Requirement {
559-
Requirement::Union(version_set_union_id)
560-
}
561-
562684
#[cfg(test)]
563685
mod tests {
564686
use super::*;

0 commit comments

Comments
 (0)