diff options
Diffstat (limited to 'bin')
| -rw-r--r-- | bin/main.ml | 62 |
1 files changed, 56 insertions, 6 deletions
diff --git a/bin/main.ml b/bin/main.ml index 01cdbf8..ca89a12 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -74,12 +74,62 @@ let temporal_report y = _build/default/bin/main.exe up two directories and back down into data/ always finds both files, regardless of the caller's own cwd. - Known limitation, not yet exercised by this project: a `dune install`- - style deployment (executable copied to a prefix with no adjacent _build/ - default/data/) would need a different resolution strategy; there is no - install story yet (README.md: `dune exec` only), so this is not a - regression against anything this project currently supports. *) -let data_dir () = Filename.dirname (Filename.dirname Sys.executable_name) ^ "/data/ef" + RESOLVED: the "known limitation" this comment used to end on -- that a + `dune install`-style deployment (executable copied to a prefix with no + adjacent _build/default/data/) had no resolution strategy, and would exit 2 + unable to find sanctoral.sexp -- is now handled by probing candidates in + order rather than computing one path and hoping. data/dune installs the + four runtime files into <prefix>/share/colitur/ef/. + + Two layouts are probed, and one override short-circuits both: + + [COLITUR_DATA_DIR], when set and non-blank -- an explicit override. It + NEVER falls through: if it is set and does not contain the data, that is + an error naming the directory, not a reason to quietly use different + data. A packager or operator who names a directory has stated an + intent, and silently calendaring off some other copy because theirs was + wrong is precisely the silent substitution this project refuses + everywhere else (CLAUDE.md's first binding decision: divergence is + flagged LOUDLY, never silently swallowed). Getting this wrong is not + hypothetical -- the first version of this function did fall through, and + a deliberately bogus COLITUR_DATA_DIR produced a full, plausible, + entirely un-flagged year off the build tree's data. + + Otherwise, in order: + 1. <exedir>/../share/colitur/ef -- the INSTALLED layout, from an opam or + `dune install` prefix where the binary sits at <prefix>/bin/colitur. + data/dune puts the four runtime files there. + 2. <exedir>/../data/ef -- the BUILD TREE, which is what `dune exec` and + the cram tests use. + + A candidate is accepted only if sanctoral.sexp is actually readable inside + it, not merely because the directory exists: an empty or half-populated + share/colitur/ef (a failed install, a partially removed package) falls + through to a working build tree rather than shadowing it and then failing + at load time with a confusing per-file error. Verified by simulation, not + assumed. + + Environment reads are fine HERE and only here: this is bin/, not the + kernel, whose contract forbids them (CLAUDE.md, "Kernel is total & + deterministic: no wall-clock, randomness, or environment reads"). Nothing + below the CLI ever learns where the data came from -- the loaders take a + path. *) +let data_dir () = + let has_data d = Sys.file_exists (Filename.concat d "sanctoral.sexp") in + let prefix = Filename.dirname (Filename.dirname Sys.executable_name) in + let installed = List.fold_left Filename.concat prefix [ "share"; "colitur"; "ef" ] in + let build_tree = Filename.concat prefix (Filename.concat "data" "ef") in + match Sys.getenv_opt "COLITUR_DATA_DIR" with + | Some d when String.trim d <> "" -> + if has_data d then d + else begin + Printf.eprintf + "colitur: COLITUR_DATA_DIR is set to %s, which contains no sanctoral.sexp\n\ + colitur: refusing to fall back to another data directory -- unset it, or point it at one\n" + d; + exit 2 + end + | _ -> if has_data installed then installed else build_tree (* Loads the universal sanctoral layer and applies the one hand-authored overlay over it (data/ef/adjustments.sexp -- see that file's own header): |
