I needed to install OxCaml quickly on a fresh machine without any opam machinery. This was an excellent excuse to refresh my memory on how distributing system packages for distributions like Debian, Arch, Fedora and Homebrew work.
I've built oxcaml-pkgs to churn out native packages for the distros I need, with details below for future reference.
These all install an oxcaml-compiler package into /opt, since anywhere else
would clash with the native OCaml packaging. The idea is that a consumer can
add this to their PATH to specifically get OxCaml. However, I don't intend
most people to do this manually as I'm wrapping this in my oi
at the moment.
1 The quick installer script
If you just want OxCaml and don't care how, there's a one-shot installer:
curl -fsSL https://oi.thicket.dev/repo/install.sh | sh
The rest of this note is the manual breakdown of what that script automates, mostly so I remember how each packaging system works the next time I revisit it!
2 Debian / Ubuntu
curl -fsSL https://oi.thicket.dev/repo/apt/oxcaml.asc | sudo gpg --dearmor -o /usr/share/keyrings/oxcaml.gpg
# pick your release: noble, resolute, or trixie
echo 'deb [signed-by=/usr/share/keyrings/oxcaml.gpg] https://oi.thicket.dev/repo/apt resolute main' | sudo tee /etc/apt/sources.list.d/oxcaml.list
sudo apt update && sudo apt install oxcaml-compiler
Debian and Ubuntu both maintain packaging metadata in a debian/ directory with various bits of metadata, e.g.:
Source: oxcaml-compiler
Section: devel
Priority: optional
Maintainer: @MAINTAINER@
Build-Depends: debhelper-compat (= 13),
gcc, g++, make, m4, autoconf, perl, rsync, tar, gzip, bzip2, pkg-config, zstd
Standards-Version: 4.7.0
Homepage: https://oxcaml.org
Rules-Requires-Root: no
Package: oxcaml-compiler
Architecture: any
Depends: ${misc:Depends}, libc6
This is then compiled from a source package to an architecture-specific binary one that has a .deb extension.
That's done via scripts that invoke pbuilder in a Docker container for the exact Ubuntu or Debian distro.
I would have used Launchpad as I used to do for opam PPAs back in the day, but it's currently down due to a DDoS so I'm building these myself for now.
3 Fedora 44
sudo tee /etc/yum.repos.d/oxcaml.repo >/dev/null <<'EOF'
[oxcaml]
name=OxCaml
baseurl=https://oi.thicket.dev/repo/rpm/fedora-44
enabled=1
gpgcheck=0
repo_gpgcheck=1
gpgkey=https://oi.thicket.dev/repo/rpm/fedora-44/oxcaml.asc
EOF
sudo dnf install oxcaml-compiler
Fedora's got the DNF package manager, which uses spec files to wrap the build. These source RPMs are then compiled to binary ones via a mock build in a Docker container for that distro.
Once that's done, the repository metadata is assembled using createrepo, and a bunch of files that can be served over HTTP.
4 Arch Linux
curl -fsSL https://oi.thicket.dev/repo/arch/oxcaml.asc | sudo pacman-key --add -
sudo pacman-key --lsign-key <keyid>
sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[oxcaml]
SigLevel = Required DatabaseOptional
Server = https://oi.thicket.dev/repo/arch/$arch
EOF
sudo pacman -Sy oxcaml-compiler
Arch uses PKGBUILD files for its metadata format, which makepkg compiles into a simple .pkg.tar.zst layer in an archlinux container; repo-add then stitches them into a pacman database.
5 Homebrew
My custom Homebrew instructions from yesteryear still work fine, so I just pushed a Homebrew OxCaml formula there and let the brew bot do its magic.
The release flow here is two stage:
tests.ymlrunsbrew test-boton every PR, building the formula and uploading the resulting bottles as CI artifacts.- Then when I manually add the
pr-pulllabel to that PR,publish.ymlfires on thelabeledevent, runsbrew pr-pullto download those built bottles, commits them with the formula tomain, pushes, and deletes the branch.
The only quirk here is to not link it into /opt/homebrew as it would collide with OCaml, so it's marked "keg only" (installed but not symlinked into the prefix). I think we can integrate the brew bottling directly into obuilder just as soon as we add secrets support to the macOS backend.
6 Resurrecting a 2013 GPG key
I did also have to do some GPG shenanigans as my ancient Debian signing key
from 2013 is now rejected because its crypto signature used SHA-1.
Modern gpg, apt and pacman reject SHA-1 certifications outright now
as being too weak, so reprepro refused to trust me anymore.
Upgrading the key gracefully (rather than minting a brand new identity and losing decades worth of signatures) turned out to be fiddly, and I have only the haziest memory of modern keyserver etiquette. The last time I looked at this seriously was chatting to Yaron Minsky about fifteen years ago about his SKS OCaml keyserver!
Anyway, I have a fresh ed25519 signing subkey now, so I'll properly
rebuild the web of trust later on. Maybe tangled.org's
new vouching system would be a good place to anchor a PGP web
of trust again.
7 Reproducing the OxCaml opam directives
The most fiddly part of all this was reproducing the OxCaml build
exactly as its opam directives would, but without opam in the loop
and just a single unified tarball where you can do a make && make install. Distros typically abhor other package managers...
I used a fair amount of agentic coding here: I pinned the
oxcaml-compiler.5.2.0minus31 opam package and had Claude resolve the
patch list and build/install steps into a single shell script, then
verified that the resulting unified patches were byte-identical to what
opam would have assembled.
This is also all fiddly enough that it makes me want to investigate package repositories on ATProto more now...
