New odoc rules

This commit is contained in:
Rudi Grinberg 2018-03-16 13:24:45 +08:00
parent 86768475b2
commit 7a26c18e20
11 changed files with 538 additions and 351 deletions

View File

@ -11,6 +11,8 @@ open! No_io
module Gen(P : Install_rules.Params) = struct module Gen(P : Install_rules.Params) = struct
module Alias = Build_system.Alias module Alias = Build_system.Alias
module SC = Super_context module SC = Super_context
module Odoc = Odoc.Gen(P)
open P open P
let ctx = SC.context sctx let ctx = SC.context sctx
@ -760,13 +762,7 @@ module Gen(P : Install_rules.Params) = struct
SC.add_rule sctx build SC.add_rule sctx build
); );
(* Odoc *) Odoc.setup_library_odoc_rules lib ~dir ~requires ~modules ~dep_graphs ~scope
let mld_files =
String_set.fold files ~init:[] ~f:(fun fn acc ->
if Filename.check_suffix fn ".mld" then fn :: acc else acc)
in
Odoc.setup_library_rules sctx lib ~dir ~requires ~modules ~dep_graphs
~mld_files ~scope
; ;
let flags = let flags =
@ -983,7 +979,7 @@ module Gen(P : Install_rules.Params) = struct
(match components with (match components with
| ".js" :: rest -> Js_of_ocaml_rules.setup_separate_compilation_rules | ".js" :: rest -> Js_of_ocaml_rules.setup_separate_compilation_rules
sctx rest sctx rest
| "_doc" :: rest -> Odoc.gen_rules sctx rest ~dir | "_doc" :: rest -> Odoc.gen_rules rest ~dir
| ".ppx" :: rest -> Preprocessing.gen_rules sctx rest | ".ppx" :: rest -> Preprocessing.gen_rules sctx rest
| _ -> | _ ->
match Path.Map.find stanzas_per_dir dir with match Path.Map.find stanzas_per_dir dir with
@ -1006,7 +1002,13 @@ module Gen(P : Install_rules.Params) = struct
let module_names_of_lib = module_names_of_lib let module_names_of_lib = module_names_of_lib
let mlds_of_dir = mlds_of_dir let mlds_of_dir = mlds_of_dir
end) in end) in
Install_rules.init () Install_rules.init ();
Odoc.init ~modules_by_lib:(fun ~dir lib ->
let m = modules_by_lib ~dir lib in
match m.alias_module with
| Some m -> [m]
| None -> Module.Name.Map.values m.modules
) ~mlds_of_dir
end end
module type Gen = sig module type Gen = sig

10
src/odoc.boot.ml Normal file
View File

@ -0,0 +1,10 @@
module Gen (S : sig val sctx : Super_context.t end) = struct
let setup_library_odoc_rules _ ~dir:_ ~scope:_ ~modules:_ ~requires:_
~dep_graphs:_ = ()
let init ~modules_by_lib:_ ~mlds_of_dir:_ = ()
let gen_rules ~dir:_ _ = ()
end

View File

@ -4,113 +4,100 @@ open Build.O
module SC = Super_context module SC = Super_context
module Doc = struct let (++) = Path.relative
let root sctx = Path.relative (SC.context sctx).Context.build_dir "_doc"
type origin = let lib_unique_name lib =
| Public of string let name = Lib.name lib in
| Private of string * Scope_info.Name.t match Lib.status lib with
| Installed -> assert false
| Public _ -> name
| Private scope_name -> SC.Scope_key.to_string name scope_name
let dir_internal t origin = let pkg_or_lnu lib =
let name = match Lib.package lib with
match origin with | Some p -> Package.Name.to_string p
| Public n -> n | None -> lib_unique_name lib
| Private (n, s) -> sprintf "%s@%s" n (Scope_info.Name.to_string s)
in
Path.relative (root t) name
let dir t (lib : Library.t) = type target =
dir_internal t | Lib of Lib.t
(match lib.public with | Pkg of Package.Name.t
| Some { name; _ } -> Public name
| None -> Private (lib.name, lib.scope_name))
let alias = Build_system.Alias.make ".doc-all" module Gen (S : sig val sctx : SC.t end) = struct
open S
let deps t = let context = SC.context sctx
Build.dyn_paths (Build.arr (
List.fold_left ~init:[] ~f:(fun acc (lib : Lib.t) ->
if Lib.is_local lib then (
let dir =
dir_internal t
(match Lib.status lib with
| Installed -> assert false
| Public _ -> Public (Lib.name lib)
| Private s -> Private (Lib.name lib, s))
in
Build_system.Alias.stamp_file (alias ~dir) :: acc
) else (
acc
)
)))
let alias t lib = alias ~dir:(dir t lib) module Paths = struct
let root = context.Context.build_dir ++ "_doc"
let static_deps t lib = Build_system.Alias.dep (alias t lib) let odocs m =
root ++ (
match m with
| Lib lib -> sprintf "_odoc/lib/%s" (lib_unique_name lib)
| Pkg pkg -> sprintf "_odoc/pkg/%s" (Package.Name.to_string pkg)
)
let setup_deps t lib files = SC.add_alias_deps t (alias t lib) files let html_root = root ++ "_html"
let dir t lib = dir t lib let html m =
end html_root ++ (
match m with
| Pkg pkg -> Package.Name.to_string pkg
| Lib lib -> pkg_or_lnu lib
)
let gen_mld_dir (pkg : Package.t) =
root ++ "_mlds" ++ (Package.Name.to_string pkg.name)
end
let ( ++ ) = Path.relative module Dep = struct
let html_alias m =
Build_system.Alias.doc ~dir:(Paths.html m)
let get_odoc sctx = SC.resolve_program sctx "odoc" ~hint:"opam install odoc" let alias = Build_system.Alias.make ".odoc-all"
let odoc_ext = ".odoc"
module Mld : sig let deps =
type t Build.dyn_paths (Build.arr (
val create : name:string -> t List.fold_left ~init:[] ~f:(fun acc (lib : Lib.t) ->
if Lib.is_local lib then (
let dir = Paths.odocs (Lib lib) in
Build_system.Alias.stamp_file (alias ~dir) :: acc
) else (
acc
)
)))
val odoc_file : doc_dir:Path.t -> t -> Path.t let alias m = alias ~dir:(Paths.odocs m)
val odoc_input : doc_dir:Path.t -> t -> Path.t
val html_filename : t -> string (* let static_deps t lib = Build_system.Alias.dep (alias t lib) *)
end = struct
type t = string (** source file name without the extension. *)
let create ~name = name let setup_deps m files = SC.add_alias_deps sctx (alias m) files
end
let odoc_file ~doc_dir t = let odoc = SC.resolve_program sctx "odoc" ~hint:"opam install odoc"
Path.relative doc_dir (sprintf "page-%s%s" t odoc_ext) let odoc_ext = ".odoc"
let odoc_input ~doc_dir t = module Mld : sig
Path.relative doc_dir (sprintf "%s-generated.mld" t) type t
let html_filename t = val create : Path.t -> t
sprintf "%s.html" t
end
module Module_or_mld = struct val odoc_file : doc_dir:Path.t -> t -> Path.t
type t = val odoc_input : t -> Path.t
| Mld of Mld.t
| Module of Module.t
let odoc_file ~doc_dir = function end = struct
| Mld m -> Mld.odoc_file ~doc_dir m type t = Path.t
| Module m -> Module.odoc_file ~doc_dir m
let odoc_input ~obj_dir ~doc_dir = function let create p = p
| Mld m -> Mld.odoc_input ~doc_dir m
| Module m -> Module.cmti_file m ~obj_dir
let html_dir ~doc_dir = function let odoc_file ~doc_dir t =
| Mld _ -> doc_dir let t = Filename.chop_extension (Path.basename t) in
| Module m -> doc_dir ++ String.capitalize m.obj_name Path.relative doc_dir (sprintf "page-%s%s" t odoc_ext)
let html_file ~doc_dir t = let odoc_input t = t
match t with end
| Mld m -> html_dir ~doc_dir t ++ Mld.html_filename m
| Module _ -> html_dir ~doc_dir t ++ "index.html"
end
let module_or_mld_deps (m : Module_or_mld.t) ~doc_dir let module_deps (m : Module.t) ~doc_dir ~(dep_graphs:Ocamldep.Dep_graphs.t) =
~(dep_graphs:Ocamldep.Dep_graphs.t) =
match m with
| Mld _ ->
Build.arr (fun x -> x)
| Module m ->
Build.dyn_paths Build.dyn_paths
((match m.intf with ((match m.intf with
| Some _ -> | Some _ ->
@ -120,194 +107,140 @@ let module_or_mld_deps (m : Module_or_mld.t) ~doc_dir
Ocamldep.Dep_graph.deps_of dep_graphs.impl m) Ocamldep.Dep_graph.deps_of dep_graphs.impl m)
>>^ List.map ~f:(Module.odoc_file ~doc_dir)) >>^ List.map ~f:(Module.odoc_file ~doc_dir))
let compile sctx (m : Module_or_mld.t) ~odoc ~dir ~obj_dir ~includes ~dep_graphs let compile_module (m : Module.t) ~dir ~obj_dir ~includes ~dep_graphs
~doc_dir ~lib_unique_name = ~doc_dir ~pkg_or_lnu =
let context = SC.context sctx in let odoc_file = Module.odoc_file m ~doc_dir in
let odoc_file = Module_or_mld.odoc_file m ~doc_dir in
SC.add_rule sctx
(module_or_mld_deps m ~doc_dir ~dep_graphs
>>>
includes
>>>
Build.run ~context ~dir:doc_dir odoc
[ A "compile"
; A "-I"; Path dir
; Dyn (fun x -> x)
; As ["--pkg"; lib_unique_name]
; A "-o"; Target odoc_file
; Dep (Module_or_mld.odoc_input m ~obj_dir ~doc_dir)
]);
(m, odoc_file)
let to_html sctx (m : Module_or_mld.t) odoc_file ~doc_dir ~odoc ~dir ~includes
~(lib : Library.t) =
let context = SC.context sctx in
let html_dir = Module_or_mld.html_dir ~doc_dir m in
let html_file = Module_or_mld.html_file ~doc_dir m in
let to_remove, jbuilder_keep =
match m with
| Mld _ -> html_file, []
| Module _ ->
let jbuilder_keep =
Build.create_file (html_dir ++ Config.jbuilder_keep_fname) in
html_dir, [jbuilder_keep]
in
SC.add_rule sctx
(Doc.static_deps sctx lib
>>>
includes
>>>
Build.progn (
Build.remove_tree to_remove
:: Build.mkdir html_dir
:: Build.run ~context ~dir odoc ~extra_targets:[html_file]
[ A "html"
; A "-I"; Path doc_dir
; Dyn (fun x -> x)
; A "-o"; Path (Path.parent doc_dir)
; Dep odoc_file
]
:: jbuilder_keep
)
);
html_file
let all_mld_files sctx ~(lib : Library.t) ~modules ~dir files =
let all_files =
if List.mem "index.mld" ~set:files then files else "index.mld" :: files
in
let lib_name = Library.best_name lib in
let doc_dir = Doc.dir sctx lib in
List.map all_files ~f:(fun file ->
let name = Filename.chop_extension file in
let mld = Mld.create ~name in
let generated_mld = Mld.odoc_input ~doc_dir mld in
let source_mld = dir ++ file in
SC.add_rule sctx SC.add_rule sctx
(Build.if_file_exists source_mld (module_deps m ~doc_dir ~dep_graphs
~then_:(Build.contents source_mld)
~else_:(Build.arr (fun () ->
(if lib.wrapped then
sprintf
"{1 Library %s}\n\
The entry point for this library is module {!module:%s}."
lib_name
(String.capitalize lib.name)
else
sprintf
"{1 Library %s}\n\
This library exposes the following toplevel modules: {!modules:%s}."
lib_name
((Module.Name.Map.keys modules :> string list)
|> String.concat ~sep:" "))))
>>> >>>
Build.write_file_dyn generated_mld); includes
mld >>>
) Build.run ~context ~dir:doc_dir odoc
[ A "compile"
; A "-I"; Path dir
; Dyn (fun x -> x)
; As ["--pkg"; pkg_or_lnu]
; A "-o"; Target odoc_file
; Dep (Module.cmti_file m ~obj_dir)
]);
(m, odoc_file)
let css_file ~doc_dir = doc_dir ++ "odoc.css" let compile_mld (m : Mld.t) ~includes ~doc_dir ~pkg =
let odoc_file = Mld.odoc_file m ~doc_dir in
SC.add_rule sctx
(includes
>>>
Build.run ~context ~dir:doc_dir odoc
[ A "compile"
; Dyn (fun x -> x)
; As ["--pkg"; Package.Name.to_string pkg]
; A "-o"; Target odoc_file
; Dep (Mld.odoc_input m)
]);
odoc_file
let toplevel_index ~doc_dir = doc_dir ++ "index.html" type odoc =
{ odoc_input: Path.t
; html_dir: Path.t
; html_file: Path.t
; html_alias: Build_system.Alias.t
; typ: [`Module | `Mld]
}
let setup_library_rules sctx (lib : Library.t) ~dir ~scope ~modules ~mld_files let odoc_include_flags libs =
~requires ~(dep_graphs:Ocamldep.Dep_graph.t Ml_kind.Dict.t) = let paths =
let doc_dir = Doc.dir sctx lib in libs |> List.fold_left ~f:(fun paths lib ->
let obj_dir, lib_unique_name = if Lib.is_local lib then (
Path.Set.add paths (Paths.odocs (Lib lib))
) else (
paths
)
) ~init:Path.Set.empty in
Arg_spec.S (List.concat_map (Path.Set.to_list paths)
~f:(fun dir -> [Arg_spec.A "-I"; Path dir]))
let to_html (odoc_file : odoc) ~deps =
let to_remove, jbuilder_keep =
match odoc_file.typ with
| `Mld -> odoc_file.html_file, []
| `Module ->
let jbuilder_keep =
Build.create_file (odoc_file.html_dir ++ Config.jbuilder_keep_fname) in
odoc_file.html_dir, [jbuilder_keep]
in
SC.add_rule sctx
(deps
>>> Build.progn (
Build.remove_tree to_remove
:: Build.mkdir odoc_file.html_dir
:: Build.run ~context ~dir:Paths.html_root
odoc ~extra_targets:[odoc_file.html_file]
[ A "html"
; Dyn odoc_include_flags
; A "-o"; Path Paths.html_root
; Dep odoc_file.odoc_input
]
:: jbuilder_keep
)
);
odoc_file.html_file
let css_file = Paths.html_root ++ "odoc.css"
let toplevel_index = Paths.html_root ++ "index.html"
let setup_library_odoc_rules (library : Library.t) ~dir ~scope ~modules
~requires ~(dep_graphs:Ocamldep.Dep_graph.t Ml_kind.Dict.t) =
let lib = let lib =
Option.value_exn (Lib.DB.find_even_when_hidden (Scope.libs scope) lib.name) Option.value_exn (Lib.DB.find_even_when_hidden (Scope.libs scope)
library.name) in
(* Using the proper package name doesn't actually work since odoc assumes
that a package contains only 1 library *)
let pkg_or_lnu = pkg_or_lnu lib in
let doc_dir = Paths.odocs (Lib lib) in
let obj_dir = Lib.obj_dir lib in
let includes =
Build.memoize "includes"
(requires
>>> Dep.deps
>>^ Lib.L.include_flags ~stdlib_dir:context.stdlib_dir)
in in
let name = let modules_and_odoc_files =
let name = Lib.name lib in List.map (Module.Name.Map.values modules) ~f:(
match Lib.status lib with compile_module ~dir ~obj_dir ~includes ~dep_graphs
| Installed -> assert false ~doc_dir ~pkg_or_lnu)
| Public _ -> name
| Private scope_name ->
sprintf "%s@%s" name (Scope_info.Name.to_string scope_name)
in in
(Lib.obj_dir lib, name) Dep.setup_deps (Lib lib) (List.map modules_and_odoc_files ~f:snd)
in
let odoc = get_odoc sctx in
let includes =
let ctx = SC.context sctx in
Build.memoize "includes"
(requires
>>> Doc.deps sctx
>>^ Lib.L.include_flags ~stdlib_dir:ctx.stdlib_dir)
in
let mld_files =
all_mld_files sctx ~dir ~lib ~modules mld_files
in
let mld_and_odoc_files =
List.map mld_files ~f:(fun m ->
compile sctx ~odoc ~dir ~obj_dir ~includes ~dep_graphs
~doc_dir ~lib_unique_name (Mld m))
in
let modules_and_odoc_files =
List.map (Module.Name.Map.values modules) ~f:(fun m ->
compile sctx ~odoc ~dir ~obj_dir ~includes ~dep_graphs
~doc_dir ~lib_unique_name (Module m))
in
let inputs_and_odoc_files = modules_and_odoc_files @ mld_and_odoc_files in
Doc.setup_deps sctx lib (List.map inputs_and_odoc_files ~f:snd);
(*
let modules_and_odoc_files =
if lib.wrapped then
let main_module_name = String.capitalize lib.name in
List.filter modules_and_odoc_files
~f:(fun (m, _) -> m.Module.name = main_module_name)
else
modules_and_odoc_files
in*)
let html_files =
List.map inputs_and_odoc_files ~f:(fun (m, odoc_file) ->
to_html sctx m odoc_file ~doc_dir ~odoc ~dir ~includes ~lib)
in
let doc_root = Doc.root sctx in
let alias =
match lib.public with
| None -> Build_system.Alias.private_doc ~dir
| Some _ -> Build_system.Alias.doc ~dir in
SC.add_alias_deps sctx alias
(css_file ~doc_dir:doc_root
:: toplevel_index ~doc_dir:doc_root
:: html_files)
let setup_css_rule sctx = let setup_css_rule () =
let context = SC.context sctx in SC.add_rule sctx
let doc_dir = Doc.root sctx in (Build.run ~context
SC.add_rule sctx ~dir:context.build_dir
(Build.run ~context ~extra_targets:[css_file]
~dir:context.build_dir odoc
~extra_targets:[css_file ~doc_dir] [ A "css"; A "-o"; Path Paths.html_root ])
(get_odoc sctx)
[ A "css"; A "-o"; Path doc_dir ])
let sp = Printf.sprintf let sp = Printf.sprintf
let setup_toplevel_index_rule sctx = let setup_toplevel_index_rule () =
let list_items = let list_items =
Super_context.stanzas_to_consider_for_install sctx Super_context.packages sctx
|> List.filter_map ~f:(fun (_path, _scope, stanza) -> |> Package.Name.Map.to_list
match stanza with |> List.filter_map ~f:(fun (name, pkg) ->
| Stanza.Library let name = Package.Name.to_string name in
{Library.kind = Library.Kind.Normal; public = Some public_info; _} ->
let name = public_info.name in
let link = sp {|<a href="%s/index.html">%s</a>|} name name in let link = sp {|<a href="%s/index.html">%s</a>|} name name in
let version_suffix = let version_suffix =
match public_info.package.Package.version_from_opam_file with match pkg.Package.version_from_opam_file with
| None -> | None ->
"" ""
| Some v -> | Some v ->
sp {| <span class="version">%s</span>|} v sp {| <span class="version">%s</span>|} v
in in
Some (sp "<li>%s%s</li>" link version_suffix) Some (sp "<li>%s%s</li>" link version_suffix))
in
| _ -> let list_items = String.concat ~sep:"\n " list_items in
None) let html = sp
in {|<!DOCTYPE html>
let list_items = String.concat ~sep:"\n " list_items in
let html =
sp {|<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>index</title> <title>index</title>
@ -321,28 +254,281 @@ let setup_toplevel_index_rule sctx =
<ol> <ol>
%s %s
</ol> </ol>
</body> </div>
</html> </body>
|} list_items </html>|} list_items
in
let doc_dir = Doc.root sctx in
SC.add_rule sctx @@ Build.write_file (toplevel_index ~doc_dir) html
let gen_rules sctx ~dir:_ rest =
match rest with
| [] ->
setup_css_rule sctx;
setup_toplevel_index_rule sctx
| lib :: _ ->
let lib, lib_db =
match String.rsplit2 lib ~on:'@' with
| None ->
(lib, SC.public_libs sctx)
| Some (lib, name) ->
(lib,
Scope.libs
(SC.find_scope_by_name sctx (Scope_info.Name.of_string name)))
in in
match Lib.DB.find lib_db lib with SC.add_rule sctx @@ Build.write_file toplevel_index html
| Error _ -> ()
| Ok lib -> SC.load_dir sctx ~dir:(Lib.src_dir lib)
let html_alias pkg =
Build_system.Alias.doc ~dir:(
Path.append context.build_dir pkg.Package.path
)
let libs_of_pkg ~pkg =
match Package.Name.Map.find (SC.libs_by_package sctx) pkg with
| None -> Lib.Set.empty
| Some (_, libs) -> libs
let load_all_odoc_rules_pkg ~pkg =
let pkg_libs = libs_of_pkg ~pkg in
SC.load_dir sctx ~dir:(Paths.odocs (Pkg pkg));
Lib.Set.iter pkg_libs ~f:(fun lib ->
SC.load_dir sctx ~dir:(Paths.odocs (Lib lib)));
pkg_libs
let create_odoc ~target odoc_input =
let html_alias = Dep.html_alias target in
let html_base = Paths.html target in
match target with
| Lib _ ->
let html_dir =
html_base ++ (
Path.basename odoc_input
|> Filename.chop_extension
|> Stdune.String.capitalize
) in
{ odoc_input
; html_dir
; html_file = html_dir ++ "index.html"
; typ = `Module
; html_alias
}
| Pkg _ ->
{ odoc_input
; html_dir = html_base
; html_file = html_base ++ sprintf "%s.html" (
Path.basename odoc_input
|> Filename.chop_extension
|> String.drop_prefix ~prefix:"page-"
|> Option.value_exn
)
; typ = `Mld
; html_alias
}
let setup_pkg_html_rules =
let loaded = Package.Name.Table.create ~default_value:None in
let odoc_glob =
Re.compile (Re.seq [Re.(rep1 any) ; Re.str ".odoc" ; Re.eos]) in
fun ~pkg ~libs ->
if Package.Name.Table.get loaded pkg = None then begin
Package.Name.Table.set loaded ~key:pkg ~data:(Some ());
let odocs =
let odocs target =
let dir = Paths.odocs target in
SC.eval_glob sctx ~dir odoc_glob
|> List.map ~f:(fun d -> create_odoc (Path.relative dir d) ~target)
in
List.concat (
odocs (Pkg pkg)
:: (List.map libs ~f:(fun lib -> odocs (Lib lib)))
) in
let html_files =
let closure =
match Lib.closure libs with
| Ok closure -> closure
| Error _ -> libs in
let deps = Build.return closure >>> Dep.deps in
List.map odocs ~f:(to_html ~deps) in
List.iter (
Dep.html_alias (Pkg pkg)
:: List.map ~f:(fun lib -> Dep.html_alias (Lib lib)) libs
) ~f:(fun alias ->
SC.add_alias_deps sctx alias
[ css_file
; toplevel_index
]
);
List.combine odocs html_files
|> List.iter ~f:(fun (odoc, html) ->
SC.add_alias_deps sctx odoc.html_alias [html]
);
end
let gen_rules ~dir:_ rest =
match rest with
| ["_html"] ->
setup_css_rule ();
setup_toplevel_index_rule ()
| "_mlds" :: _pkg :: _
| "_odoc" :: "pkg" :: _pkg :: _ ->
() (* rules were already setup lazily in gen_rules *)
| "_odoc" :: "lib" :: lib :: _ ->
let lib, lib_db = SC.Scope_key.of_string sctx lib in
begin match Lib.DB.find lib_db lib with
| Error _ -> ()
| Ok lib -> SC.load_dir sctx ~dir:(Lib.src_dir lib)
end
| "_html" :: lib_unique_name_or_pkg :: _ ->
let setup_html_rules pkg =
setup_pkg_html_rules ~pkg ~libs:(
Lib.Set.to_list (load_all_odoc_rules_pkg ~pkg)
) in
(* TODO we can be a better with the error handling in the case where
lib_unique_name_or_pkg is neither a valid pkg or lnu *)
let lib, lib_db = SC.Scope_key.of_string sctx lib_unique_name_or_pkg in
begin match Lib.DB.find lib_db lib with
| Error _ -> ()
| Ok lib -> Option.iter (Lib.package lib) ~f:setup_html_rules
end;
Option.iter
(Package.Name.Map.find (SC.packages sctx)
(Package.Name.of_string lib_unique_name_or_pkg))
~f:(fun pkg -> setup_html_rules pkg.name)
| _ -> ()
let setup_package_aliases (pkg : Package.t) =
let alias = html_alias pkg in
SC.add_alias_deps sctx alias (
Dep.html_alias (Pkg pkg.name)
:: (libs_of_pkg ~pkg:pkg.name
|> Lib.Set.to_list
|> List.map ~f:(fun lib -> Dep.html_alias (Lib lib)))
|> List.map ~f:Build_system.Alias.stamp_file
)
let pkg_odoc (pkg : Package.t) = Paths.odocs (Pkg pkg.name)
let entry_modules ~(pkg : Package.t) ~entry_modules_by_lib =
libs_of_pkg ~pkg:pkg.name
|> Lib.Set.to_list
|> List.filter_map ~f:(fun l ->
if Lib.is_local l then (
Some (l, entry_modules_by_lib l)
) else (
None
))
|> Lib.Map.of_list_exn
let default_index entry_modules =
let b = Buffer.create 512 in
Lib.Map.iteri entry_modules ~f:(fun lib modules ->
Buffer.add_string b (
sprintf
"{1 Library %s}\n\
This library exposes the following toplevel modules: \
{!modules:%s}.\n"
(Lib.name lib)
(modules
|> List.map ~f:(fun m -> Module.Name.to_string (Module.name m))
|> String.concat ~sep:" ")
)
);
Buffer.contents b
let check_mlds_no_dupes ~pkg ~mlds =
match
List.map mlds ~f:(fun mld -> (Path.basename mld, mld))
|> String_map.of_list
with
| Ok m -> m
| Error (_, p1, p2) ->
die "Package %s has two mld's with the same basename %s, %s"
(Package.Name.to_string pkg.Package.name)
(Path.to_string_maybe_quoted p1)
(Path.to_string_maybe_quoted p2)
let setup_package_odoc_rules ~pkg ~mlds ~entry_modules_by_lib =
let mlds = check_mlds_no_dupes ~pkg ~mlds in
let mlds =
if String_map.mem mlds "index" then
mlds
else
let entry_modules = entry_modules ~pkg ~entry_modules_by_lib in
let gen_mld = Paths.gen_mld_dir pkg ++ "index.mld" in
SC.add_rule sctx (
Build.write_file gen_mld (default_index entry_modules)
);
String_map.add mlds "index" gen_mld in
let odocs = List.map (String_map.values mlds) ~f:(fun mld ->
compile_mld
(Mld.create mld)
~pkg:pkg.name
~doc_dir:(Paths.odocs (Pkg pkg.name))
~includes:(Build.arr (fun _ -> Arg_spec.As []))
) in
Dep.setup_deps (Pkg pkg.name) odocs
let init ~modules_by_lib ~mlds_of_dir =
let docs_by_package =
let map = lazy (
SC.stanzas sctx
|> List.concat_map ~f:(fun (w : SC.Dir_with_jbuild.t) ->
List.filter_map w.stanzas ~f:(function
| Jbuild.Stanza.Documentation (d : Jbuild.Documentation.t) ->
Some (d.package.name, (w.ctx_dir, d))
| _ ->
None
))
|> Package.Name.Map.of_list_multi
) in
fun (p : Package.t) ->
Option.value (Package.Name.Map.find (Lazy.force map) p.name) ~default:[]
in
let modules_by_lib =
let module M = Map.Make(
struct
type t = Path.t * string
let compare (d1, l1) (d2, l2) =
match Path.compare d1 d2 with
| Ordering.Eq -> String.compare l1 l2
| o -> o
end) in
let lib_to_library = lazy (
SC.stanzas sctx
|> List.concat_map ~f:(fun (w : SC.Dir_with_jbuild.t) ->
List.filter_map w.stanzas ~f:(function
| Jbuild.Stanza.Library (l : Library.t) ->
Some ((w.ctx_dir, Library.best_name l), l)
| _ ->
None
))
|> M.of_list_exn
) in
fun lib ->
let dir = Lib.src_dir lib in
let library = Option.value_exn (
M.find (Lazy.force lib_to_library) (dir, Lib.name lib)
) in
modules_by_lib ~dir library
in
SC.packages sctx
|> Package.Name.Map.iter ~f:(fun (pkg : Package.t) ->
SC.on_load_dir sctx
~dir:(pkg_odoc pkg)
~f:(fun () ->
setup_package_odoc_rules
~pkg
~mlds:(
docs_by_package pkg
|> List.concat_map ~f:(fun (dir, doc) -> mlds_of_dir doc ~dir)
)
~entry_modules_by_lib:modules_by_lib
);
(* setup @doc to build the correct html for the package *)
setup_package_aliases pkg;
);
Super_context.add_alias_deps
sctx
(Build_system.Alias.private_doc ~dir:context.build_dir)
(SC.stanzas sctx
|> List.concat_map ~f:(fun (w : SC.Dir_with_jbuild.t) ->
List.filter_map w.stanzas ~f:(function
| Jbuild.Stanza.Library (l : Jbuild.Library.t) ->
begin match l.public with
| Some _ -> None
| None ->
let scope = SC.find_scope_by_dir sctx w.ctx_dir in
Some (Option.value_exn (
Lib.DB.find_even_when_hidden (Scope.libs scope) l.name)
)
end
| (_ : Jbuild.Stanza.t) -> None
))
|> List.map ~f:(fun (lib : Lib.t) ->
Build_system.Alias.stamp_file (Dep.alias (Lib lib)))
)
end

View File

@ -2,15 +2,21 @@
open Jbuild open Jbuild
val setup_library_rules module Gen (S : sig val sctx : Super_context.t end) : sig
: Super_context.t
-> Library.t
-> dir:Path.t
-> scope:Scope.t
-> modules:Module.t Module.Name.Map.t
-> mld_files:string list
-> requires:(unit, Lib.t list) Build.t
-> dep_graphs:Ocamldep.Dep_graphs.t
-> unit
val gen_rules : Super_context.t -> dir:Path.t -> string list -> unit val setup_library_odoc_rules
: Library.t
-> dir:Path.t
-> scope:Scope.t
-> modules:Module.t Module.Name.Map.t
-> requires:(unit, Lib.t list) Build.t
-> dep_graphs:Ocamldep.Dep_graphs.t
-> unit
val init
: modules_by_lib:(dir:Path.t -> Library.t -> Module.t list)
-> mlds_of_dir:(Documentation.t -> dir:Path.t -> Path.t list)
-> unit
val gen_rules : dir:Path.t -> string list -> unit
end

View File

@ -28,3 +28,6 @@
((name runtest) ((name runtest)
(deps (foo.install)) (deps (foo.install))
(action (echo "${read:foo.install}")))) (action (echo "${read:foo.install}"))))
(documentation
((mld_files (doc))))

View File

@ -43,3 +43,6 @@
"_build/install/default/share/foo/bar.ml" "_build/install/default/share/foo/bar.ml"
"_build/install/default/share/foo/baz.ml" {"baz.ml"} "_build/install/default/share/foo/baz.ml" {"baz.ml"}
] ]
doc: [
"_build/install/default/doc/foo/odoc-pages/doc.mld" {"odoc-pages/doc.mld"}
]

View File

@ -1,16 +1,9 @@
This test checks that there is no clash when two private libraries have the same name This test checks that there is no clash when two private libraries have the same name
$ $JBUILDER build -j1 --display short --root . @doc-private $ $JBUILDER build -j1 --display short --root . @doc-private
odoc _doc/odoc.css
odoc _doc/test@a/page-index.odoc
ocamldep a/test.ml.d ocamldep a/test.ml.d
ocamlc a/.test.objs/test.{cmi,cmo,cmt} ocamlc a/.test.objs/test.{cmi,cmo,cmt}
odoc _doc/test@a/test.odoc odoc _doc/_odoc/lib/test@a/test.odoc
odoc _doc/test@a/index.html
odoc _doc/test@b/page-index.odoc
ocamldep b/test.ml.d ocamldep b/test.ml.d
ocamlc b/.test.objs/test.{cmi,cmo,cmt} ocamlc b/.test.objs/test.{cmi,cmo,cmt}
odoc _doc/test@b/test.odoc odoc _doc/_odoc/lib/test@b/test.odoc
odoc _doc/test@b/index.html
odoc _doc/test@a/Test/.jbuilder-keep,_doc/test@a/Test/index.html
odoc _doc/test@b/Test/.jbuilder-keep,_doc/test@b/Test/index.html

View File

@ -1,36 +1,26 @@
Duplicate mld's in the same scope Duplicate mld's in the same scope
$ $JBUILDER build @doc -j1 --display short --root ./same-scope 2>&1 | grep -v Entering $ $JBUILDER build @doc -j1 --display short --root ./same-scope 2>&1 | grep -v Entering
odoc _doc/odoc.css odoc _doc/_html/odoc.css
odoc _doc/root.lib1/page-index.odoc
odoc _doc/root.lib1/page-test.odoc
ocamlc lib1/.root_lib1.objs/root_lib1.{cmi,cmo,cmt} ocamlc lib1/.root_lib1.objs/root_lib1.{cmi,cmo,cmt}
odoc _doc/root.lib1/root_lib1.odoc odoc _doc/_odoc/lib/root.lib1/root_lib1.odoc
odoc _doc/root.lib1/index.html
odoc _doc/root.lib2/page-index.odoc
odoc _doc/root.lib2/page-test.odoc
ocamlc lib2/.root_lib2.objs/root_lib2.{cmi,cmo,cmt} ocamlc lib2/.root_lib2.objs/root_lib2.{cmi,cmo,cmt}
odoc _doc/root.lib2/root_lib2.odoc odoc _doc/_odoc/lib/root.lib2/root_lib2.odoc
odoc _doc/root.lib2/index.html odoc _doc/_html/root/Root_lib1/.jbuilder-keep,_doc/_html/root/Root_lib1/index.html
odoc _doc/root.lib1/test.html odoc _doc/_odoc/pkg/root/page-index.odoc
odoc _doc/root.lib1/Root_lib1/.jbuilder-keep,_doc/root.lib1/Root_lib1/index.html odoc _doc/_html/root/index.html
odoc _doc/root.lib2/test.html odoc _doc/_html/root/Root_lib2/.jbuilder-keep,_doc/_html/root/Root_lib2/index.html
odoc _doc/root.lib2/Root_lib2/.jbuilder-keep,_doc/root.lib2/Root_lib2/index.html
Duplicate mld's in different scope Duplicate mld's in different scope
$ rm -rf diff-scope/_build $ rm -rf diff-scope/_build
$ $JBUILDER build @doc -j1 --display short --root ./diff-scope 2>&1 | grep -v Entering $ $JBUILDER build @doc -j1 --display short --root ./diff-scope 2>&1 | grep -v Entering
odoc _doc/odoc.css odoc _doc/_html/odoc.css
odoc _doc/scope1/page-foo.odoc
odoc _doc/scope1/page-index.odoc
ocamlc scope1/.scope1.objs/scope1.{cmi,cmo,cmt} ocamlc scope1/.scope1.objs/scope1.{cmi,cmo,cmt}
odoc _doc/scope1/scope1.odoc odoc _doc/_odoc/lib/scope1/scope1.odoc
odoc _doc/scope1/foo.html odoc _doc/_html/scope1/Scope1/.jbuilder-keep,_doc/_html/scope1/Scope1/index.html
odoc _doc/scope2/page-foo.odoc odoc _doc/_odoc/pkg/scope1/page-index.odoc
odoc _doc/scope2/page-index.odoc odoc _doc/_html/scope1/index.html
ocamlc scope2/.scope2.objs/scope2.{cmi,cmo,cmt} ocamlc scope2/.scope2.objs/scope2.{cmi,cmo,cmt}
odoc _doc/scope2/scope2.odoc odoc _doc/_odoc/lib/scope2/scope2.odoc
odoc _doc/scope2/foo.html odoc _doc/_html/scope2/Scope2/.jbuilder-keep,_doc/_html/scope2/Scope2/index.html
odoc _doc/scope1/index.html odoc _doc/_odoc/pkg/scope2/page-index.odoc
odoc _doc/scope1/Scope1/.jbuilder-keep,_doc/scope1/Scope1/index.html odoc _doc/_html/scope2/index.html
odoc _doc/scope2/index.html
odoc _doc/scope2/Scope2/.jbuilder-keep,_doc/scope2/Scope2/index.html

View File

@ -20,5 +20,5 @@
(alias (alias
((name runtest) ((name runtest)
(deps (_doc/index.html)) (deps (_doc/_html/index.html))
(action (echo "${read:_doc/index.html}")))) (action (echo "${read:_doc/_html/index.html}"))))

View File

@ -1,21 +1,15 @@
$ $JBUILDER build @doc -j1 --display short --root . $ $JBUILDER build @doc -j1 --display short --root .
ocamldep foo_byte.ml.d
ocamlc .foo_byte.objs/foo_byte.{cmi,cmo,cmt}
odoc _doc/foo.byte/foo_byte.odoc
odoc _doc/foo.byte/page-index.odoc
odoc _doc/foo.byte/page-test.odoc
odoc _doc/foo.byte/Foo_byte/.jbuilder-keep,_doc/foo.byte/Foo_byte/index.html
ocamldep foo.ml.d ocamldep foo.ml.d
ocamlc .foo.objs/foo.{cmi,cmo,cmt} ocamlc .foo.objs/foo.{cmi,cmo,cmt}
odoc _doc/foo/foo.odoc odoc _doc/_odoc/lib/foo/foo.odoc
odoc _doc/foo/page-index.odoc ocamldep foo_byte.ml.d
odoc _doc/foo/page-test.odoc ocamlc .foo_byte.objs/foo_byte.{cmi,cmo,cmt}
odoc _doc/foo/Foo/.jbuilder-keep,_doc/foo/Foo/index.html odoc _doc/_odoc/lib/foo.byte/foo_byte.odoc
odoc _doc/odoc.css odoc _doc/_html/foo/Foo/.jbuilder-keep,_doc/_html/foo/Foo/index.html
odoc _doc/foo.byte/index.html odoc _doc/_odoc/pkg/foo/page-index.odoc
odoc _doc/foo.byte/test.html odoc _doc/_html/foo/index.html
odoc _doc/foo/index.html odoc _doc/_html/odoc.css
odoc _doc/foo/test.html odoc _doc/_html/foo/Foo_byte/.jbuilder-keep,_doc/_html/foo/Foo_byte/index.html
$ $JBUILDER runtest -j1 --display short --root . $ $JBUILDER runtest -j1 --display short --root .
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
@ -30,7 +24,7 @@
<h2>OCaml package documentation</h2> <h2>OCaml package documentation</h2>
<ol> <ol>
<li><a href="foo/index.html">foo</a></li> <li><a href="foo/index.html">foo</a></li>
<li><a href="foo.byte/index.html">foo.byte</a></li>
</ol> </ol>
</body> </div>
</html> </body>
</html>