this repo has no description
at main 133 lines 4.4 kB view raw
1open Bos 2 3(** To extract the library names for a given package, without using dune, we 4 5 1. parse the META file of the package with ocamlfind to see which libraries 6 exist and what their archive name (.cma filename) is. 7 8 2. use ocamlobjinfo to get a list of all modules within the archives. EDIT: 9 it seems this step is now skipped. 10 11 This code assumes that the META file lists for every library an archive 12 [archive_name], and that for this cma archive exists a corresponsing 13 [archive_name].ocamlobjinfo file. *) 14 15type library = { 16 name : string; 17 archive_name : string option; 18 dir : string option; 19 deps : string list; 20} 21 22type t = { meta_dir : Fpath.t; libraries : library list } 23 24let read_libraries_from_pkg_defs ~library_name pkg_defs = 25 try 26 let archive_filename = 27 try Some (Fl_metascanner.lookup "archive" [ "byte" ] pkg_defs) 28 with _ -> ( 29 try Some (Fl_metascanner.lookup "archive" [ "native" ] pkg_defs) 30 with _ -> None) 31 in 32 33 let deps = 34 try 35 let deps_str = Fl_metascanner.lookup "requires" [] pkg_defs in 36 (* The deps_str is a string of space-separated package names, e.g. "a b c" *) 37 (* We use Astring to split the string into a list of package names *) 38 Astring.String.fields ~empty:false deps_str 39 with _ -> [] 40 in 41 42 let dir = 43 List.find_opt (fun d -> d.Fl_metascanner.def_var = "directory") pkg_defs 44 in 45 let dir = Option.map (fun d -> d.Fl_metascanner.def_value) dir in 46 let archive_name = 47 Option.bind archive_filename (fun a -> 48 let file_name_len = String.length a in 49 if file_name_len > 0 then Some (Filename.chop_extension a) else None) 50 in 51 [ { name = library_name; archive_name; dir; deps } ] 52 with Not_found -> [] 53 54let process_meta_file file = 55 let () = Format.eprintf "process_meta_file: %s\n%!" (Fpath.to_string file) in 56 let meta_dir = Fpath.parent file in 57 let meta = 58 OS.File.with_ic file (fun ic () -> Fl_metascanner.parse ic) () 59 |> Result.get_ok 60 in 61 let base_library_name = 62 if Fpath.basename file = "META" then Fpath.parent file |> Fpath.basename 63 else Fpath.get_ext file 64 in 65 let rec extract_name_and_archive ~prefix 66 ((name, pkg_expr) : string * Fl_metascanner.pkg_expr) = 67 let library_name = prefix ^ "." ^ name in 68 let libraries = 69 read_libraries_from_pkg_defs ~library_name pkg_expr.pkg_defs 70 in 71 let child_libraries = 72 pkg_expr.pkg_children 73 |> List.map (extract_name_and_archive ~prefix:library_name) 74 |> List.flatten 75 in 76 libraries @ child_libraries 77 in 78 let libraries = 79 read_libraries_from_pkg_defs ~library_name:base_library_name meta.pkg_defs 80 in 81 let is_not_private (lib : library) = 82 not 83 (String.split_on_char '.' lib.name 84 |> List.exists (fun x -> x = "__private__")) 85 in 86 let libraries = 87 libraries 88 @ (meta.pkg_children 89 |> List.map (extract_name_and_archive ~prefix:base_library_name) 90 |> List.flatten) 91 |> List.filter is_not_private 92 in 93 { meta_dir; libraries } 94 95let libname_of_archive v = 96 let { meta_dir; libraries } = v in 97 List.fold_left 98 (fun acc (x : library) -> 99 match x.archive_name with 100 | None -> acc 101 | Some archive_name -> 102 let dir = 103 match x.dir with 104 | None -> meta_dir 105 | Some x -> Fpath.(meta_dir // v x) 106 in 107 Fpath.Map.update 108 Fpath.(dir / archive_name) 109 (function 110 | None -> Some x.name 111 | Some y -> 112 Logs.err (fun m -> 113 m "Multiple libraries for archive %s: %s and %s." 114 archive_name x.name y); 115 Some y) 116 acc) 117 Fpath.Map.empty libraries 118 119let directories v = 120 let { meta_dir; libraries } = v in 121 List.fold_left 122 (fun acc x -> 123 match x.dir with 124 | None | Some "" -> Fpath.Set.add meta_dir acc 125 | Some x -> ( 126 let dir = Fpath.(meta_dir // v x) in 127 (* NB. topkg installs a META file that points to a ../topkg-care directory 128 that is installed by the topkg-care package. We filter that out here, 129 though I've not thought of a good way to sort out the `topkg-care` package *) 130 match OS.Dir.exists dir with 131 | Ok true -> Fpath.Set.add dir acc 132 | _ -> acc)) 133 Fpath.Set.empty libraries