17 Feb 2025

Previous: 10 Feb 2025 Next: 25 Feb 2025
  1. Babel: opam repository with pubgrub-opam

    1. X tests

    2. X 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.
      • Completed here.
    3. X 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.

    4. X 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)
      
    5. X add support for a ‘root’ package to support setting variable values

      done here

    6. X 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)
      
    7. O a Debian/Alpine encoding in PubGrub, which I think should be much simpler than Opam

      1. O and tie into opam with depexts for cross-ecosystem resolutions

    8. O PubGrub custom error provider

      • look at test_package_formula_or_error
      • look at test_opam_repository_ocaml_variants
    9. O optional dependencies

    10. O conflicts

      tracking issue here

    11. O boolean and integer filter literals

    12. O statically configure possible variable values

    13. O read up on answer set programming

      https://pubgrub-rs-guide.pages.dev/internals/intro is a good starting point

  2. Teaching

    1. Supervision 2 of Robinson Computer Networking