diff --git a/CHANGES.md b/CHANGES.md index e617528a5b..74fb71fd79 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -26,6 +26,7 @@ (@panglesd, #1076). - Added a `compile-asset` command (@EmileTrotignon, @panglesd, #1170) - Allow referencing assets (@panglesd, #1171) +- Added a `--asset-path` arg to `html-generate` (@panglesd, #1185) ### Changed diff --git a/src/document/renderer.ml b/src/document/renderer.ml index db10e9c33d..270f70292d 100644 --- a/src/document/renderer.ml +++ b/src/document/renderer.ml @@ -24,7 +24,7 @@ type input = type 'a t = { name : string; render : 'a -> Types.Block.t option -> Types.Document.t -> page list; - extra_documents : 'a -> input -> Types.Document.t list; + filepath : 'a -> Url.Path.t -> Fpath.t; } let document_of_page ~syntax v = diff --git a/src/document/types.ml b/src/document/types.ml index 34e9b502d9..6ab49a3bfe 100644 --- a/src/document/types.ml +++ b/src/document/types.ml @@ -196,13 +196,8 @@ and Source_page : sig end = Source_page -and Asset : sig - type t = { url : Url.Path.t; src : Fpath.t } -end = - Asset - module Document = struct - type t = Page of Page.t | Source_page of Source_page.t | Asset of Asset.t + type t = Page of Page.t | Source_page of Source_page.t end let inline ?(attr = []) desc = Inline.{ attr; desc } diff --git a/src/driver/compile.ml b/src/driver/compile.ml index a9b84c16fb..d42154fa4a 100644 --- a/src/driver/compile.ml +++ b/src/driver/compile.ml @@ -2,21 +2,26 @@ type compiled = Odoc_unit.t -let mk_byhash (pkgs : Odoc_unit.intf Odoc_unit.unit list) = +let mk_byhash (pkgs : Odoc_unit.t list) = List.fold_left - (fun acc (u : Odoc_unit.intf Odoc_unit.unit) -> + (fun acc (u : Odoc_unit.t) -> match u.Odoc_unit.kind with - | `Intf { hash; _ } -> Util.StringMap.add hash u acc) + | `Intf { hash; _ } as kind -> Util.StringMap.add hash { u with kind } acc + | _ -> acc) Util.StringMap.empty pkgs let init_stats (units : Odoc_unit.t list) = - let total, total_impl, non_hidden, mlds, indexes = + let total, total_impl, non_hidden, mlds, assets, indexes = List.fold_left - (fun (total, total_impl, non_hidden, mlds, indexes) (unit : Odoc_unit.t) -> + (fun (total, total_impl, non_hidden, mlds, assets, indexes) + (unit : Odoc_unit.t) -> let total = match unit.kind with `Intf _ -> total + 1 | _ -> total in let total_impl = match unit.kind with `Impl _ -> total_impl + 1 | _ -> total_impl in + let assets = + match unit.kind with `Asset -> assets + 1 | _ -> assets + in let indexes = Fpath.Set.add unit.index.output_file indexes in let non_hidden = match unit.kind with @@ -24,8 +29,8 @@ let init_stats (units : Odoc_unit.t list) = | _ -> non_hidden in let mlds = match unit.kind with `Mld -> mlds + 1 | _ -> mlds in - (total, total_impl, non_hidden, mlds, indexes)) - (0, 0, 0, 0, Fpath.Set.empty) + (total, total_impl, non_hidden, mlds, assets, indexes)) + (0, 0, 0, 0, 0, Fpath.Set.empty) units in @@ -33,6 +38,7 @@ let init_stats (units : Odoc_unit.t list) = Atomic.set Stats.stats.total_impls total_impl; Atomic.set Stats.stats.non_hidden_units non_hidden; Atomic.set Stats.stats.total_mlds mlds; + Atomic.set Stats.stats.total_assets assets; Atomic.set Stats.stats.total_indexes (Fpath.Set.cardinal indexes) open Eio.Std @@ -78,93 +84,99 @@ let find_partials odoc_dir : Odoc_unit.intf Odoc_unit.unit Util.StringMap.t * _ | Error _ -> (* odoc_dir doesn't exist...? *) (Util.StringMap.empty, tbl) let compile ?partial ~partial_dir ?linked_dir:_ (all : Odoc_unit.t list) = - (* let linked_dir = Option.value linked_dir ~default:output_dir in *) - let intf_units, impl_units, mld_units = - List.fold_left - (fun (intf_units, impl_units, page_units) (unit : Odoc_unit.t) -> - match unit with - | { kind = `Intf _; _ } as intf -> - (intf :: intf_units, impl_units, page_units) - | { kind = `Impl _; _ } as impl -> - (intf_units, impl :: impl_units, page_units) - | { kind = `Mld; _ } as mld -> - (intf_units, impl_units, mld :: page_units)) - ([], [], []) all - in - let hashes = mk_byhash intf_units in - let other_hashes, tbl = - match partial with - | Some _ -> find_partials partial_dir - | None -> (Util.StringMap.empty, Hashtbl.create 10) - in - let all_hashes = - Util.StringMap.union (fun _x o1 _o2 -> Some o1) hashes other_hashes + let hashes = mk_byhash all in + let compile_mod = + (* Modules have a more complicated compilation because: + - They have dependencies and must be compiled in the right order + - In Voodoo mode, there might exists already compiled parts *) + let other_hashes, tbl = + match partial with + | Some _ -> find_partials partial_dir + | None -> (Util.StringMap.empty, Hashtbl.create 10) + in + let all_hashes = + Util.StringMap.union (fun _x o1 _o2 -> Some o1) hashes other_hashes + in + let compile_one compile_other hash = + match Util.StringMap.find_opt hash all_hashes with + | None -> + Logs.debug (fun m -> m "Error locating hash: %s" hash); + Error Not_found + | Some unit -> + let deps = match unit.kind with `Intf { deps; _ } -> deps in + let _fibers = + Fiber.List.map + (fun (other_unit : Odoc_unit.intf Odoc_unit.unit) -> + match compile_other other_unit with + | Ok r -> Some r + | Error _exn -> + Logs.debug (fun m -> + m + "Error during compilation of module %s (hash %s, \ + required by %s)" + (Fpath.filename other_unit.input_file) + (match other_unit.kind with + | `Intf { hash; _ } -> hash) + (Fpath.filename unit.input_file)); + None) + deps + in + let includes = Fpath.Set.of_list unit.include_dirs in + Odoc.compile ~output_dir:unit.output_dir ~input_file:unit.input_file + ~includes ~parent_id:unit.parent_id; + Atomic.incr Stats.stats.compiled_units; + + Ok unit + in + let rec compile_mod : + Odoc_unit.intf Odoc_unit.unit -> + (Odoc_unit.intf Odoc_unit.unit, exn) Result.t = + fun unit -> + let hash = match unit.kind with `Intf { hash; _ } -> hash in + match Hashtbl.find_opt tbl hash with + | Some p -> Promise.await p + | None -> + let p, r = Promise.create () in + Hashtbl.add tbl hash p; + let result = compile_one compile_mod hash in + Promise.resolve r result; + result + in + compile_mod in - let compile_one compile_other hash = - match Util.StringMap.find_opt hash all_hashes with - | None -> - Logs.debug (fun m -> m "Error locating hash: %s" hash); - Error Not_found - | Some unit -> - let deps = match unit.kind with `Intf { deps; _ } -> deps in - let _fibers = - Fiber.List.map - (fun other_unit -> - match compile_other other_unit with - | Ok r -> Some r - | Error _exn -> - Logs.debug (fun m -> - m "Missing module %s (hash %s, required by %s)" "TODO" - (* n h *) "TODO" "TODO" (* unit.m_name *)); - None) - deps - in + + let compile (unit : Odoc_unit.t) = + match unit.kind with + | `Intf _ as kind -> + (compile_mod { unit with kind } :> (Odoc_unit.t, _) Result.t) + | `Impl src -> + let includes = Fpath.Set.of_list unit.include_dirs in + let source_id = src.src_id in + Odoc.compile_impl ~output_dir:unit.output_dir + ~input_file:unit.input_file ~includes ~parent_id:unit.parent_id + ~source_id; + Atomic.incr Stats.stats.compiled_impls; + Ok unit + | `Asset -> + Odoc.compile_asset ~output_dir:unit.output_dir ~parent_id:unit.parent_id + ~name:(Fpath.filename unit.input_file); + Atomic.incr Stats.stats.compiled_assets; + Ok unit + | `Mld -> let includes = Fpath.Set.of_list unit.include_dirs in Odoc.compile ~output_dir:unit.output_dir ~input_file:unit.input_file ~includes ~parent_id:unit.parent_id; - Atomic.incr Stats.stats.compiled_units; - + Atomic.incr Stats.stats.compiled_mlds; Ok unit in - - let rec compile_mod : - Odoc_unit.intf Odoc_unit.unit -> - (Odoc_unit.intf Odoc_unit.unit, exn) Result.t = - fun unit -> - let hash = match unit.kind with `Intf { hash; _ } -> hash in - match Hashtbl.find_opt tbl hash with - | Some p -> Promise.await p - | None -> - let p, r = Promise.create () in - Hashtbl.add tbl hash p; - let result = compile_one compile_mod hash in - Promise.resolve r result; - result - in - let to_build = Util.StringMap.bindings hashes |> List.map snd in - let mod_results = Fiber.List.map compile_mod to_build in - let compile_mld (unit : Odoc_unit.mld Odoc_unit.unit) = - let includes = Fpath.Set.of_list unit.include_dirs in - Odoc.compile ~output_dir:unit.output_dir ~input_file:unit.input_file - ~includes ~parent_id:unit.parent_id; - Atomic.incr Stats.stats.compiled_mlds - in - let () = Fiber.List.iter compile_mld mld_units in - let compile_impl (unit : Odoc_unit.impl Odoc_unit.unit) = - let includes = Fpath.Set.of_list unit.include_dirs in - let source_id = match unit.kind with `Impl src -> src.src_id in - Odoc.compile_impl ~output_dir:unit.output_dir ~input_file:unit.input_file - ~includes ~parent_id:unit.parent_id ~source_id; - Atomic.incr Stats.stats.compiled_impls - in - let () = Fiber.List.iter compile_impl impl_units in - let zipped_res = - List.map2 - (fun Odoc_unit.{ kind = `Intf { hash; _ }; _ } b -> (hash, b)) - to_build mod_results - in + let res = Fiber.List.map compile all in + (* For voodoo mode, we need to keep which modules successfully compiled *) let zipped = - List.filter_map (function a, Ok b -> Some (a, b) | _ -> None) zipped_res + List.filter_map + (function + | Ok (Odoc_unit.{ kind = `Intf { hash; _ }; _ } as b) -> Some (hash, b) + | _ -> None) + res in (match partial with | Some l -> marshal (zipped, hashes) Fpath.(l / "index.m") @@ -193,6 +205,7 @@ let link : compiled list -> _ = (match c.kind with | `Intf _ -> Atomic.incr Stats.stats.linked_units | `Mld -> Atomic.incr Stats.stats.linked_mlds + | `Asset -> () | `Impl _ -> Atomic.incr Stats.stats.linked_impls); c in @@ -239,6 +252,9 @@ let html_generate output_dir linked = Odoc.html_generate_source ~search_uris:[] ~output_dir ~input_file ~source:src_path (); Atomic.incr Stats.stats.generated_units + | `Asset -> + Odoc.html_generate_asset ~output_dir ~input_file:l.odoc_file + ~asset_path:l.input_file () | _ -> let db_path = compile_index l.index in let search_uris = [ db_path; Sherlodoc.js_file ] in diff --git a/src/driver/dune_style.ml b/src/driver/dune_style.ml index a626d59a18..54a67f514f 100644 --- a/src/driver/dune_style.ml +++ b/src/driver/dune_style.ml @@ -53,6 +53,9 @@ let of_dune_build dir = version = "1.0"; libraries = [ lib ]; mlds = []; + assets = + [] + (* When dune has a notion of doc assets, do something *); pkg_dir; other_docs = Fpath.Set.empty; } ) diff --git a/src/driver/odoc.ml b/src/driver/odoc.ml index ed3bdc579a..aa5d9855e4 100644 --- a/src/driver/odoc.ml +++ b/src/driver/odoc.ml @@ -45,6 +45,20 @@ let compile ~output_dir ~input_file:file ~includes ~parent_id = Cmd_outputs.( add_prefixed_output cmd compile_output (Fpath.to_string file) lines) +let compile_asset ~output_dir ~name ~parent_id = + let open Cmd in + let output_file = + Some Fpath.(output_dir // parent_id / ("asset-" ^ name ^ ".odoc")) + in + let cmd = + !odoc % "compile-asset" % "--name" % name % "--output-dir" % p output_dir + in + + let cmd = cmd % "--parent-id" % Fpath.to_string parent_id in + let desc = Printf.sprintf "Compiling %s" name in + let lines = Cmd_outputs.submit desc cmd output_file in + Cmd_outputs.(add_prefixed_output cmd compile_output name lines) + let compile_impl ~output_dir ~input_file:file ~includes ~parent_id ~source_id = let open Cmd in let includes = @@ -129,23 +143,19 @@ let compile_index ?(ignore_output = false) ~output_file ~json ~docs ~libs () = Cmd_outputs.( add_prefixed_output cmd link_output (Fpath.to_string output_file) lines) -let html_generate ~output_dir ?index ?(ignore_output = false) ?(assets = []) +let html_generate ~output_dir ?index ?(ignore_output = false) ?(search_uris = []) ~input_file:file () = let open Cmd in let index = match index with None -> empty | Some idx -> v "--index" % p idx in - let assets = - List.fold_left (fun acc filename -> acc % "--asset" % filename) empty assets - in let search_uris = List.fold_left (fun acc filename -> acc % "--search-uri" % p filename) empty search_uris in let cmd = - !odoc % "html-generate" % p file %% assets %% index %% search_uris % "-o" - % output_dir + !odoc % "html-generate" % p file %% index %% search_uris % "-o" % output_dir in let desc = Printf.sprintf "Generating HTML for %s" (Fpath.to_string file) in let lines = Cmd_outputs.submit desc cmd None in @@ -153,6 +163,19 @@ let html_generate ~output_dir ?index ?(ignore_output = false) ?(assets = []) Cmd_outputs.( add_prefixed_output cmd generate_output (Fpath.to_string file) lines) +let html_generate_asset ~output_dir ?(ignore_output = false) ~input_file:file + ~asset_path () = + let open Cmd in + let cmd = + !odoc % "html-generate-asset" % "-o" % output_dir % "--asset-unit" % p file + % p asset_path + in + let desc = Printf.sprintf "Copying asset %s" (Fpath.to_string file) in + let lines = Cmd_outputs.submit desc cmd None in + if not ignore_output then + Cmd_outputs.( + add_prefixed_output cmd generate_output (Fpath.to_string file) lines) + let html_generate_source ~output_dir ?(ignore_output = false) ~source ?(search_uris = []) ~input_file:file () = let open Cmd in @@ -163,7 +186,7 @@ let html_generate_source ~output_dir ?(ignore_output = false) ~source empty search_uris in let cmd = - !odoc % "html-generate-impl" %% file % p source %% search_uris % "-o" + !odoc % "html-generate-source" %% file % p source %% search_uris % "-o" % output_dir in let desc = Printf.sprintf "Generating HTML for %s" (Fpath.to_string source) in diff --git a/src/driver/odoc.mli b/src/driver/odoc.mli index a9cb57b392..06ae01fcf3 100644 --- a/src/driver/odoc.mli +++ b/src/driver/odoc.mli @@ -24,6 +24,8 @@ val compile : parent_id:id -> unit +val compile_asset : output_dir:Fpath.t -> name:string -> parent_id:id -> unit + val link : ?ignore_output:bool -> input_file:Fpath.t -> @@ -48,12 +50,19 @@ val html_generate : output_dir:string -> ?index:Fpath.t -> ?ignore_output:bool -> - ?assets:string list -> ?search_uris:Fpath.t list -> input_file:Fpath.t -> unit -> unit +val html_generate_asset : + output_dir:string -> + ?ignore_output:bool -> + input_file:Fpath.t -> + asset_path:Fpath.t -> + unit -> + unit + val html_generate_source : output_dir:string -> ?ignore_output:bool -> diff --git a/src/driver/odoc_driver.ml b/src/driver/odoc_driver.ml index 069eda6c25..7d92dfc365 100644 --- a/src/driver/odoc_driver.ml +++ b/src/driver/odoc_driver.ml @@ -451,6 +451,7 @@ let render_stats env nprocs = let total = Atomic.get Stats.stats.total_units in let total_impls = Atomic.get Stats.stats.total_impls in let total_mlds = Atomic.get Stats.stats.total_mlds in + let total_assets = Atomic.get Stats.stats.total_assets in let total_indexes = Atomic.get Stats.stats.total_indexes in let bar message total = let open Progress.Line in @@ -474,6 +475,7 @@ let render_stats env nprocs = dline "Compiling" total ++ dline "Compiling impls" total_impls ++ dline "Compiling pages" total_mlds + ++ dline "Compiling assets" total_assets ++ dline "Linking" non_hidden ++ dline "Linking impls" total_impls ++ dline "Linking mlds" total_mlds @@ -481,12 +483,14 @@ let render_stats env nprocs = ++ dline "HTML" (total_impls + non_hidden + total_mlds) ++ line (procs nprocs) ++ descriptions) - (fun comp compimpl compmld link linkimpl linkmld indexes html procs descr -> - let rec inner (a, b, c, d, e, f, i, g, h) = + (fun comp compimpl compmld compassets link linkimpl linkmld indexes html + procs descr -> + let rec inner (a, b, c, j, d, e, f, i, g, h) = Eio.Time.sleep clock 0.1; let a' = Atomic.get Stats.stats.compiled_units in let b' = Atomic.get Stats.stats.compiled_impls in let c' = Atomic.get Stats.stats.compiled_mlds in + let j' = Atomic.get Stats.stats.compiled_assets in let d' = Atomic.get Stats.stats.linked_units in let e' = Atomic.get Stats.stats.linked_impls in let f' = Atomic.get Stats.stats.linked_mlds in @@ -499,6 +503,7 @@ let render_stats env nprocs = comp (a' - a); compimpl (b' - b); compmld (c' - c); + compassets (j' - j); link (d' - d); linkimpl (e' - e); linkmld (f' - f); @@ -506,9 +511,9 @@ let render_stats env nprocs = html (g' - g); procs (h' - h); if g' < non_hidden + total_impls + total_mlds then - inner (a', b', c', d', e', f', i', g', h') + inner (a', b', c', j', d', e', f', i', g', h') in - inner (0, 0, 0, 0, 0, 0, 0, 0, 0)) + inner (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) let run libs verbose packages_dir odoc_dir odocl_dir html_dir stats nb_workers odoc_bin voodoo package_name blessed dune_style = diff --git a/src/driver/odoc_unit.ml b/src/driver/odoc_unit.ml index c55472e3f8..685dd5464e 100644 --- a/src/driver/odoc_unit.ml +++ b/src/driver/odoc_unit.ml @@ -32,7 +32,9 @@ type impl = [ `Impl of impl_extra ] type mld = [ `Mld ] -type t = [ impl | intf | mld ] unit +type asset = [ `Asset ] + +type t = [ impl | intf | mld | asset ] unit let of_packages ~output_dir ~linked_dir ~index_dir (pkgs : Packages.t list) : t list = @@ -90,14 +92,13 @@ let of_packages ~output_dir ~linked_dir ~index_dir (pkgs : Packages.t list) : let output_file = Fpath.(index_dir / pkg.name / Odoc.index_filename) in { pkg_args; output_file; json = false; search_dir = pkg.pkg_dir } in - let make_unit ~kind ~rel_dir ~input_file ~prefix ~pkg ~include_dirs : _ unit = + let make_unit ~name ~kind ~rel_dir ~input_file ~pkg ~include_dirs : _ unit = let ( // ) = Fpath.( // ) in let ( / ) = Fpath.( / ) in - let filename = input_file |> Fpath.rem_ext |> Fpath.basename in let odoc_dir = output_dir // rel_dir in let parent_id = rel_dir |> Odoc.id_of_fpath in - let odoc_file = odoc_dir / (prefix ^ filename ^ ".odoc") in - let odocl_file = linked_dir // rel_dir / (prefix ^ filename ^ ".odocl") in + let odoc_file = odoc_dir / (name ^ ".odoc") in + let odocl_file = linked_dir // rel_dir / (name ^ ".odocl") in { output_dir; pkgname = pkg.Packages.name; @@ -134,7 +135,8 @@ let of_packages ~output_dir ~linked_dir ~index_dir (pkgs : Packages.t list) : let kind = `Intf { hidden; hash = intf.mif_hash; deps } in (include_dirs, kind) in - make_unit ~kind ~rel_dir ~prefix:"" ~input_file:intf.mif_path ~pkg + let name = intf.mif_path |> Fpath.rem_ext |> Fpath.basename in + make_unit ~name ~kind ~rel_dir ~input_file:intf.mif_path ~pkg ~include_dirs in let of_impl pkg libname (impl : Packages.impl) : impl unit option = @@ -154,9 +156,12 @@ let of_packages ~output_dir ~linked_dir ~index_dir (pkgs : Packages.t list) : in `Impl { src_id; src_path } in + let name = + impl.mip_path |> Fpath.rem_ext |> Fpath.basename |> ( ^ ) "impl-" + in let unit = - make_unit ~kind ~rel_dir ~input_file:impl.mip_path ~pkg ~include_dirs - ~prefix:"impl-" + make_unit ~name ~kind ~rel_dir ~input_file:impl.mip_path ~pkg + ~include_dirs in Some unit in @@ -187,15 +192,31 @@ let of_packages ~output_dir ~linked_dir ~index_dir (pkgs : Packages.t list) : in let include_dirs = (output_dir // rel_dir) :: include_dirs in let kind = `Mld in + let name = mld_path |> Fpath.rem_ext |> Fpath.basename |> ( ^ ) "page-" in + let unit = + make_unit ~name ~kind ~rel_dir ~input_file:mld_path ~pkg ~include_dirs + in + [ unit ] + in + let of_asset pkg (asset : Packages.asset) : asset unit list = + let open Fpath in + let { Packages.asset_path; asset_rel_path } = asset in + let rel_dir = + pkg.Packages.pkg_dir / "doc" // Fpath.parent asset_rel_path + |> Fpath.normalize + in + let include_dirs = [] in + let kind = `Asset in let unit = - make_unit ~kind ~rel_dir ~input_file:mld_path ~pkg ~include_dirs - ~prefix:"page-" + let name = asset_path |> Fpath.basename |> ( ^ ) "asset-" in + make_unit ~name ~kind ~rel_dir ~input_file:asset_path ~pkg ~include_dirs in [ unit ] in let of_package (pkg : Packages.t) : t list = let lib_units :> t list list = List.map (of_lib pkg) pkg.libraries in let mld_units :> t list list = List.map (of_mld pkg) pkg.mlds in - List.concat (List.rev_append lib_units mld_units) + let asset_units :> t list list = List.map (of_asset pkg) pkg.assets in + List.concat (lib_units @ mld_units @ asset_units) in List.concat_map of_package pkgs diff --git a/src/driver/odoc_unit.mli b/src/driver/odoc_unit.mli index d27f12c0ca..be617ee12c 100644 --- a/src/driver/odoc_unit.mli +++ b/src/driver/odoc_unit.mli @@ -32,7 +32,9 @@ type impl = [ `Impl of impl_extra ] type mld = [ `Mld ] -type t = [ impl | intf | mld ] unit +type asset = [ `Asset ] + +type t = [ impl | intf | mld | asset ] unit val of_packages : output_dir:Fpath.t -> diff --git a/src/driver/opam.ml b/src/driver/opam.ml index f167d3c6ed..23e43de9a8 100644 --- a/src/driver/opam.ml +++ b/src/driver/opam.ml @@ -135,6 +135,10 @@ let pkg_to_dir_map () = | "doc" :: _pkg :: "odoc-pages" :: _ -> Logs.debug (fun m -> m "Found odoc page: %a" Fpath.pp fpath); + (Fpath.Set.add Fpath.(v prefix // fpath) odoc_pages, others) + | "doc" :: _pkg :: "odoc-assets" :: _ -> + Logs.debug (fun m -> m "Found odoc page: %a" Fpath.pp fpath); + (Fpath.Set.add Fpath.(v prefix // fpath) odoc_pages, others) | "doc" :: _ -> Logs.debug (fun m -> m "Found other doc: %a" Fpath.pp fpath); diff --git a/src/driver/packages.ml b/src/driver/packages.ml index ee4523ec4d..bc7dde6008 100644 --- a/src/driver/packages.ml +++ b/src/driver/packages.ml @@ -29,6 +29,10 @@ type mld = { mld_path : Fpath.t; mld_rel_path : Fpath.t } let pp_mld fmt m = Format.fprintf fmt "%a" Fpath.pp m.mld_path +type asset = { asset_path : Fpath.t; asset_rel_path : Fpath.t } + +let pp_asset fmt m = Format.fprintf fmt "%a" Fpath.pp m.asset_path + type libty = { lib_name : string; archive_name : string; @@ -40,6 +44,7 @@ type t = { version : string; libraries : libty list; mlds : mld list; + assets : asset list; other_docs : Fpath.Set.t; pkg_dir : Fpath.t; } @@ -276,14 +281,34 @@ let of_libs ~packages_dir libs = in ignore libname_of_archive; let mk_mlds pkg_name odoc_pages = + let odig_convention asset_path = + let asset_prefix = + Fpath.(v (Opam.prefix ()) / "doc" / pkg_name / "odoc-assets") + in + let rel_path = Fpath.rem_prefix asset_prefix asset_path in + match rel_path with + | None -> [] + | Some rel_path -> + [ { asset_path; asset_rel_path = Fpath.(v "_assets" // rel_path) } ] + in let prefix = Fpath.(v (Opam.prefix ()) / "doc" / pkg_name / "odoc-pages") in - Fpath.Set.fold - (fun mld_path acc -> - let rel_path = Fpath.rem_prefix prefix mld_path in - match rel_path with - | None -> acc - | Some mld_rel_path -> { mld_path; mld_rel_path } :: acc) - odoc_pages [] + let mlds, assets = + Fpath.Set.fold + (fun path (mld_acc, asset_acc) -> + let rel_path = Fpath.rem_prefix prefix path in + match rel_path with + | None -> (mld_acc, odig_convention path @ asset_acc) + | Some rel_path -> + if Fpath.has_ext "mld" path then + ( { mld_path = path; mld_rel_path = rel_path } :: mld_acc, + asset_acc ) + else + ( mld_acc, + { asset_path = path; asset_rel_path = rel_path } :: asset_acc + )) + odoc_pages ([], []) + in + (mlds, assets) in Fpath.Map.fold (fun dir archives acc -> @@ -310,7 +335,7 @@ let of_libs ~packages_dir libs = pkg = pkg') map in - let mlds = mk_mlds pkg'.name odoc_pages in + let mlds, assets = mk_mlds pkg'.name odoc_pages in Logs.debug (fun m -> m "%d mlds for package %s (from %d odoc_pages)" (List.length mlds) pkg.name @@ -327,6 +352,7 @@ let of_libs ~packages_dir libs = version = pkg.version; libraries; mlds; + assets; other_docs; pkg_dir; }) diff --git a/src/driver/packages.mli b/src/driver/packages.mli index ee4f766663..7e2f7ab7ca 100644 --- a/src/driver/packages.mli +++ b/src/driver/packages.mli @@ -35,6 +35,12 @@ type mld = { mld_path : Fpath.t; mld_rel_path : Fpath.t } val pp_mld : Format.formatter -> mld -> unit +(** {1 Asset units} *) + +type asset = { asset_path : Fpath.t; asset_rel_path : Fpath.t } + +val pp_asset : Format.formatter -> asset -> unit + (** {1 Packages} *) (** Compilation units are associated to libraries, while documentation are @@ -63,6 +69,7 @@ type t = { version : string; libraries : libty list; mlds : mld list; + assets : asset list; other_docs : Fpath.Set.t; pkg_dir : Fpath.t; } diff --git a/src/driver/stats.ml b/src/driver/stats.ml index 70d9c9608b..13624e42f0 100644 --- a/src/driver/stats.ml +++ b/src/driver/stats.ml @@ -6,11 +6,13 @@ type stats = { mutable total_units : int Atomic.t; mutable total_impls : int Atomic.t; mutable total_mlds : int Atomic.t; + mutable total_assets : int Atomic.t; mutable total_indexes : int Atomic.t; mutable non_hidden_units : int Atomic.t; mutable compiled_units : int Atomic.t; mutable compiled_impls : int Atomic.t; mutable compiled_mlds : int Atomic.t; + mutable compiled_assets : int Atomic.t; mutable linked_units : int Atomic.t; mutable linked_impls : int Atomic.t; mutable linked_mlds : int Atomic.t; @@ -25,11 +27,13 @@ let stats = total_units = Atomic.make 0; total_impls = Atomic.make 0; total_mlds = Atomic.make 0; + total_assets = Atomic.make 0; total_indexes = Atomic.make 0; non_hidden_units = Atomic.make 0; compiled_units = Atomic.make 0; compiled_impls = Atomic.make 0; compiled_mlds = Atomic.make 0; + compiled_assets = Atomic.make 0; linked_units = Atomic.make 0; linked_impls = Atomic.make 0; linked_mlds = Atomic.make 0; diff --git a/src/driver/voodoo.ml b/src/driver/voodoo.ml index 8e328687b7..ee09ff3615 100644 --- a/src/driver/voodoo.ml +++ b/src/driver/voodoo.ml @@ -50,32 +50,50 @@ let process_package pkg = let pkg_path = Fpath.(v "prep" / "universes" / pkg.universe / pkg.name / pkg.version) in - - let mlds = + let assets, mlds = List.filter_map (fun p -> let prefix = Fpath.(v "doc" / pkg.name / "odoc-pages") in - + let asset_prefix = Fpath.(v "doc" / pkg.name / "odoc-assets") in + let check_name pkg_name = + if pkg_name <> pkg.name then ( + Logs.err (fun k -> + k + "Error: name in 'doc' dir does not match package name: %s <> \ + %s" + pkg_name pkg.name); + None) + else Some () + in + let ( >>= ) = Option.bind in match Fpath.segs p with | "doc" :: pkg_name :: "odoc-pages" :: _ :: _ -> ( - if pkg_name <> pkg.name then ( - Logs.err (fun k -> - k - "Error: name in 'doc' dir does not match package name: %s \ - <> %s" - pkg_name pkg.name); - None) - else - let rel_path = Fpath.rem_prefix prefix p in - match rel_path with - | None -> None - | Some mld_rel_path -> + check_name pkg_name >>= fun () -> + match Fpath.rem_prefix prefix p with + | None -> None + | Some rel_path -> + let path = Fpath.(pkg_path // p) in + if Fpath.has_ext "mld" p then Some - { Packages.mld_path = Fpath.(pkg_path // p); mld_rel_path }) + (`M { Packages.mld_path = path; mld_rel_path = rel_path }) + else + Some + (`A + { Packages.asset_path = path; asset_rel_path = rel_path }) + ) + | "doc" :: pkg_name :: "odoc-assets" :: _ :: _ -> ( + check_name pkg_name >>= fun () -> + match Fpath.rem_prefix asset_prefix p with + | None -> None + | Some asset_rel_path -> + let asset_path = Fpath.(pkg_path // p) in + Some (`A { Packages.asset_path; asset_rel_path })) | _ -> None) pkg.files + |> List.partition_map (function + | `A asset -> Either.Left asset + | `M mld -> Either.Right mld) in - let libraries = List.filter_map (fun meta_file -> @@ -156,6 +174,7 @@ let process_package pkg = version = pkg.version; libraries; mlds; + assets; other_docs = Fpath.Set.empty; pkg_dir = top_dir pkg; } diff --git a/src/html/generator.ml b/src/html/generator.ml index cdedd10326..58c5e34b27 100644 --- a/src/html/generator.ml +++ b/src/html/generator.ml @@ -535,32 +535,14 @@ module Page = struct if Config.as_json config then Html_fragment_json.make_src ~config ~url ~breadcrumbs [ doc ] else Html_page.make_src ~breadcrumbs ~header ~config ~url title [ doc ] - - let asset ~config { Asset.url; src } = - let filename = Link.Path.as_filename ~is_flat:(Config.flat config) url in - let content ppf = - let ic = open_in_bin (Fpath.to_string src) in - let len = 1024 in - let buf = Bytes.create len in - let rec loop () = - let read = input ic buf 0 len in - if read = len then ( - Format.fprintf ppf "%s" (Bytes.to_string buf); - loop ()) - else if len > 0 then - let buf = Bytes.sub buf 0 read in - Format.fprintf ppf "%s" (Bytes.to_string buf) - in - loop (); - close_in ic - in - { Odoc_document.Renderer.filename; content; children = [] } end let render ~config ~sidebar = function | Document.Page page -> [ Page.page ~config ~sidebar page ] | Source_page src -> [ Page.source_page ~config src ] - | Asset asset -> [ Page.asset ~config asset ] + +let filepath ~config url = + Link.Path.as_filename ~is_flat:(Config.flat config) url let doc ~config ~xref_base_uri b = let resolve = Link.Base xref_base_uri in diff --git a/src/html/generator.mli b/src/html/generator.mli index c08ef80e08..446d2346f7 100644 --- a/src/html/generator.mli +++ b/src/html/generator.mli @@ -4,6 +4,8 @@ val render : Odoc_document.Types.Document.t -> Odoc_document.Renderer.page list +val filepath : config:Config.t -> Odoc_document.Url.Path.t -> Fpath.t + val doc : config:Config.t -> xref_base_uri:string -> diff --git a/src/latex/generator.ml b/src/latex/generator.ml index 63f5cc7f7b..470edec96d 100644 --- a/src/latex/generator.ml +++ b/src/latex/generator.ml @@ -40,10 +40,10 @@ module Link = struct ( List.map segment_to_string dir, String.concat "." (List.map segment_to_string file) ) - let filename url = + let filename ?(add_ext = true) url = let dir, file = get_dir_and_file url in let file = Fpath.(v (String.concat dir_sep (dir @ [ file ]))) in - Fpath.(add_ext "tex" file) + if add_ext then Fpath.add_ext "tex" file else file end let style = function @@ -477,4 +477,6 @@ end let render ~with_children = function | Document.Page page -> [ Page.page ~with_children page ] - | Source_page _ | Asset _ -> [] + | Source_page _ -> [] + +let filepath url = Link.filename ~add_ext:false url diff --git a/src/latex/generator.mli b/src/latex/generator.mli index 3140da0418..d1b6c177aa 100644 --- a/src/latex/generator.mli +++ b/src/latex/generator.mli @@ -6,3 +6,5 @@ val render : with_children:bool -> Odoc_document.Types.Document.t -> Odoc_document.Renderer.page list + +val filepath : Odoc_document.Url.Path.t -> Fpath.t diff --git a/src/manpage/generator.ml b/src/manpage/generator.ml index ccb98f618e..bee3f87751 100644 --- a/src/manpage/generator.ml +++ b/src/manpage/generator.ml @@ -562,4 +562,6 @@ and render_page (p : Page.t) = let render = function | Document.Page page -> [ render_page page ] - | Source_page _ | Asset _ -> [] + | Source_page _ -> [] + +let filepath url = Link.as_filename ~add_ext:false url diff --git a/src/manpage/generator.mli b/src/manpage/generator.mli index 4d80e7664c..e3b0cbed61 100644 --- a/src/manpage/generator.mli +++ b/src/manpage/generator.mli @@ -1 +1,3 @@ val render : Odoc_document.Types.Document.t -> Odoc_document.Renderer.page list + +val filepath : Odoc_document.Url.Path.t -> Fpath.t diff --git a/src/manpage/link.ml b/src/manpage/link.ml index 5be2bf9458..f007aa5510 100644 --- a/src/manpage/link.ml +++ b/src/manpage/link.ml @@ -7,7 +7,7 @@ let segment_to_string (kind, name) = | `Module | `Page | `LeafPage | `Class -> name | _ -> Format.asprintf "%a-%s" Odoc_document.Url.Path.pp_kind kind name -let as_filename (url : Url.Path.t) = +let as_filename ?(add_ext = true) (url : Url.Path.t) = let components = Url.Path.to_list url in let dir, path = Url.Path.split @@ -17,7 +17,7 @@ let as_filename (url : Url.Path.t) = let dir = List.map segment_to_string dir in let path = String.concat "." (List.map segment_to_string path) in let str_path = String.concat Fpath.dir_sep (dir @ [ path ]) in - Fpath.(v str_path + ".3o") + if add_ext then Fpath.(v str_path + ".3o") else Fpath.v str_path let rec is_class_or_module_path (url : Url.Path.t) = match url.kind with diff --git a/src/model/lang.ml b/src/model/lang.ml index 51b53e153d..f65154e4ad 100644 --- a/src/model/lang.ml +++ b/src/model/lang.ml @@ -525,11 +525,7 @@ end = Implementation module rec Page : sig - type child = - | Page_child of string - | Module_child of string - | Source_tree_child of string - | Asset_child of string + type child = Page_child of string | Module_child of string type t = { name : Identifier.Page.t; diff --git a/src/odoc/bin/main.ml b/src/odoc/bin/main.ml index ff3c741ee7..bc8ccccd81 100644 --- a/src/odoc/bin/main.ml +++ b/src/odoc/bin/main.ml @@ -651,6 +651,9 @@ end = struct let is_page input = input |> Fpath.filename |> Astring.String.is_prefix ~affix:"page-" + let is_asset input = + input |> Fpath.filename |> Astring.String.is_prefix ~affix:"asset-" + let link directories page_roots lib_roots input_file output_file current_package warnings_options open_modules = let input = Fs.File.of_string input_file in @@ -664,10 +667,12 @@ end = struct (`Msg "Arguments given to -P and -L cannot be included in each others") else Ok ()) >>= fun () -> - let is_page = is_page input in - (if is_page then Ok None else current_library_of_input lib_roots input) + let is_page_or_asset = is_page input || is_asset input in + (if is_page_or_asset then Ok None + else current_library_of_input lib_roots input) >>= fun current_lib -> - (if is_page then current_package_of_page ~current_package page_roots input + (if is_page_or_asset then + current_package_of_page ~current_package page_roots input else validate_current_package page_roots current_package) >>= fun current_package -> let current_dir = Fs.File.dirname output in @@ -779,6 +784,8 @@ module Make_renderer (R : S) : sig val generate : docs:string -> unit Term.t * Term.info val generate_source : docs:string -> unit Term.t * Term.info + + val generate_asset : docs:string -> unit Term.t * Term.info end = struct let input_odoc = let doc = "Input file." in @@ -906,6 +913,42 @@ end = struct let generate_source ~docs = Generate_source.(cmd, info ~docs) + module Generate_asset = struct + let generate extra output_dir extra_suffix input_file warnings_options + asset_file = + Rendering.generate_asset_odoc ~renderer:R.renderer ~warnings_options + ~output:output_dir ~extra_suffix ~asset_file extra input_file + + let input_odocl = + let doc = "Odoc asset unit." in + Arg.( + required + & opt (some convert_fpath) None + & info [ "asset-unit" ] ~doc ~docv:"asset-FILE.odocl") + + let asset_file = + let doc = "The asset file" in + Arg.( + required + & pos 0 (some convert_fpath) None + & info ~doc ~docv:"FILE.ext" []) + + let cmd = + Term.( + const handle_error + $ (const generate $ R.extra_args $ dst ~create:true () $ extra_suffix + $ input_odocl $ warnings_options $ asset_file)) + + let info ~docs = + let doc = + Format.sprintf "Generate %s files from a $(i,impl-*.odocl)." + R.renderer.name + in + Term.info ~docs ~doc (R.renderer.name ^ "-generate-asset") + end + + let generate_asset ~docs = Generate_asset.(cmd, info ~docs) + module Targets = struct let list_targets output_dir directories extra odoc_file = let odoc_file = Fs.File.of_string odoc_file in @@ -1131,27 +1174,19 @@ module Odoc_html_args = struct in Arg.(value & flag & info ~doc [ "as-json" ]) - let assets = - let doc = - "Assets files. These must match the assets listed as children during the \ - compile phase." - in - Arg.( - value & opt_all convert_fpath [] & info [ "asset" ] ~doc ~docv:"file.ext") - let extra_args = let config semantic_uris closed_details indent theme_uri support_uri - search_uris flat as_json assets = + search_uris flat as_json = let open_details = not closed_details in let html_config = Odoc_html.Config.v ~theme_uri ~support_uri ~search_uris ~semantic_uris ~indent ~flat ~open_details ~as_json () in - { Html_page.html_config; assets } + { Html_page.html_config } in Term.( const config $ semantic_uris $ closed_details $ indent $ theme_uri - $ support_uri $ search_uri $ flat $ as_json $ assets) + $ support_uri $ search_uri $ flat $ as_json) end module Odoc_html = Make_renderer (Odoc_html_args) @@ -1535,6 +1570,7 @@ let () = Odoc_link.(cmd, info ~docs:section_pipeline); Odoc_html.generate ~docs:section_pipeline; Odoc_html.generate_source ~docs:section_pipeline; + Odoc_html.generate_asset ~docs:section_pipeline; Support_files_command.(cmd, info ~docs:section_pipeline); Compile_impl.(cmd, info ~docs:section_pipeline); Indexing.(cmd, info ~docs:section_pipeline); diff --git a/src/odoc/compile.ml b/src/odoc/compile.ml index 432a151fe2..09e59a68aa 100644 --- a/src/odoc/compile.ml +++ b/src/odoc/compile.ml @@ -71,8 +71,6 @@ let parse_parent_child_reference s = in match String.cut ~sep:"-" s with | Some ("page", n) -> Ok (Lang.Page.Page_child (unquote n)) - | Some ("srctree", n) -> Ok (Source_tree_child (unquote n)) - | Some ("asset", n) -> Ok (Asset_child (unquote n)) | Some ("module", n) -> Ok (Module_child (unquote (String.Ascii.capitalize n))) | Some ("src", _) -> Error (`Msg "Implementation unexpected") @@ -85,8 +83,7 @@ let resolve_parent_page resolver f = match Resolver.lookup_page resolver p with | Some r -> Ok r | None -> Error (`Msg "Couldn't find specified parent page")) - | Source_tree_child _ | Module_child _ | Asset_child _ -> - Error (`Msg "Expecting page as parent") + | Module_child _ -> Error (`Msg "Expecting page as parent") in let extract_parent = function | { Paths.Identifier.iv = `Page _; _ } as container -> Ok container @@ -171,7 +168,7 @@ let root_of_compilation_unit ~parent_id ~parents_children ~hidden ~output Filename.chop_extension Fs.File.(to_string @@ basename output) in String.Ascii.(uncapitalize n = uncapitalize filename) - | Asset_child _ | Source_tree_child _ | Page_child _ -> false + | Page_child _ -> false in match parents_children with | Some parents_children -> @@ -215,7 +212,7 @@ let mld ~parent_id ~parents_children ~output ~children ~warnings_options input = let page_name = PageName.make_std root_name in let check_child = function | Lang.Page.Page_child n -> root_name = n - | Asset_child _ | Source_tree_child _ | Module_child _ -> false + | Module_child _ -> false in (if children = [] then (* No children, this is a leaf page. *) diff --git a/src/odoc/fs.ml b/src/odoc/fs.ml index a7d78e9b81..f6a2471bd5 100644 --- a/src/odoc/fs.ml +++ b/src/odoc/fs.ml @@ -21,6 +21,18 @@ type directory = Fpath.t type file = Fpath.t +let mkdir_p dir = + let mkdir d = + try Unix.mkdir (Fpath.to_string d) 0o755 with + | Unix.Unix_error (Unix.EEXIST, _, _) -> () + | exn -> raise exn + in + let rec dirs_to_create p acc = + if Sys.file_exists (Fpath.to_string p) then acc + else dirs_to_create (Fpath.parent p) (p :: acc) + in + List.iter (dirs_to_create dir []) ~f:mkdir + module File = struct type t = file @@ -92,6 +104,28 @@ module File = struct Result.Error (`Msg err) with Sys_error e -> Result.Error (`Msg e) + let copy ~src ~dst = + let with_ open_ close filename f = + let c = open_ (Fpath.to_string filename) in + Odoc_utils.Fun.protect ~finally:(fun () -> close c) (fun () -> f c) + in + let with_ic = with_ open_in_bin close_in_noerr in + let with_oc = with_ open_out_bin close_out_noerr in + try + with_ic src (fun ic -> + mkdir_p (dirname dst); + with_oc dst (fun oc -> + let len = 65536 in + let buf = Bytes.create len in + let rec loop () = + let read = input ic buf 0 len in + if read > 0 then ( + output oc buf 0 read; + loop ()) + in + Ok (loop ()))) + with Sys_error e -> Result.Error (`Msg e) + let exists file = Sys.file_exists (Fpath.to_string file) let rec of_segs_tl acc = function @@ -140,17 +174,7 @@ module Directory = struct let contains ~parentdir f = Fpath.is_rooted ~root:parentdir f - let mkdir_p dir = - let mkdir d = - try Unix.mkdir (Fpath.to_string d) 0o755 with - | Unix.Unix_error (Unix.EEXIST, _, _) -> () - | exn -> raise exn - in - let rec dirs_to_create p acc = - if Sys.file_exists (Fpath.to_string p) then acc - else dirs_to_create (Fpath.parent p) (p :: acc) - in - List.iter (dirs_to_create dir []) ~f:mkdir + let mkdir_p dir = mkdir_p dir let to_string = Fpath.to_string diff --git a/src/odoc/fs.mli b/src/odoc/fs.mli index 09a162fcbf..30eb535545 100644 --- a/src/odoc/fs.mli +++ b/src/odoc/fs.mli @@ -91,6 +91,8 @@ module File : sig val read : t -> (string, [> msg ]) result + val copy : src:t -> dst:t -> (unit, [> msg ]) result + val exists : t -> bool val of_segs : string list -> t diff --git a/src/odoc/html_page.ml b/src/odoc/html_page.ml index 3d4bc2d320..01e2de2ccc 100644 --- a/src/odoc/html_page.ml +++ b/src/odoc/html_page.ml @@ -14,65 +14,12 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) -open Odoc_model +type args = { html_config : Odoc_html.Config.t } -type args = { html_config : Odoc_html.Config.t; assets : Fpath.t list } - -let render { html_config; assets = _ } sidebar page = +let render { html_config } sidebar page = Odoc_html.Generator.render ~config:html_config ~sidebar page -let asset_documents parent_id children asset_paths = - let asset_names = - Odoc_utils.List.filter_map - (function Lang.Page.Asset_child name -> Some name | _ -> None) - children - in - let rec extract paths name = - match paths with - | [] -> (paths, (name, None)) - | x :: xs when Fpath.basename x = name -> (xs, (name, Some x)) - | x :: xs -> - let rest, elt = extract xs name in - (x :: rest, elt) - in - let unmatched, paired_or_missing = - let rec foldmap paths paired = function - | [] -> (paths, paired) - | name :: names -> - let paths, pair = extract paths name in - foldmap paths (pair :: paired) names - in - foldmap asset_paths [] asset_names - in - List.iter - (fun asset -> - Error.raise_warning - (Error.filename_only "this asset was not declared as a child of %s" - (Paths.Identifier.name parent_id) - (Fs.File.to_string asset))) - unmatched; - Odoc_utils.List.filter_map - (fun (name, path) -> - match path with - | None -> - Error.raise_warning (Error.filename_only "asset is missing." name); - None - | Some path -> - let asset_id = - Paths.Identifier.Mk.asset_file - (parent_id, Names.AssetName.make_std name) - in - let url = Odoc_document.Url.Path.from_identifier asset_id in - Some (Odoc_document.Types.Document.Asset { url; src = path })) - paired_or_missing - -let extra_documents args input = - match input with - | Odoc_document.Renderer.CU _unit -> - (* Remove assets from [Document.t] and move their rendering in the main - [render] function to allow to remove the [extra_documents] - machinery? *) - [] - | Page page -> asset_documents page.Lang.Page.name page.children args.assets +let filepath { html_config } url = + Odoc_html.Generator.filepath ~config:html_config url -let renderer = { Odoc_document.Renderer.name = "html"; render; extra_documents } +let renderer = { Odoc_document.Renderer.name = "html"; render; filepath } diff --git a/src/odoc/html_page.mli b/src/odoc/html_page.mli index 7fe18ffb13..1688ad6b4b 100644 --- a/src/odoc/html_page.mli +++ b/src/odoc/html_page.mli @@ -16,6 +16,6 @@ open Odoc_document -type args = { html_config : Odoc_html.Config.t; assets : Fpath.t list } +type args = { html_config : Odoc_html.Config.t } val renderer : args Renderer.t diff --git a/src/odoc/latex.ml b/src/odoc/latex.ml index a1b62c247f..78e1d621f5 100644 --- a/src/odoc/latex.ml +++ b/src/odoc/latex.ml @@ -5,6 +5,6 @@ type args = { with_children : bool } let render args _sidebar page = Odoc_latex.Generator.render ~with_children:args.with_children page -let extra_documents _args _unit = [] +let filepath _args url = Odoc_latex.Generator.filepath url -let renderer = { Renderer.name = "latex"; render; extra_documents } +let renderer = { Renderer.name = "latex"; render; filepath } diff --git a/src/odoc/man_page.ml b/src/odoc/man_page.ml index 7ee8260250..95e4a26814 100644 --- a/src/odoc/man_page.ml +++ b/src/odoc/man_page.ml @@ -2,6 +2,6 @@ open Odoc_document let render _ _sidebar page = Odoc_manpage.Generator.render page -let extra_documents _args _unit = [] +let filepath _ url = Odoc_manpage.Generator.filepath url -let renderer = { Renderer.name = "man"; render; extra_documents } +let renderer = { Renderer.name = "man"; render; filepath } diff --git a/src/odoc/rendering.ml b/src/odoc/rendering.ml index a1d7a8146e..b373203b4e 100644 --- a/src/odoc/rendering.ml +++ b/src/odoc/rendering.ml @@ -2,46 +2,50 @@ open Odoc_document open Or_error open Odoc_model -let documents_of_unit ~warnings_options ~syntax ~renderer ~extra unit = - Error.catch_warnings (fun () -> - renderer.Renderer.extra_documents extra (CU unit)) - |> Error.handle_warnings ~warnings_options - >>= fun extra_docs -> - Ok (Renderer.document_of_compilation_unit ~syntax unit :: extra_docs) - -let documents_of_page ~warnings_options ~syntax ~renderer ~extra page = - Error.catch_warnings (fun () -> - renderer.Renderer.extra_documents extra (Page page)) - |> Error.handle_warnings ~warnings_options - >>= fun extra_docs -> Ok (Renderer.document_of_page ~syntax page :: extra_docs) +let prepare ~extra_suffix ~output_dir filename = + let filename = + match extra_suffix with + | Some s -> Fpath.add_ext s filename + | None -> filename + in + let filename = Fpath.normalize @@ Fs.File.append output_dir filename in + let directory = Fs.File.dirname filename in + Fs.Directory.mkdir_p directory; + filename -let documents_of_odocl ~warnings_options ~renderer ~extra ~syntax input = +let document_of_odocl ~syntax input = Odoc_file.load input >>= fun unit -> match unit.content with | Odoc_file.Page_content odoctree -> - documents_of_page ~warnings_options ~syntax ~renderer ~extra odoctree - | Impl_content _impl -> - (* documents_of_implementation ~warnings_options ~syntax impl source *) + Ok (Renderer.document_of_page ~syntax odoctree) + | Unit_content odoctree -> + Ok (Renderer.document_of_compilation_unit ~syntax odoctree) + | Impl_content _ -> Error (`Msg "Wrong kind of unit: Expected a page or module unit, got an \ implementation. Use the dedicated command for implementation.") - | Unit_content odoctree -> - documents_of_unit ~warnings_options ~syntax ~renderer ~extra odoctree - | Asset_content _ -> Ok [] (* TODO *) + | Asset_content _ -> + Error + (`Msg + "Wrong kind of unit: Expected a page or module unit, got an asset \ + unit. Use the dedicated command for assets.") -let documents_of_input ~renderer ~extra ~resolver ~warnings_options ~syntax - input = +let document_of_input ~resolver ~warnings_options ~syntax input = let output = Fs.File.(set_ext ".odocl" input) in Odoc_link.from_odoc ~resolver ~warnings_options input output >>= function - | `Page page -> Ok [ Renderer.document_of_page ~syntax page ] - | `Impl _impl -> + | `Page page -> Ok (Renderer.document_of_page ~syntax page) + | `Module m -> Ok (Renderer.document_of_compilation_unit ~syntax m) + | `Impl _ -> Error (`Msg "Wrong kind of unit: Expected a page or module unit, got an \ implementation. Use the dedicated command for implementation.") - | `Module m -> documents_of_unit ~warnings_options ~syntax ~renderer ~extra m - | `Asset _ -> Ok [] (* TODO *) + | `Asset _ -> + Error + (`Msg + "Wrong kind of unit: Expected a page or module unit, got an asset \ + unit. Use the dedicated command for assets.") let render_document renderer ~sidebar ~output:root_dir ~extra_suffix ~extra doc = @@ -49,7 +53,6 @@ let render_document renderer ~sidebar ~output:root_dir ~extra_suffix ~extra doc match doc with | Odoc_document.Types.Document.Page { url; _ } -> url | Source_page { url; _ } -> url - | Asset { url; _ } -> url in let sidebar = Odoc_utils.Option.map @@ -58,14 +61,7 @@ let render_document renderer ~sidebar ~output:root_dir ~extra_suffix ~extra doc in let pages = renderer.Renderer.render extra sidebar doc in Renderer.traverse pages ~f:(fun filename content -> - let filename = - match extra_suffix with - | Some s -> Fpath.add_ext s filename - | None -> filename - in - let filename = Fpath.normalize @@ Fs.File.append root_dir filename in - let directory = Fs.File.dirname filename in - Fs.Directory.mkdir_p directory; + let filename = prepare ~extra_suffix ~output_dir:root_dir filename in let oc = open_out (Fs.File.to_string filename) in let fmt = Format.formatter_of_out_channel oc in Format.fprintf fmt "%t@?" content; @@ -74,14 +70,11 @@ let render_document renderer ~sidebar ~output:root_dir ~extra_suffix ~extra doc let render_odoc ~resolver ~warnings_options ~syntax ~renderer ~output extra file = let extra_suffix = None in - documents_of_input ~renderer ~extra ~resolver ~warnings_options ~syntax file - >>= fun docs -> - List.iter - (render_document renderer ~sidebar:None ~output ~extra_suffix ~extra) - docs; + document_of_input ~resolver ~warnings_options ~syntax file >>= fun doc -> + render_document renderer ~sidebar:None ~output ~extra_suffix ~extra doc; Ok () -let generate_odoc ~syntax ~warnings_options ~renderer ~output ~extra_suffix +let generate_odoc ~syntax ~warnings_options:_ ~renderer ~output ~extra_suffix ~sidebar extra file = (match sidebar with | None -> Ok None @@ -89,11 +82,8 @@ let generate_odoc ~syntax ~warnings_options ~renderer ~output ~extra_suffix Odoc_file.load_index x >>= fun (sidebar, _) -> Ok (Some (Odoc_document.Sidebar.of_lang sidebar))) >>= fun sidebar -> - documents_of_odocl ~warnings_options ~renderer ~extra ~syntax file - >>= fun docs -> - List.iter - (render_document renderer ~output ~sidebar ~extra_suffix ~extra) - docs; + document_of_odocl ~syntax file >>= fun doc -> + render_document renderer ~output ~sidebar ~extra_suffix ~extra doc; Ok () let documents_of_implementation ~warnings_options:_ ~syntax impl source_file = @@ -128,22 +118,30 @@ let generate_source_odoc ~syntax ~warnings_options ~renderer ~output | Page_content _ | Unit_content _ | Asset_content _ -> Error (`Msg "Expected an implementation unit") +let generate_asset_odoc ~warnings_options:_ ~renderer ~output ~asset_file + ~extra_suffix extra file = + Odoc_file.load file >>= fun unit -> + match unit.content with + | Odoc_file.Asset_content unit -> + let url = Odoc_document.Url.Path.from_identifier unit.name in + let filename = renderer.Renderer.filepath extra url in + let dst = prepare ~extra_suffix ~output_dir:output filename in + Fs.File.copy ~src:asset_file ~dst + | Page_content _ | Unit_content _ | Impl_content _ -> + Error (`Msg "Expected an asset unit") + let targets_odoc ~resolver ~warnings_options ~syntax ~renderer ~output:root_dir ~extra odoctree = - let docs = + let doc = if Fpath.get_ext odoctree = ".odoc" then - documents_of_input ~renderer ~extra ~resolver ~warnings_options ~syntax - odoctree - else documents_of_odocl ~warnings_options ~renderer ~extra ~syntax odoctree + document_of_input ~resolver ~warnings_options ~syntax odoctree + else document_of_odocl ~syntax odoctree in - docs >>= fun docs -> - List.iter - (fun doc -> - let pages = renderer.Renderer.render extra None doc in - Renderer.traverse pages ~f:(fun filename _content -> - let filename = Fpath.normalize @@ Fs.File.append root_dir filename in - Format.printf "%a\n" Fpath.pp filename)) - docs; + doc >>= fun doc -> + let pages = renderer.Renderer.render extra None doc in + Renderer.traverse pages ~f:(fun filename _content -> + let filename = Fpath.normalize @@ Fs.File.append root_dir filename in + Format.printf "%a\n" Fpath.pp filename); Ok () let targets_source_odoc ~syntax ~warnings_options ~renderer ~output:root_dir diff --git a/src/odoc/rendering.mli b/src/odoc/rendering.mli index c631751a6b..1d274d3c76 100644 --- a/src/odoc/rendering.mli +++ b/src/odoc/rendering.mli @@ -33,6 +33,16 @@ val generate_source_odoc : Fpath.t -> (unit, [> msg ]) result +val generate_asset_odoc : + warnings_options:Odoc_model.Error.warnings_options -> + renderer:'a Renderer.t -> + output:Fs.directory -> + asset_file:Fs.file -> + extra_suffix:string option -> + 'a -> + Fs.file -> + (unit, [> Or_error.msg ]) result + val targets_odoc : resolver:Resolver.t -> warnings_options:Odoc_model.Error.warnings_options -> diff --git a/src/utils/odoc_utils.ml b/src/utils/odoc_utils.ml index 58daa04eb4..d3654cab29 100644 --- a/src/utils/odoc_utils.ml +++ b/src/utils/odoc_utils.ml @@ -80,3 +80,19 @@ end module Option = struct let map f = function None -> None | Some x -> Some (f x) end + +module Fun = struct + exception Finally_raised of exn + + let protect ~(finally : unit -> unit) work = + let finally_no_exn () = + try finally () with e -> raise (Finally_raised e) + in + match work () with + | result -> + finally_no_exn (); + result + | exception work_exn -> + finally_no_exn (); + raise work_exn +end diff --git a/src/xref2/link.ml b/src/xref2/link.ml index f0291a0c35..479a7b26e3 100644 --- a/src/xref2/link.ml +++ b/src/xref2/link.ml @@ -1109,7 +1109,6 @@ let page env page = List.iter (fun child -> match child with - | Page.Asset_child _ | Page.Source_tree_child _ -> () | Page.Page_child page -> ( match Env.lookup_page_by_name page env with | Ok _ -> () diff --git a/test/pages/assets.t/index.mld b/test/pages/assets.t/index.mld deleted file mode 100644 index 4e01eac22f..0000000000 --- a/test/pages/assets.t/index.mld +++ /dev/null @@ -1,4 +0,0 @@ -{0 Package page} - -Some image: -{%html: %} diff --git a/test/pages/assets.t/run.t b/test/pages/assets.t/run.t deleted file mode 100644 index 04816657db..0000000000 --- a/test/pages/assets.t/run.t +++ /dev/null @@ -1,93 +0,0 @@ -Blablabla - - $ cat index.mld - {0 Package page} - - Some image: - {%html: %} - -And we'll have a module that we'll put underneath this package page. - - $ cat test.mli - (** Humpf, let's try accessing the asset: - {%html: %} - *) - - (** Nevermind *) - type t - - -Compile the module first - - $ ocamlc -c -bin-annot test.mli - -Then we need to odoc-compile the package mld file, listing its children - - $ odoc compile index.mld --child module-test --child asset-img.jpg - Warning: Potential name clash - child page named 'index' - -This will have produced a file called 'page-index.odoc'. -Now we can odoc-compile the module odoc file passing that file as parent. - - $ odoc compile test.cmti -I . --parent index - -Link and generate the HTML (forgetting the asset!): - - $ for i in *.odoc; do odoc link -I . $i; done - $ for i in *.odocl; do odoc html-generate $i -o html; done - File "img.jpg": - Warning: asset is missing. - -Note that the html was generated despite the missing asset (there might be dead refs!) - - $ find html -type f | sort - html/index/Test/index.html - html/index/index.html - -Which matches the output of the targets command (which emits no warning): - - $ odoc html-targets page-index.odocl -o html - html/index/index.html - -Trying to pass an asset which doesn't exist: -(also: some sed magic due to cmdliner output changing based on the version) - - $ odoc html-generate page-index.odocl --asset img.jpg -o html 2>&1 | \ - > sed 's/…/.../' | sed "s/\`/'/g" - odoc: option '--asset': no 'img.jpg' file or directory - Usage: odoc html-generate [OPTION]... FILE.odocl - Try 'odoc html-generate --help' or 'odoc --help' for more information. - -Creating then passing the asset alongside an incorrect one: - - $ touch img.jpg - $ odoc html-generate page-index.odocl --asset img.jpg --asset test.mli -o html - File "test.mli": - Warning: this asset was not declared as a child of index - -This time, the asset should have been copied at the right place: - - $ find html -type f | sort - html/index/Test/index.html - html/index/img.jpg - html/index/index.html - -Which once again matches the output of the targets command (still no warning!): - - $ odoc html-targets page-index.odocl --asset img.jpg --asset test.mli -o html - html/index/index.html - html/index/img.jpg - -Let's make sure the manpage and latex renderers "work" too - - $ for i in *.odocl; do odoc man-generate $i -o man; odoc latex-generate $i -o latex; done - - $ find man -type f | sort - man/index.3o - man/index/Test.3o - - $ find latex -type f | sort - latex/index.tex - latex/index/Test.tex - -Notice that the assets are *not* there. This should probably be fixed for the latex backend. diff --git a/test/pages/assets.t/test.mli b/test/pages/assets.t/test.mli deleted file mode 100644 index d329f104f8..0000000000 --- a/test/pages/assets.t/test.mli +++ /dev/null @@ -1,7 +0,0 @@ -(** Humpf, let's try accessing the asset: - {%html: %} - *) - -(** Nevermind *) -type t - diff --git a/test/pages/new_assets.t/run.t b/test/pages/new_assets.t/run.t index 6341bb6e03..01bde99fe9 100644 --- a/test/pages/new_assets.t/run.t +++ b/test/pages/new_assets.t/run.t @@ -10,3 +10,13 @@ }, "root": "" } + + $ echo "Hello!" > img.png + + $ odoc html-generate-asset --output-dir _html --asset-unit odoc/root/test/asset-img.png.odoc img.png + + $ find _html -name img.png + _html/root/test/img.png + + $ cat $(find _html -name img.png) + Hello! diff --git a/test/sources/include_in_expansion.t/run.t b/test/sources/include_in_expansion.t/run.t index f3732716e0..24b87d70d1 100644 --- a/test/sources/include_in_expansion.t/run.t +++ b/test/sources/include_in_expansion.t/run.t @@ -1,6 +1,6 @@ Checking that source parents are kept, using include. - $ odoc compile -c module-a -c srctree-source root.mld + $ odoc compile -c module-a root.mld $ ocamlc -c -o b.cmo b.ml -bin-annot -I . $ ocamlc -c -o main__A.cmo a.ml -bin-annot -I . @@ -20,7 +20,7 @@ Checking that source parents are kept, using include. $ odoc html-generate-source --impl impl-main.odocl --indent -o html main.ml $ odoc html-generate --indent -o html main.odocl - $ odoc html-generate-source --impl impl-main__A.odocl --hidden --indent -o html a.ml + $ odoc html-generate-source --impl impl-main__A.odocl --indent -o html a.ml $ odoc html-generate --hidden --indent -o html main__A.odocl In Main.A, the source parent of value x should be to Main__A, while the diff --git a/test/sources/recursive_module.t/run.t b/test/sources/recursive_module.t/run.t index a021e57f69..f8e0b51890 100644 --- a/test/sources/recursive_module.t/run.t +++ b/test/sources/recursive_module.t/run.t @@ -6,11 +6,7 @@ Checking that source links exists inside recursive modules. $ odoc link -I . impl-main.odoc $ odoc link -I . main.odoc $ odoc html-generate --indent -o html main.odocl - $ odoc html-generate-impl --source main.ml --indent -o html impl-main.odocl - odoc: unknown command 'html-generate-impl', must be one of 'aggregate-occurrences', 'classify', 'compile', 'compile-asset', 'compile-deps', 'compile-impl', 'compile-index', 'compile-targets', 'count-occurrences', 'css', 'errors', 'html', 'html-deps', 'html-fragment', 'html-generate', 'html-generate-source', 'html-targets', 'html-targets-source', 'html-url', 'latex', 'latex-generate', 'latex-targets', 'latex-url', 'link', 'link-deps', 'man', 'man-generate', 'man-targets', 'support-files' or 'support-files-targets'. - Usage: odoc [COMMAND] … - Try 'odoc --help' for more information. - [2] + $ odoc html-generate-source --impl impl-main.odocl --indent -o html main.ml Both modules should contain source links