A fork of mtelver's day10 project
1(** Doc tools layer management for odoc toolchain.
2
3 Split into two layers:
4 1. Driver layer (shared): odoc_driver_voodoo, sherlodoc, odoc-md
5 - Built once with OCaml 5.x
6 - These tools just need executables, don't need to match target compiler
7
8 2. Odoc layer (per OCaml version): odoc
9 - Must be built with same OCaml version as target packages
10 - .cmt/.cmti files have version-specific formats
11
12 All tools are pinned to odoc 3.1 from the configured repo/branch. *)
13
14let driver_packages = [ "odoc"; "odoc-parser"; "odoc-md"; "sherlodoc"; "odoc-driver" ]
15let odoc_packages = [ "odoc"; "odoc-parser" ]
16
17(** Return the local repo path to bind-mount for doc tools, if any. *)
18let local_repo_mount ~(config : Config.t) =
19 match Local_repo.find_for_packages ~local_repos:config.local_repos driver_packages with
20 | Some (path, _) -> Some (path, "/home/opam/local/odoc")
21 | None -> None
22
23(** Compute hash for the shared driver layer.
24 Depends on repo/branch and compiler layers (when stacking on pre-built compiler). *)
25let driver_layer_hash ~(config : Config.t) ~(compiler_layers : string list) =
26 let source_component = match Local_repo.find_for_packages ~local_repos:config.local_repos driver_packages with
27 | Some (path, _) -> Local_repo.repo_hash path
28 | None -> config.doc_tools_repo ^ "|" ^ config.doc_tools_branch
29 in
30 let components = [ "driver"; source_component ] @ compiler_layers in
31 String.concat "|" components |> Digest.string |> Digest.to_hex
32
33(** Directory name for the driver layer *)
34let driver_layer_name ~(config : Config.t) ~(compiler_layers : string list) =
35 "doc-driver-" ^ driver_layer_hash ~config ~compiler_layers
36
37(** Full path to the driver layer *)
38let driver_layer_path ~(config : Config.t) ~(compiler_layers : string list) =
39 let os_key = Config.os_key ~config in
40 Path.(config.dir / os_key / driver_layer_name ~config ~compiler_layers)
41
42(** Generate build script for the shared driver layer.
43 Builds odoc_driver_voodoo, sherlodoc, and odoc-md.
44 When [needs_compiler] is true, installs ocaml-base-compiler.5.2.1 first.
45 When false (compiler comes from lower layers), skips compiler installation. *)
46let driver_build_script ~(config : Config.t) ~needs_compiler =
47 let repo = config.doc_tools_repo in
48 let branch = config.doc_tools_branch in
49 let pin_cmds = match Local_repo.find_for_packages ~local_repos:config.local_repos driver_packages with
50 | Some (_, matched) ->
51 let local_mount = "/home/opam/local/odoc" in
52 let from_local = List.map (fun pkg ->
53 Printf.sprintf "opam pin add -yn %s %s" pkg local_mount
54 ) matched in
55 let from_git = List.filter_map (fun pkg ->
56 if List.mem pkg matched then None
57 else Some (Printf.sprintf "opam pin add -yn %s %s#%s" pkg repo branch)
58 ) driver_packages in
59 from_local @ from_git
60 | None ->
61 List.map (fun pkg ->
62 Printf.sprintf "opam pin add -yn %s %s#%s" pkg repo branch
63 ) driver_packages
64 in
65 let compiler_cmds = if needs_compiler then [ "opam install -y ocaml-base-compiler.5.2.1" ] else [] in
66 String.concat " && "
67 (compiler_cmds
68 @ pin_cmds
69 @ [ "opam install -y odoc-driver odoc-md sherlodoc";
70 "eval $(opam env) && sherlodoc js > /home/opam/sherlodoc.js";
71 "which odoc_driver_voodoo && which sherlodoc" ])
72
73(** Check if driver layer exists *)
74let driver_exists ~(config : Config.t) ~(compiler_layers : string list) : bool =
75 Sys.file_exists (driver_layer_path ~config ~compiler_layers)
76
77(** Get the hash/name for the driver layer *)
78let get_driver_hash ~(config : Config.t) ~(compiler_layers : string list) : string =
79 driver_layer_name ~config ~compiler_layers
80
81(** Check if odoc_driver_voodoo is available in the driver layer *)
82let has_odoc_driver_voodoo ~(config : Config.t) ~(compiler_layers : string list) : bool =
83 let voodoo_path = Path.(driver_layer_path ~config ~compiler_layers / "fs" / "home" / "opam" / ".opam" / "default" / "bin" / "odoc_driver_voodoo") in
84 Sys.file_exists voodoo_path
85
86(** Path to sherlodoc.js within the driver layer *)
87let sherlodoc_js_path ~(config : Config.t) ~(compiler_layers : string list) =
88 Path.(driver_layer_path ~config ~compiler_layers / "fs" / "home" / "opam" / "sherlodoc.js")
89
90(* --- Per-version odoc layer --- *)
91
92(** Compute hash for the per-version odoc layer.
93 Depends on OCaml version, repo/branch, and compiler layers. *)
94let odoc_layer_hash ~(config : Config.t) ~(ocaml_version : OpamPackage.t) ~(compiler_layers : string list) =
95 let compiler_pkg = OpamPackage.to_string ocaml_version in
96 let source_component = match Local_repo.find_for_packages ~local_repos:config.local_repos odoc_packages with
97 | Some (path, _) -> Local_repo.repo_hash path
98 | None -> config.doc_tools_repo ^ "|" ^ config.doc_tools_branch
99 in
100 let components = [ "odoc"; compiler_pkg; source_component ] @ compiler_layers in
101 String.concat "|" components |> Digest.string |> Digest.to_hex
102
103(** Directory name for the odoc layer *)
104let odoc_layer_name ~(config : Config.t) ~(ocaml_version : OpamPackage.t) ~(compiler_layers : string list) =
105 "doc-odoc-" ^ odoc_layer_hash ~config ~ocaml_version ~compiler_layers
106
107(** Full path to the odoc layer *)
108let odoc_layer_path ~(config : Config.t) ~(ocaml_version : OpamPackage.t) ~(compiler_layers : string list) =
109 let os_key = Config.os_key ~config in
110 Path.(config.dir / os_key / odoc_layer_name ~config ~ocaml_version ~compiler_layers)
111
112(** Generate build script for the per-version odoc layer.
113 Builds odoc with the specified OCaml version, pinned to 3.1 from repo/branch.
114 When [needs_compiler] is false, skips compiler installation (comes from lower layers). *)
115let odoc_build_script ~(config : Config.t) ~(ocaml_version : OpamPackage.t) ~needs_compiler =
116 let repo = config.doc_tools_repo in
117 let branch = config.doc_tools_branch in
118 let compiler_pkg = OpamPackage.to_string ocaml_version in
119 let pin_cmds = match Local_repo.find_for_packages ~local_repos:config.local_repos odoc_packages with
120 | Some (_, matched) ->
121 let local_mount = "/home/opam/local/odoc" in
122 let from_local = List.map (fun pkg ->
123 Printf.sprintf "opam pin add -yn %s %s" pkg local_mount
124 ) matched in
125 let from_git = List.filter_map (fun pkg ->
126 if List.mem pkg matched then None
127 else Some (Printf.sprintf "opam pin add -yn %s %s#%s" pkg repo branch)
128 ) odoc_packages in
129 from_local @ from_git
130 | None ->
131 List.map (fun pkg ->
132 Printf.sprintf "opam pin add -yn %s %s#%s" pkg repo branch
133 ) odoc_packages
134 in
135 let compiler_cmds = if needs_compiler then [ Printf.sprintf "opam install -y %s" compiler_pkg ] else [] in
136 String.concat " && "
137 (compiler_cmds
138 @ pin_cmds
139 @ [ "opam install -y odoc";
140 "eval $(opam env) && which odoc && odoc --version" ])
141
142(** Check if odoc layer exists for this OCaml version *)
143let odoc_exists ~(config : Config.t) ~(ocaml_version : OpamPackage.t) ~(compiler_layers : string list) : bool =
144 Sys.file_exists (odoc_layer_path ~config ~ocaml_version ~compiler_layers)
145
146(** Get the hash/name for the odoc layer *)
147let get_odoc_hash ~(config : Config.t) ~(ocaml_version : OpamPackage.t) ~(compiler_layers : string list) : string =
148 odoc_layer_name ~config ~ocaml_version ~compiler_layers
149
150(** Check if odoc is available in the odoc layer *)
151let has_odoc ~(config : Config.t) ~(ocaml_version : OpamPackage.t) ~(compiler_layers : string list) : bool =
152 let odoc_path = Path.(odoc_layer_path ~config ~ocaml_version ~compiler_layers / "fs" / "home" / "opam" / ".opam" / "default" / "bin" / "odoc") in
153 Sys.file_exists odoc_path