let quote s = Printf.sprintf "%S" s (* Each entry is an opam field, the command to append to it, and a substring that identifies our command if it is already present (for idempotency). The per-package hooks deliberately don't mention the odd binary — they are plain coreutils so recording works even when odd isn't installed — so the marker can't just be [exe]. *) let fields ~exe = [ (* post-install (incl. same-version rebuilds): mark the package stale. [mkdir -p] then [touch], passing prefix/name as $1/$2 to avoid quoting opam variables into the script. *) ( "post-install-commands", {|["sh" "-c" "mkdir -p \"$1/odoc/$2\" && touch \"$1/odoc/$2/.odd-stale\"" "--" "%{prefix}%" "%{name}%"] {error-code = 0}|}, ".odd-stale" ); (* post-remove: delete the package's docs outright. Run as an argv list (no shell), so a prefix with spaces needs no quoting. *) ( "post-remove-commands", {|["rm" "-rf" "%{prefix}%/odoc/%{name}%"] {error-code = 0}|}, "/odoc/%{name}%" ); (* post-session: the one hook that needs odd — rebuild stale packages and refresh the landing page. *) ( "post-session-commands", Printf.sprintf {|[%s "sync" "--prefix" "%%{prefix}%%"] {success}|} (quote exe), exe ); ] let opam_option_arg field value = Printf.sprintf "%s+=%s" field value let print ~exe = List.iter (fun (field, value, _marker) -> Printf.printf "opam option --global '%s'\n" (opam_option_arg field value)) (fields ~exe) let already_set field marker = let cmd = Bos.Cmd.(v "opam" % "option" % "--global" % field) in match Bos.OS.Cmd.(run_out ~err:err_null cmd |> to_string ~trim:true) with | Ok current -> Astring.String.is_infix ~affix:marker current | Error _ -> false let apply ~exe = List.fold_left (fun acc (field, value, marker) -> Result.bind acc @@ fun () -> if already_set field marker then ( Printf.printf "%s already configured; skipping\n" field; Ok ()) else let cmd = Bos.Cmd.( v "opam" % "option" % "--global" % opam_option_arg field value) in match Bos.OS.Cmd.run cmd with | Ok () -> Printf.printf "added to %s\n" field; Ok () | Error (`Msg m) -> Error (`Msg (Printf.sprintf "failed to set %s: %s" field m))) (Ok ()) (fields ~exe)