End-to-end sync against a fake switch and a fake driver. The driver stands in for odoc_driver_opam: it logs the order it was called in and creates the package's doc directory. $ mkpkg() { mkdir -p "prefix/.opam-switch/packages/$1.$2"; cat > "prefix/.opam-switch/packages/$1.$2/opam"; } $ mkpkg a 1 < opam-version: "2.0" > EOF $ mkpkg b 1 < opam-version: "2.0" > depends: [ "a" ] > EOF $ cat > driver.sh <<'EOF' > #!/bin/sh > pkg="$1" > [ "$pkg" = "bad" ] && { echo "boom" >&2; exit 1; } > echo "$pkg" >> "$OPAM_SWITCH_PREFIX/build-order" > mkdir -p "$OPAM_SWITCH_PREFIX/odoc/$pkg" > echo ok > "$OPAM_SWITCH_PREFIX/odoc/$pkg/index.html" > EOF $ chmod +x driver.sh The post-install hook marks a package stale by touching a marker file; the post-remove hook just deletes the package's doc directory. Stand in for them: $ stale() { mkdir -p "prefix/odoc/$1"; touch "prefix/odoc/$1/.odd-stale"; } $ removed() { rm -rf "prefix/odoc/$1"; } No stale markers is a no-op with no output: $ odd sync --prefix prefix --driver ./driver.sh Marked packages are rebuilt in dependency order, regardless of the order they were marked in (b was marked first but depends on a): $ stale b $ stale a $ odd sync --prefix prefix --driver ./driver.sh odd: 2 built, 0 failed $ cat prefix/build-order a b A successful build clears the marker, and a landing page lists the documented packages: $ test -e prefix/odoc/a/.odd-stale || echo consumed consumed $ grep -o '
  • .*
  • ' prefix/odoc/index.html | sed 's/<[^>]*>//g' a 1 b 1 A failing package stays marked for the next session; others still build: $ mkpkg bad 1 < opam-version: "2.0" > EOF $ stale bad $ stale a $ odd sync --prefix prefix --driver ./driver.sh odd: 1 built, 1 failed odd: see $TESTCASE_ROOT/prefix/var/cache/odd/log $ test -e prefix/odoc/bad/.odd-stale && echo still-marked still-marked A removal deletes the package's docs (post-remove hook); the landing page drops it the next time sync runs. Note that bad, still marked from the previous run, is retried (and fails again) — the designed retry behaviour: $ test -d prefix/odoc/b && echo present present $ rm -r prefix/.opam-switch/packages/b.1 $ removed b $ odd sync --prefix prefix --driver ./driver.sh odd: 0 built, 1 failed odd: see $TESTCASE_ROOT/prefix/var/cache/odd/log $ test -d prefix/odoc/b || echo gone gone $ grep -o '
  • .*
  • ' prefix/odoc/index.html | sed 's/<[^>]*>//g' a 1 A missing driver leaves marked packages stale and is reported via the summary (sync still exits 0 — it is hook-facing): $ stale a $ odd sync --prefix prefix --driver ./no-such-driver odd: 0 built, 2 failed odd: see $TESTCASE_ROOT/prefix/var/cache/odd/log $ ls prefix/odoc/a/.odd-stale prefix/odoc/bad/.odd-stale prefix/odoc/a/.odd-stale prefix/odoc/bad/.odd-stale rebuild is the user-facing equivalent: it marks packages stale itself and fails loudly: $ odd rebuild --prefix prefix --driver ./driver.sh a bad odd: 1 built, 1 failed odd: see $TESTCASE_ROOT/prefix/var/cache/odd/log [1] $ odd rebuild --prefix prefix --driver ./driver.sh nosuchpkg odd: not installed in this switch: nosuchpkg [124]