# Updating to the OxCaml 5.2.0-minus39 opam packaging

*2026-08-08 — note*


This [website](https://anil.recoil.org/notes/bushel-lives) is written using the [OxCaml](https://oxcaml.org) language extensions from Jane Street, so I can experiment with [zero allocation](https://anil.recoil.org/notes/oxcaml-httpz) frameworks written in OCaml.  Since [David Allsopp](https://www.dra27.uk) released [oxcaml-minus39](https://github.com/oxcaml/opam-repository/tree/main/packages/oxcaml-compiler/oxcaml-compiler.5.2.0minus39) a few weeks ago, I've updated to that. This first required [figuring out the packaging](https://github.com/oxcaml/opam-repository/pull/59) of some of the third-party dependencies that I require.
Using OxCaml is more complex than vanilla OCaml since many existing
packages fail to compile due to it requiring additional type
annotations or eta-expansions.  However, there's a neat set of guard
packages present in the OxCaml opam overlay that make it straightforward to find
compatible versions.

My own need was for [Eio](https://github.com/ocaml-multicore/eio), as Eio \<=1.3
didn't build under OxCaml. The Jane Street overlay shipped an `eio.1.3+ox`
fork built from a patched branch. [Thomas Leonard](https://github.com/https://roscidus.com) and I fixed the OxCaml build
issues in [Eio 1.4](https://github.com/ocaml-multicore/eio/releases/tag/v1.4),
but found ourselves [blocked from using it](https://notes.roscidus.com/2026/08/07/) due to the system of guard packages.

I figured I'd dig into how this works and make it easier to contribute fixes to Jane Street for third-party packages like mine.
I've opened [PR\#59](https://github.com/oxcaml/opam-repository/pull/59) on OxCaml's repo, and here's an explanation of [how the OxCaml guard machinery works](#how-the-package-guards-work),
how to [let newer releases through](#allowing-newer-packages-through-the-guards),
and [what's new in ox-minus39](#whats-new-in-oxcaml-minus39) itself.

## How the package guards work

The [OxCaml opam-repository](https://github.com/oxcaml/opam-repository) is an
opam overlay that you add alongside the main
[ocaml/opam-repository](https://github.com/ocaml/opam-repository). It supplies
both the [OxCaml](https://anil.recoil.org/projects/oxcaml) compiler itself as well as `+ox` forks of upstream
packages that don't yet compile with the extended compiler.

This overlay repository seeks to ensure that opam can never resolve *around*
one of the patched packages (e.g. if upstream releases a newer version), and
install the unpatched upstream version instead.  The OxCaml maintainers need to
explicitly test the new version and permit it through the OxCaml package
guards.

[David Allsopp](https://www.dra27.uk) did this by adding a couple of opam meta-packages per forked package:
For `eio`, for example, we get:
- `oxcaml-eio.guard`, meaning "the patched eio is *not* installed here".
- `oxcaml-eio-patches.enabled` meaning "the patched package *is* installed here".

The two packages conflict with each other, and a third `oxcaml-patch-guards` package
uses a disjunction of dependencies to require one or the other.
Crucially, the guard packages' `conflicts:` field lists the upstream versions that
are forbidden if we go down the 'unpatched' package route:

```
conflicts: [ "oxcaml-eio-patches" "eio" {< "1.4"} ]
```

This means that every `eio` version before 1.4 was [incompatible](https://github.com/ocaml-multicore/eio/pull/898)
with OxCaml. The solver is then free to look for an [eio 1.4+ release](https://notes.roscidus.com/2026/07/24/)
in upstream opam-repository and select that if available.

## Allowing newer packages through the guards

The nice property of this design is that retiring an OxCaml fork does
not require deleting the `+ox` package, as someone on an older OxCaml compiler still
needs it.

For mdx, for example, it just requires patching the guard package to allow
the new version through:

```diff
--- a/packages/oxcaml-mdx/oxcaml-mdx.guard/opam
+++ b/packages/oxcaml-mdx/oxcaml-mdx.guard/opam
@@ -6,7 +6,7 @@ authors: "David Allsopp"
 license: "CC0-1.0+"
 homepage: "https://oxcaml.org"
 bug-reports: "https://github.com/oxcaml/opam-repository/issues"
-conflicts: [ "oxcaml-mdx-patches" "mdx" ]
+conflicts: [ "oxcaml-mdx-patches" "mdx" {< "2.6.0"} ]
 messages: ["WARNING! An older version of OxCaml is being installed" {oxcaml:version = "archived"}]
 depends: [
   ("oxcaml-merlin" {post} | "oxcaml-merlin-patches" {post})
```

So this now has mdx working, but then I [needed](https://anil.recoil.org/notes/2026w30)
to add the `unix` dependency that mdx 2.6 now needs in the dune rules.
[That fix](https://github.com/ocaml/dune/pull/15592) shipped in
the [dune 3.24.2](https://github.com/ocaml/dune/releases/tag/3.24.2) release,
so I've added that into the diff too.

Is this a good system then? It's certainly an ingenious use of the opam solver,
but the packaging encoding itself is difficult to parse at first glance (although made
much easier with agents; I used my [local deepseek agent](https://anil.recoil.org/notes/language-integrated-llms)
to help me out. I'm hopeful that as [Ryan Gibb](https://ryan.freumh.org) advances his [package management calculus](https://anil.recoil.org/papers/2026-package-calculus), we'll
be able to improve the syntax UI (and the error messages!) in time.

## What's new in OxCaml minus39?

I updated my [Claude OCaml Marketplace](https://anil.recoil.org/notes/aoah-2025-25) skill, which you can [find here](https://github.com/avsm/ocaml-claude-marketplace/tree/main/plugins/ocaml-dev/skills/oxcaml). A few highlights from minus31-39 that caught my eye are:

- Domain preemption has an initial implementation, so a tight compute loop can be interrupted at a poll point instead of starving everything else sharing the domain.
- Arrays of unboxed elements now have their own packed representations (`int8 array`, `float32 array`, `vec512 array` etc), so an array of small numbers isn't a word per element.
- The `borrow_` operator lets you pass a `unique` value where an `aliased` one is wanted and still own it uniquely afterwards.
- Runtime metaprogramming's quotes and splices (`<<e>>` and `$(e)`) only need `-extension-universe beta`, and `[%eval]` is now an ordinary `Eval.eval` function.
- There's an LLDB language plugin to ease debugging oxidised binaries.
- Implicit kinds are enabled so fewer kind annotations need writing out by hand.
- Mode syntax improved so that `(e : @ modes)` parses directly, removing the need for the `: _ @ modes` workaround.
- Any two-constructor variant can now be `[@@or_null]` giving custom `Nope | Yep of 'a` types the same non-allocating null encoding as the built-in `'a or_null`.
- `-O4` exists, being `-O3` plus the mysterious "reaper" pass, which I need to investigate.
- Some things were deleted, such as the block-index array syntax (`.(0)`, `.:(0)`, `.L(i)`), float record indices, and the `_internal` kind escape hatches. I never used these so wasn't affected.

This website is now running on minus39 using [my bleeding edge monorepo](https://github.com/avsm/oxmono/tree/minus39). If you're interested in trying OxCaml yourself, the compiler's stable, but the packaging is still fluid and needs some expertise in how opam works for you to add your own overrides. Hopefully this post will help you navigate that a bit more easily\!
Synopsis: How the OxCaml overlay's guard packages keep incompatible releases out, and how to contribute to it with your own packages.
Words: 926

## Related

- [.plan-26-30: Bananas ripen, models open, and OxCaml releases](https://anil.recoil.org/notes/2026w30) (note, 2026-07-26)
- [Package Managers à la Carte: A Formal Model of Dependency Resolution](https://anil.recoil.org/papers/2026-package-calculus) (paper, 2026-07-01)
- [Language integrated LLMs as an OCaml function](https://anil.recoil.org/notes/language-integrated-llms) (note, 2026-06-14)
- [My (very) fast zero-allocation webserver using OxCaml](https://anil.recoil.org/notes/oxcaml-httpz) (note, 2026-02-01)
- [AoAH Day 25: Claude OCaml Marketplace for all your festive coding needs](https://anil.recoil.org/notes/aoah-2025-25) (note, 2025-12-25)
- [Arise Bushel, my sixth generation oxidised website](https://anil.recoil.org/notes/bushel-lives) (note, 2025-01-29)
- [OxCaml Labs](https://anil.recoil.org/projects/oxcaml) (project, 2025-01-01)

---
Canonical: https://anil.recoil.org/notes/oxcaml-opam-guards
Type: note
Tags: ocaml, oxcaml, packaging, eio
