§Babel: opam repository with pubgrub-opam
§DONE tests
§DONE PubGrub 0.3.0
- Uv is using the development branch of PubGrub.
- There’s a lot of improvements to be had since the last release was 4
years ago.
- The new version bounds will cleanly express version constraints,
e.g. stop converting
<= 1.0.0
to< 1.0.0.1
- It looks like we might be able to manually add conflicts with
add_incompatibility
- Ah, actually this is an internal thing.
- The new version bounds will cleanly express version constraints,
e.g. stop converting
- Completed here.
§DONE conjunctions and disjunctions in filtered package formula
Take
"D" { test & > "2.0.0"}
as an
example. We encode this as,
(filtered-package-formula-variable-version, 1.0.0) -> (D {(test & = >2.0.0)}, *)
(D {(test & = >2.0.0)}, false) -> (`test`, false)
(D {(test & = >2.0.0)}, true) -> (`test`, true), (A, >1.0.0)
(`test`, false)
(`test`, true)
(A, 2.0.0) -> ...
Note we introduce a proxy package that depends on either the filter being false (with versions stripped out), or the filter being true (with versions part of the equation, taking the union on conjunctions and intersection on disjunctions).
Take
"A" { test | !test }
as an example. We
encode this as,
(filtered-package-formula-or, 1.0.0) -> (A {(test | !test)}, *)
(A {(test | !test)}, false) -> (`test`, ∅)
(A {(test | !test)}, true) -> (A {(test | !test)}, *)
(A {(test | !test)}, lhs) -> (`test`, true), (A, *)
(`test`, true)
(A, 1.0.0) -> ...
Note
we combine the versions of the variable test
with an intersection which leads to the
empty set.
§DONE comparison of booleans
done here e.g.
(filtered-package-formula-equality, 1.0.0) -> (A {(test = build)}, *)
versions of A {(test = build)}: false, true
(A {(test = build)}, false) -> ({(test != build)}, *)
versions of {(test != build)}: lhs, rhs
({(test != build)}, lhs) -> (`test`, true), (`build`, false)
versions of `test`: false, true
(`test`, true)
versions of `build`: false, true
(`build`, false)
§DONE add support for a ‘root’ package to support setting variable values
done here
§DONE ocaml-variants.5.3.1+trunk
I’m reproducing a bug in opam-repository’s constraints that Patrick found.
$ opam sw create 5.3.1+trunk
<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><> 🐫
Switch invariant: ["ocaml-variants" {= "5.3.1+trunk"}]
[ERROR] Could not determine which packages to install for this switch:
* No agreement on the version of ocaml-variants:
- (invariant) → ocaml-variants = 5.3.1+trunk → ocaml-compiler < 5.3.0~alpha1 → ocaml = 5.3.0 → ocaml-variants < 5.3.1~
- (invariant) → ocaml-variants = 5.3.1+trunk
You can temporarily relax the switch invariant with `--update-invariant'
* Incompatible packages:
- (invariant) → ocaml-variants = 5.3.1+trunk → ocaml-compiler < 5.3.0~alpha1 → ocaml = 5.3.0 → dkml-base-compiler < 5.3.1~
- (invariant) → ocaml-variants = 5.3.1+trunk
* Incompatible packages:
- (invariant) → ocaml-variants = 5.3.1+trunk → ocaml-compiler < 5.3.0~alpha1 → ocaml = 5.3.0 → ocaml-base-compiler >= 5.3.0~
- (invariant) → ocaml-variants = 5.3.1+trunk
* Missing dependency:
- (invariant) → ocaml-variants = 5.3.1+trunk → ocaml-compiler < 5.3.0~alpha1 → ocaml = 5.3.0 → ocaml-variants < 5.3.1~ →
system-msvc
unmet availability conditions: 'os = "win32"'
After adding conflict-class support (which are required for the ocaml compiler packages) we can reproduce the error:
Because ocaml-system >=5.3.0~, <5.3.1~ depends on Conflict class ocaml-core-compiler ocaml-system and ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) rhs depends on ocaml-system >=5.3.0~, <5.3.1~, ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) rhs depends on Conflict class ocaml-core-compiler ocaml-system. (1)
Because ocaml-base-compiler >=5.3.0~, <5.3.1~ depends on Conflict class ocaml-core-compiler ocaml-base-compiler and (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}) lhs depends on ocaml-base-compiler >=5.3.0~, <5.3.1~, (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}) lhs depends on Conflict class ocaml-core-compiler ocaml-base-compiler.
And because (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}) <lhs | >lhs depends on ocaml-variants >=5.3.0~, <5.3.1~ and ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) lhs depends on (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}), Conflict class ocaml-core-compiler Not ( ocaml-base-compiler ), ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) lhs, ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
And because ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) rhs depends on Conflict class ocaml-core-compiler ocaml-system (1), Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) *, ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
And because (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) lhs depends on ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) and (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) <lhs | >lhs depends on dkml-base-compiler >=5.3.0~, <5.3.1~, Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) *, ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
And because ocaml 5.3.0 depends on (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) and ocaml {(= 5.3.0 & post)} true depends on ocaml 5.3.0, ocaml {(= 5.3.0 & post)} true, Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
And because ocaml {(= 5.3.0 & post)} false depends on `post` false and ocaml-compiler 5.3 depends on ocaml {(= 5.3.0 & post)}, ocaml-compiler 5.3, Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), `post` Not ( false ), ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
And because ocaml-variants 5.3.1+trunk depends on ocaml-compiler 5.3 and ocaml-variants 5.3.1+trunk depends on Conflict class ocaml-core-compiler ocaml-variants, ocaml-variants 5.3.1+trunk depends on `post` false.
And because Root depends on `post` true and Root depends on ocaml-variants 5.3.1+trunk, Root is forbidden.
Let’s break this down.
Because ocaml-system >=5.3.0~, <5.3.1~ depends on Conflict class ocaml-core-compiler ocaml-system and ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) rhs depends on ocaml-system >=5.3.0~, <5.3.1~, ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) rhs depends on Conflict class ocaml-core-compiler ocaml-system. (1)
Because ocaml-system
is in conflict class ocaml-core-compiler
and this formula’s right
hand side (RHS) depends on ocaml-system
,
the RHS of the formula depends on ocaml-system
in the ocaml-core-compiler
conflict class.
Because ocaml-base-compiler >=5.3.0~, <5.3.1~ depends on Conflict class ocaml-core-compiler ocaml-base-compiler and (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}) lhs depends on ocaml-base-compiler >=5.3.0~, <5.3.1~, (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}) lhs depends on Conflict class ocaml-core-compiler ocaml-base-compiler.
Because ocaml-base-compiler
is in conflict class ocaml-core-compiler
and this formula’s left hand
side (LHS) depends on ocaml-base-compiler
,
the LHS of the formula depends on ocaml-base-compiler
in the ocaml-core-compiler
conflict class.
And because (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}) lhs depends on ocaml-variants >=5.3.0~, <5.3.1~ and ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) lhs depends on (ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~}), Conflict class ocaml-core-compiler Not ( ocaml-base-compiler ), ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) lhs, ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
And because the parent
formula’s LHS has it’s RHS depends on ocaml-variants
, and the formula’s LHS depends on
the formula’s LHS (duh), then we can’t select the LHS of the formula
(ocaml-base-compiler
or ocaml-variants
) and not select ocaml-base-compiler
and not select
ocaml-variants (phew).
Basically, the LHS of the
formula depends on either ocaml-base-compiler
or ocaml-variants
.
And because ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) rhs depends on Conflict class ocaml-core-compiler ocaml-system (1), Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) *, ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
Because the formula’s RHS
selects ocaml-system
in conflict class
ocaml-core-compiler
, then we can’t select
(either side of) the formula without selecting ocaml-base-compiler
or ocaml-system
in ocaml-core-compiler
, and ocam-variants
.
And because (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) lhs depends on ((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~}) and (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) lhs depends on dkml-base-compiler >=5.3.0~, <5.3.1~, Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) *, ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
We can’t select this
formula without selecting ocaml-base-compiler
or ocaml-system
in ocaml-core-compiler
, and ocam-variants
. Note dkml-base-compiler
is ignored as there’s not
compatible versions.
And because ocaml 5.3.0 depends on (((ocaml-base-compiler {= >=5.3.0~, <5.3.1~}) | (ocaml-variants {= >=5.3.0~, <5.3.1~})) | (ocaml-system {= >=5.3.0~, <5.3.1~})) | (dkml-base-compiler {= >=5.3.0~, <5.3.1~}) and ocaml {(= 5.3.0 & post)} true depends on ocaml 5.3.0, ocaml {(= 5.3.0 & post)} true, Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
If the proxy package
associated with the filtered package formula ocaml
ocaml {(= 5.3.0 & post)}
is selected (i.e. version
true
) then we must have ocaml-base-compiler
or ocaml-system
in ocaml-core-compiler
, and ocam-variants
.
And because ocaml {(= 5.3.0 & post)} false depends on `post` false and ocaml-compiler 5.3 depends on ocaml {(= 5.3.0 & post)}, ocaml-compiler 5.3, Conflict class ocaml-core-compiler Not ( ocaml-base-compiler | ocaml-system ), `post` Not ( false ), ocaml-variants Not ( >=5.3.0~, <5.3.1~ ) are incompatible.
If we don’t select this
package formula, we need post to be false, and ocaml-compiler
depends on this formula, so we
can’t select ocaml-compiler with post=true
without ocaml-base-compiler
or ocaml-system
in ocaml-core-compiler
, and ocam-variants
.
And because ocaml-variants 5.3.1+trunk depends on ocaml-compiler 5.3 and ocaml-variants 5.3.1+trunk depends on Conflict class ocaml-core-compiler ocaml-variants, ocaml-variants 5.3.1+trunk depends on `post` false.
Because ocaml-variants
depends on ocaml-compiler
and is in conflict class ocaml-core-compiler
, we can’t select it without
having post=false
.
And because Root depends on `post` true and Root depends on ocaml-variants 5.3.1+trunk, Root is forbidden.
Since we set post=true
at the root, we have a
conflict.
This is all a very roundabout way of telling us that we have a conflict class. This provides a good example to explore using a custom error provider for.
After applying the fix, we successfully solve the dependencies:
Solution Set:
opam-version = 2.1.0
(host-arch-arm64, 1)
(base-domains, base)
(ocaml, 5.3.1)
(host-system-other, 1)
(base-threads, base)
os = macos
(base-unix, base)
(ocaml-compiler, 5.3)
(base-bigarray, base)
(ocaml-config, 3)
(base-nnp, base)
(base-effects, base)
(ocaml-variants, 5.3.1+trunk)
post = true
arch = arm64
Resolved Dependency Graph:
(base-bigarray, base)
(base-domains, base) -> (ocaml, 5.3.1)
(base-effects, base) -> (ocaml, 5.3.1)
(base-nnp, base) -> (base-domains, base)
(base-threads, base)
(base-unix, base)
(host-arch-arm64, 1)
(host-system-other, 1)
(ocaml, 5.3.1) -> (ocaml-config, 3), (ocaml-variants, 5.3.1+trunk)
(ocaml-compiler, 5.3) -> (`arch`, arm64), (`opam-version`, 2.1.0), (`os`, macos), (`post`, true), (base-bigarray, base), (base-domains, base), (base-effects, base), (base-nnp, base), (base-threads, base), (base-unix, base), (host-arch-arm64, 1), (host-system-other, 1), (ocaml, 5.3.1)
(ocaml-config, 3) -> (`os`, macos)
(ocaml-variants, 5.3.1+trunk) -> (`opam-version`, 2.1.0), (ocaml-compiler, 5.3)
§TODO a Debian/Alpine encoding in PubGrub, which I think should be much simpler than Opam
TODO and tie into opam with depexts for cross-ecosystem resolutions
§TODO PubGrub custom error provider
- look at
test_package_formula_or_error
- look at
test_opam_repository_ocaml_variants
§TODO optional dependencies
§TODO conflicts
tracking issue here
§TODO boolean and integer filter literals
§TODO statically configure possible variable values
§TODO read up on answer set programming
https://pubgrub-rs-guide.pages.dev/internals/intro is a good starting point