Page
Library
Module
Module type
Parameter
Class
Class type
Source
The uncomfortably obvious dependency bridge
dune pkg stores built packages in _build/, invisible to the standard opam switch mechanism that tools like ocamllsp, merlin, and utop rely on. And dune.lock/ plus the built artifacts live only in the project tree — there is no switch-level record to recover them from.
locswijch creates an opam switch that serves dual purpose:
dune.lock/ and _build/.pkg/ — survives dune clean (and even deletion of dune.lock/) and can reconstruct both from switch metadata.Hard links (same filesystem) make both sync and restore zero-cost in disk space. dune clean just decrements link counts; files survive in the switch. A design heresy, the human brain politely refises to process the full trauma of its functionality.
Dune decides whether to re-run a rule from _build/.db, which dune clean deletes — restored target trees alone are never trusted, so a rebuild after restore re-executes every package rule. The fix is dune's own shared cache: run builds with DUNE_CACHE=enabled. The default cache mode (enabled-except-user-rules) excludes lock-dir package actions; with DUNE_CACHE=enabled a post-clean rebuild hard-links everything back from ~/.cache/dune/db in about a second. Use restore to recover the dune.lock/ + _build/.pkg/ state itself; use the dune cache for speed.
locswijch syncRun after dune build. Hard-links package artifacts from _build/_private/default/.pkg/*/target/ into an opam switch prefix. Stores reconstruction metadata (cookie files, digest directory names, per-package .install manifests).
locswijch sync [--switch NAME] [--project DIR]locswijch restoreRun after dune clean (or after losing dune.lock/). Recreates dune.lock/ and _build/_private/default/.pkg/ from the switch via hard-links. Note that the next dune build does not trust the restored targets by themselves (see above) — pair with DUNE_CACHE=enabled for fast rebuilds.
locswijch restore [--switch NAME] [--project DIR]locswijch migrateGenerate dune.lock/ from an existing opam switch. This is a one-way translation of opam package metadata into dune's .pkg format. A full dune build is required afterward (the source artifacts must be fetched and compiled by dune), followed by locswijch sync to augment the switch as backup.
locswijch migrate [--switch NAME] [--project DIR]export DUNE_CACHE=enabled # let dune cache package builds too
# Initial setup
dune pkg lock
dune build
locswijch sync
# After dune clean
dune clean
locswijch restore # recover dune.lock/ + _build/.pkg/
dune build # ~1s — packages hard-linked from the dune cachelocswijch migrate --switch default
# Edit dune-project to declare dependencies
dune build # full rebuild (unavoidable)
locswijch sync # switch now serves as backupdune build # builds project + any new deps
locswijch sync # keep switch in sync (fast, idempotent)--switch NAME, -s NAME — opam switch name. Defaults to project directory basename.--project DIR, -C DIR — project root. Defaults to nearest ancestor containing dune-project._build/_private/default/.pkg/<name>.<ver>-<digest>/ directories, keeping only the digest dirs the current dune.lock builds (asked via dune pkg print-digest, never recomputed). Stale digest dirs — accumulated across migrate iterations — are deleted from both _build/.pkg/ and the switch store; if the dune in PATH lacks print-digest, pruning is skipped with a warning and everything is synced as before.For each package with a target/ subdirectory:
lib/, bin/, etc.)..install manifest listing which files belong to this package..opam-switch/locswijch/.switch-config, switch-state, per-package .opam files, environment.~/.opam/config..opam-switch/locswijch/._build/.pkg/<digest>/target/ tree by hard-linking files back from the switch, using .install manifests to attribute files to packages.If _build and ~/.opam are on different filesystems, hard links are impossible. The tool detects this and falls back to copying with a warning.
opam install cmdliner opam-file-format
dune builddune runtestRuns parser unit tests plus a self-contained end-to-end smoke test: a tiny two-package fixture closure (generated in a temp dir, no compiler, local sources) goes through the full migrate → build → sync → clean → restore → rebuild cycle, and the post-restore rebuild must beat a time threshold that a real package rebuild cannot meet. Requires opam and dune on PATH.
_build/.db (deleted by dune clean) regardless of the restored targets. Use DUNE_CACHE=enabled for that; restore recovers state, not build time.sync is a full re-sync. This is fast (hard-links are O(1) per file) but removes stale files from the switch.PATH orchestrates: trip and sync invoke plain dune, deliberately not opam exec -- dune — opam would resolve the switch from the target project's directory, and a project with a local _opam would supply its own (possibly older) dune. The invoking dune must support the locked dune's language version: locking dune ≥ 3.24 pulls in satellite packages (dune-configurator, ordering, ...) whose dune-project says (lang dune 3.24).migrate time (the switch's solution is host-specific anyway). The generated dune.lock/ is not portable across operating systems.In the interest of transparency, and in keeping with the transparency principles of the EU Artificial Intelligence Act (Regulation (EU) 2024/1689):
Released under the MIT License. © 2026 Cuihtlauac Alvarado.