this repo has no description
at main 361 lines 12 kB view raw
1(* Voodoo *) 2 3let ( >>= ) = Result.bind 4 5type pkg = { 6 name : string; 7 version : string; 8 universe : string; 9 blessed : bool; 10 files : Fpath.t list; 11} 12 13let prep_path = ref "prep" 14 15(* We mark the paths that contain compiled units for both packages and libraries 16 by dropping in a marker file. The contents of the file is unimportant, as we 17 can determine which package or library we're looking at simply by its path. *) 18let lib_marker = ".odoc_lib_marker" 19let pkg_marker = ".odoc_pkg_marker" 20 21let top_dir pkg = 22 if pkg.blessed then Fpath.(v "p" / pkg.name / pkg.version) 23 else Fpath.(v "u" / pkg.universe / pkg.name / pkg.version) 24 25(* Use output from Voodoo Prep as input *) 26 27let find_universe_and_version pkg_name = 28 Bos.OS.Dir.contents Fpath.(v !prep_path / "universes") >>= fun universes -> 29 let universe = 30 match 31 List.find_opt 32 (fun u -> 33 match Bos.OS.Dir.exists Fpath.(u / pkg_name) with 34 | Ok b -> b 35 | Error _ -> false) 36 universes 37 with 38 | Some u -> Ok u 39 | None -> Error (`Msg (Format.sprintf "Failed to find package %s" pkg_name)) 40 in 41 universe >>= fun u -> 42 Bos.OS.Dir.contents ~rel:true Fpath.(u / pkg_name) >>= fun version -> 43 match (Fpath.segs u, version) with 44 | _ :: _ :: u :: _, [ version ] -> Ok (u, Fpath.to_string version) 45 | _ -> Error (`Msg (Format.sprintf "Failed to find package %s" pkg_name)) 46 47(* Given a directory containing for example [a.cma] and [b.cma], this 48 function returns a Fpath.Map.t mapping [dir/a.cma -> a] and [dir/b.cma -> b] *) 49let libname_of_archives_of_dir dir = 50 let files_res = Bos.OS.Dir.contents dir in 51 match files_res with 52 | Error _ -> Fpath.Map.empty 53 | Ok files -> 54 List.fold_left 55 (fun acc file -> 56 let base = Fpath.basename file in 57 if Astring.String.is_suffix ~affix:".cma" base then 58 let libname = String.sub base 0 (String.length base - 4) in 59 Fpath.Map.add Fpath.(dir / libname) libname acc 60 else acc) 61 Fpath.Map.empty files 62 63let metas_of_pkg pkg = 64 List.filter 65 (fun p -> 66 let filename = Fpath.filename p in 67 filename = "META") 68 pkg.files 69 70let of_voodoo pkg = 71 let metas = metas_of_pkg pkg in 72 73 let pkg_path = 74 Fpath.(v "prep" / "universes" / pkg.universe / pkg.name / pkg.version) 75 in 76 77 (* a map from libname to the set of dependencies of that library *) 78 let (all_lib_deps, cmi_only_libs) : 79 Util.StringSet.t Util.StringMap.t * (Fpath.t * string) list = 80 List.fold_left 81 (fun (d, c) meta -> 82 let full_meta_path = Fpath.(pkg_path // meta) in 83 let m = Library_names.process_meta_file full_meta_path in 84 let d' = 85 List.fold_left 86 (fun acc lib -> 87 Util.StringMap.add lib.Library_names.name 88 (Util.StringSet.of_list ("stdlib" :: lib.Library_names.deps)) 89 acc) 90 d m.libraries 91 in 92 let c' = 93 List.fold_left 94 (fun acc (lib : Library_names.library) -> 95 match (lib.archive_name, lib.dir) with 96 | None, Some dir -> 97 Logs.debug (fun m -> m "Found cmi_only_lib in dir: %s" dir); 98 (Fpath.(m.meta_dir / dir), lib.name) :: acc 99 | None, None -> acc 100 | Some _, _ -> acc) 101 c m.libraries 102 in 103 (d', c')) 104 (Util.StringMap.empty, []) metas 105 in 106 107 let ss_pp fmt ss = Format.fprintf fmt "[%d]" (Util.StringSet.cardinal ss) in 108 Logs.debug (fun m -> 109 m "all_lib_deps: %a\n%!" 110 Fmt.(list ~sep:comma (pair ~sep:comma string ss_pp)) 111 (Util.StringMap.bindings all_lib_deps)); 112 113 let docs = Opam.classify_docs pkg_path (Some pkg.name) pkg.files in 114 let mlds, assets, other_docs = Packages.mk_mlds docs in 115 116 let config = 117 let config_file = 118 Fpath.(pkg_path / "doc" / pkg.name / "odoc-config.sexp") 119 in 120 match Bos.OS.File.read config_file with 121 | Error (`Msg msg) -> 122 Logs.debug (fun m -> 123 m "No config file found: %a\n%s\n%!" Fpath.pp config_file msg); 124 Global_config.empty 125 | Ok s -> 126 Logs.debug (fun m -> m "Config file: %a\n%!" Fpath.pp config_file); 127 Global_config.parse s 128 in 129 130 Logs.debug (fun m -> 131 m "Config.packages: %s\n%!" (String.concat ", " config.deps.packages)); 132 let meta_libraries : Packages.libty list = 133 metas 134 |> List.filter_map (fun meta_file -> 135 let full_meta_path = Fpath.(pkg_path // meta_file) in 136 let m = Library_names.process_meta_file full_meta_path in 137 let libname_of_archive = Library_names.libname_of_archive m in 138 Fpath.Map.iter 139 (fun k v -> Logs.debug (fun m -> m "%a,%s\n%!" Fpath.pp k v)) 140 libname_of_archive; 141 142 let directories = Library_names.directories m in 143 Some 144 (List.concat_map 145 (fun directory -> 146 Logs.debug (fun m -> 147 m "Processing directory: %a\n%!" Fpath.pp directory); 148 Packages.Lib.v ~libname_of_archive ~pkg_name:pkg.name 149 ~dir:directory ~cmtidir:None ~all_lib_deps ~cmi_only_libs 150 ~id_override:None) 151 Fpath.(Set.to_list directories))) 152 |> List.flatten 153 in 154 155 (* Check the main package lib directory even if there's no meta file *) 156 let non_meta_libraries = 157 let libdirs_without_meta = 158 List.filter 159 (fun p -> 160 match Fpath.segs p with 161 | "lib" :: _ :: _ 162 when Sys.is_directory Fpath.(pkg_path // p |> to_string) -> 163 not 164 (List.exists 165 (fun lib -> 166 Fpath.equal 167 Fpath.(to_dir_path lib.Packages.dir) 168 Fpath.(to_dir_path (pkg_path // p))) 169 meta_libraries) 170 | _ -> false) 171 pkg.files 172 in 173 174 Logs.debug (fun m -> 175 m "libdirs_without_meta: %a\n%!" 176 Fmt.(list ~sep:comma Fpath.pp) 177 (List.map (fun p -> Fpath.(pkg_path // p)) libdirs_without_meta)); 178 179 Logs.debug (fun m -> 180 m "lib dirs: %a\n%!" 181 Fmt.(list ~sep:comma Fpath.pp) 182 (List.map (fun (lib : Packages.libty) -> lib.dir) meta_libraries)); 183 184 List.map 185 (fun libdir -> 186 let libname_of_archive = 187 libname_of_archives_of_dir Fpath.(pkg_path // libdir) 188 in 189 Logs.debug (fun m -> 190 m "Processing directory without META: %a" Fpath.pp libdir); 191 Packages.Lib.v ~libname_of_archive ~pkg_name:pkg.name 192 ~dir:Fpath.(pkg_path // libdir) 193 ~cmtidir:None ~all_lib_deps ~cmi_only_libs:[] ~id_override:None) 194 libdirs_without_meta 195 |> List.flatten 196 in 197 let libraries = meta_libraries @ non_meta_libraries in 198 let pkg_dir = top_dir pkg in 199 let doc_dir = Fpath.(pkg_dir / "doc") in 200 let result = 201 { 202 Packages.name = pkg.name; 203 version = pkg.version; 204 libraries; 205 mlds; 206 assets; 207 selected = true; 208 remaps = []; 209 other_docs; 210 pkg_dir; 211 doc_dir; 212 config; 213 } 214 in 215 result 216 217let pp ppf v = 218 Format.fprintf ppf "n: %s v: %s u: %s [\n" v.name v.version v.universe; 219 List.iter (fun fp -> Format.fprintf ppf "%a\n" Fpath.pp fp) v.files; 220 Format.fprintf ppf "]\n%!" 221 222let () = ignore pp 223 224let find_pkg pkg_name ~blessed = 225 let contents = 226 Bos.OS.Dir.fold_contents ~dotfiles:true 227 (fun p acc -> p :: acc) 228 [] 229 Fpath.(v !prep_path) 230 in 231 match contents with 232 | Error _ -> None 233 | Ok c -> ( 234 let sorted = List.sort (fun p1 p2 -> Fpath.compare p1 p2) c in 235 let last, packages = 236 List.fold_left 237 (fun (cur_opt, acc) file -> 238 match Fpath.segs file with 239 | "prep" :: "universes" :: u :: p :: v :: (_ :: _ as rest) 240 when p = pkg_name -> ( 241 let file = Fpath.v (Astring.String.concat ~sep:"/" rest) in 242 match cur_opt with 243 | Some cur 244 when cur.name = p && cur.version = v && cur.universe = u -> 245 (Some { cur with files = file :: cur.files }, acc) 246 | _ -> 247 ( Some 248 { 249 name = p; 250 version = v; 251 universe = u; 252 blessed; 253 files = [ file ]; 254 }, 255 cur_opt :: acc )) 256 | _ -> (cur_opt, acc)) 257 (None, []) sorted 258 in 259 let packages = List.filter_map (fun x -> x) (last :: packages) in 260 match packages with 261 | [ package ] -> Some package 262 | [] -> 263 Logs.err (fun m -> m "No package found for %s" pkg_name); 264 None 265 | _ -> 266 Logs.err (fun m -> m "Multiple packages found for %s" pkg_name); 267 None) 268 269let occurrence_file_of_pkg pkg = 270 let top_dir = top_dir pkg in 271 Fpath.(top_dir / "occurrences-all.odoc-occurrences") 272 273type extra_paths = { 274 pkgs : Fpath.t Util.StringMap.t; 275 libs : Fpath.t Util.StringMap.t; 276 libs_of_pkg : string list Util.StringMap.t; 277} 278 279let empty_extra_paths = 280 { 281 pkgs = Util.StringMap.empty; 282 libs = Util.StringMap.empty; 283 libs_of_pkg = Util.StringMap.empty; 284 } 285 286let extra_paths compile_dir = 287 let contents = 288 Bos.OS.Dir.fold_contents ~dotfiles:true 289 (fun p acc -> p :: acc) 290 [] compile_dir 291 in 292 let add_libs pkgname libname libs_of_pkg = 293 Util.StringMap.update pkgname 294 (function None -> Some [ libname ] | Some l -> Some (libname :: l)) 295 libs_of_pkg 296 in 297 let pkgs, libs, libs_of_pkg = 298 match contents with 299 | Error _ -> 300 (Util.StringMap.empty, Util.StringMap.empty, Util.StringMap.empty) 301 | Ok c -> 302 List.fold_left 303 (fun (pkgs, libs, libs_of_pkg) abs_path -> 304 let path = Fpath.rem_prefix compile_dir abs_path |> Option.get in 305 match Fpath.segs path with 306 | [ "p"; pkg; _version; "doc"; libname; l ] when l = lib_marker -> 307 Logs.debug (fun m -> m "Found lib marker: %a" Fpath.pp path); 308 ( pkgs, 309 Util.StringMap.add libname (Fpath.parent path) libs, 310 add_libs pkg libname libs_of_pkg ) 311 | [ "p"; pkg; _version; "doc"; l ] when l = pkg_marker -> 312 Logs.debug (fun m -> m "Found pkg marker: %a" Fpath.pp path); 313 ( Util.StringMap.add pkg (Fpath.parent path) pkgs, 314 libs, 315 libs_of_pkg ) 316 | [ "u"; _universe; pkg; _version; "doc"; libname; l ] 317 when l = lib_marker -> 318 Logs.debug (fun m -> m "Found lib marker: %a" Fpath.pp path); 319 ( pkgs, 320 Util.StringMap.add libname (Fpath.parent path) libs, 321 add_libs pkg libname libs_of_pkg ) 322 | [ "u"; _universe; pkg; _version; "doc"; l ] when l = pkg_marker -> 323 Logs.debug (fun m -> m "Found pkg marker: %a" Fpath.pp path); 324 ( Util.StringMap.add pkg (Fpath.parent path) pkgs, 325 libs, 326 libs_of_pkg ) 327 | _ -> (pkgs, libs, libs_of_pkg)) 328 (Util.StringMap.empty, Util.StringMap.empty, Util.StringMap.empty) 329 c 330 in 331 { pkgs; libs; libs_of_pkg } 332 333let write_lib_markers odoc_dir pkgs = 334 let write file str = 335 match Bos.OS.File.write file str with 336 | Ok () -> () 337 | Error (`Msg msg) -> 338 Logs.err (fun m -> m "Failed to write lib marker: %s" msg) 339 in 340 List.iter 341 (fun (pkg : Packages.t) -> 342 let libs = pkg.libraries in 343 let pkg_path = Odoc_unit.doc_dir pkg in 344 let marker = Fpath.(odoc_dir // pkg_path / pkg_marker) in 345 write marker 346 (Fmt.str 347 "This marks this directory as the location of odoc files for the \ 348 package %s" 349 pkg.name); 350 351 List.iter 352 (fun (lib : Packages.libty) -> 353 let lib_dir = Odoc_unit.lib_dir pkg lib in 354 let marker = Fpath.(odoc_dir // lib_dir / lib_marker) in 355 write marker 356 (Fmt.str 357 "This marks this directory as the location of odoc files for \ 358 library %s in package %s" 359 lib.lib_name pkg.name)) 360 libs) 361 pkgs