Merge pull request #410 from rgrinberg/new-odoc-minimal

New odoc rules (minimal)
This commit is contained in:
Rudi Grinberg 2018-01-23 02:58:06 +08:00 committed by GitHub
commit a34f71f922
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 207 additions and 116 deletions

View File

@ -634,7 +634,13 @@ Add it to your jbuild file to remove this warning.
);
(* Odoc *)
Odoc.setup_library_rules sctx lib ~dir ~requires ~modules ~dep_graph;
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_graph
~mld_files
;
let flags =
match alias_module with

View File

@ -48,7 +48,7 @@ let cmt_file t ~dir (kind : Ml_kind.t) =
| Impl -> Some (Path.relative dir (t.obj_name ^ ".cmt"))
| Intf -> Option.map t.intf ~f:(fun _ -> Path.relative dir (t.obj_name ^ ".cmti"))
let odoc_file t ~dir = Path.relative dir (t.obj_name ^ ".odoc")
let odoc_file t ~doc_dir = Path.relative doc_dir (t.obj_name ^ ".odoc")
let cmti_file t ~dir =
match t.intf with

View File

@ -31,7 +31,7 @@ val cm_source : t -> dir:Path.t -> Cm_kind.t -> Path.t option
val cm_file : t -> dir:Path.t -> Cm_kind.t -> Path.t
val cmt_file : t -> dir:Path.t -> Ml_kind.t -> Path.t option
val odoc_file : t -> dir:Path.t -> Path.t
val odoc_file : t -> doc_dir:Path.t -> Path.t
(** Either the .cmti, or .cmt if the module has no interface *)
val cmti_file : t -> dir:Path.t -> Path.t

View File

@ -9,116 +9,156 @@ let ( ++ ) = Path.relative
let get_odoc sctx = SC.resolve_program sctx "odoc" ~hint:"opam install odoc"
let odoc_ext = ".odoc"
let module_deps (m : Module.t) ~dir ~dep_graph ~modules =
module Mld : sig
type t
val create : name:string -> t
val odoc_file : doc_dir:Path.t -> t -> Path.t
val odoc_input : doc_dir:Path.t -> t -> Path.t
val html_filename : t -> string
end = struct
type t = string (** source file name without the extension. *)
let create ~name = name
let odoc_file ~doc_dir t =
Path.relative doc_dir (sprintf "page-%s%s" t odoc_ext)
let odoc_input ~doc_dir t =
Path.relative doc_dir (sprintf "%s-generated.mld" t)
let html_filename t =
sprintf "%s.html" t
end
module Module_or_mld = struct
type t =
| Mld of Mld.t
| Module of Module.t
let odoc_file ~doc_dir = function
| Mld m -> Mld.odoc_file ~doc_dir m
| Module m -> Module.odoc_file ~doc_dir m
let odoc_input ~dir ~doc_dir = function
| Mld m -> Mld.odoc_input ~doc_dir m
| Module m -> Module.cmti_file m ~dir
let html_dir ~doc_dir = function
| Mld _ -> doc_dir
| Module m -> doc_dir ++ String.capitalize_ascii m.obj_name
let html_file ~doc_dir t =
match t with
| 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) ~dir ~doc_dir ~dep_graph ~modules =
Build.dyn_paths
(dep_graph
>>^ fun graph ->
List.map (Utils.find_deps ~dir graph m.name)
~f:(fun name ->
let m = Utils.find_module ~dir modules name in
Module.odoc_file m ~dir))
match m with
| Mld _ -> []
| Module m ->
List.map (Utils.find_deps ~dir graph m.name)
~f:(fun name ->
let m = Utils.find_module ~dir modules name in
Module.odoc_file m ~doc_dir))
let compile_module sctx (m : Module.t) ~odoc ~dir ~includes ~dep_graph ~modules
~lib_unique_name =
let compile sctx (m : Module_or_mld.t) ~odoc ~dir ~includes ~dep_graph
~doc_dir ~modules ~lib_unique_name =
let context = SC.context sctx in
let odoc_file = Module.odoc_file m ~dir in
let odoc_file = Module_or_mld.odoc_file m ~doc_dir in
SC.add_rule sctx
(module_deps m ~dir ~dep_graph ~modules
(module_or_mld_deps m ~doc_dir ~dir ~dep_graph ~modules
>>>
includes
>>>
Build.run ~context ~dir odoc ~extra_targets:[odoc_file]
Build.run ~context ~dir:doc_dir odoc
[ A "compile"
; Dyn (fun x -> x)
; A "-I"; Path dir
; Dyn (fun x -> x)
; As ["--pkg"; lib_unique_name]
; Dep (Module.cmti_file m ~dir)
; A "-o"; Target odoc_file
; Dep (Module_or_mld.odoc_input m ~dir ~doc_dir)
]);
(m, odoc_file)
let to_html sctx (m : Module.t) odoc_file ~doc_dir ~odoc ~dir ~includes
~lib_unique_name ~(lib : Library.t) =
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 = doc_dir ++ lib_unique_name ++ String.capitalize_ascii m.obj_name in
let html_file = html_dir ++ "index.html" 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
(SC.Libs.static_file_deps (dir, lib) ~ext:odoc_ext
(SC.Doc.static_deps sctx (dir, lib)
>>>
includes
>>>
Build.progn
[ Build.remove_tree html_dir
; Build.mkdir html_dir
; Build.run ~context ~dir odoc ~extra_targets:[html_file]
[ A "html"
; Dyn (fun x -> x)
; A "-I"; Path dir
; A "-o"; Path doc_dir
; Dep odoc_file
]
; Build.create_file (html_dir ++ Config.jbuilder_keep_fname)
]
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 lib_index sctx ~odoc ~dir ~(lib : Library.t) ~lib_name ~lib_unique_name ~doc_dir ~modules
~includes =
let context = SC.context sctx in
let generated_index_mld = dir ++ sprintf "%s-generated.mld" lib.name in
let source_index_mld = dir ++ sprintf "%s.mld" lib.name in
let header = "{{: ../index.html} Up}" in
SC.add_rule sctx
(Build.if_file_exists source_index_mld
~then_:(Build.contents source_index_mld
>>^ fun s -> sprintf "%s\n%s" header s)
~else_:(Build.arr (fun () ->
(if lib.wrapped then
sprintf
"%s\n\
{2 Library %s}\n\
The entry point for this library is module {!module:%s}."
header
lib_name
(String.capitalize_ascii lib.name)
else
sprintf
"%s\n\
{2 Library %s}\n\
This library exposes the following toplevel modules:\n{!modules:%s}"
header
lib_name
(String_map.keys modules |> String.concat ~sep:" "))))
>>>
Build.write_file_dyn generated_index_mld);
let html_file =
doc_dir ++ lib_unique_name ++ "index.html"
let all_mld_files sctx ~(lib : Library.t) ~lib_name ~modules ~dir files =
let all_files =
if List.mem "index.mld" ~set:files then files else "index.mld" :: files
in
SC.add_rule sctx
(SC.Libs.static_file_deps (dir, lib) ~ext:odoc_ext
>>>
includes
>>>
Build.run ~context ~dir odoc ~extra_targets:[html_file]
[ A "html"
; Dyn (fun x -> x)
; A "-I"; Path dir
; A "-o"; Path doc_dir
; A "--index-for"; A lib_unique_name
; Dep generated_index_mld
]);
html_file
let doc_dir ~context = Path.relative context.Context.build_dir "_doc"
let doc_dir = SC.Doc.dir sctx (dir, 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
(Build.if_file_exists source_mld
~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_ascii lib.name)
else
sprintf
"{1 Library %s}\n\
This library exposes the following toplevel modules: {!modules:%s}."
lib_name
(String_map.keys modules |> String.concat ~sep:" "))))
>>>
Build.write_file_dyn generated_mld);
mld
)
let css_file ~doc_dir = doc_dir ++ "odoc.css"
let toplevel_index ~doc_dir = doc_dir ++ "index.html"
let setup_library_rules sctx (lib : Library.t) ~dir ~modules ~requires
~(dep_graph:Ocamldep.dep_graph) =
let setup_library_rules sctx (lib : Library.t) ~dir ~modules ~mld_files
~requires ~(dep_graph:Ocamldep.dep_graph) =
let doc_dir = SC.Doc.dir sctx (dir, lib) in
let lib_unique_name = SC.unique_library_name sctx (Internal (dir, lib)) in
let lib_name = Library.best_name lib in
let context = SC.context sctx in
let dep_graph =
Build.memoize "odoc deps"
((* Use the dependency graph given by ocamldep. However, when a module has no
@ -135,45 +175,46 @@ let setup_library_rules sctx (lib : Library.t) ~dir ~modules ~requires
let includes =
Build.memoize "includes"
(requires
>>>
SC.Libs.file_deps sctx ~ext:odoc_ext
>>> SC.Doc.deps sctx
>>^ Lib.include_flags)
in
let mld_files =
all_mld_files sctx ~dir ~lib ~lib_name ~modules mld_files
in
let mld_and_odoc_files =
List.map mld_files ~f:(fun m ->
compile sctx ~odoc ~dir ~includes ~dep_graph ~modules
~doc_dir ~lib_unique_name (Mld m))
in
let modules_and_odoc_files =
List.map (String_map.values modules)
~f:(compile_module sctx ~odoc ~dir ~includes ~dep_graph ~modules
~lib_unique_name)
List.map (String_map.values modules) ~f:(fun m ->
compile sctx ~odoc ~dir ~includes ~dep_graph ~modules
~doc_dir ~lib_unique_name (Module m))
in
SC.Libs.setup_file_deps_alias sctx ~ext:odoc_ext (dir, lib)
(List.map modules_and_odoc_files ~f:snd);
let doc_dir = doc_dir ~context in
(*
let modules_and_odoc_files =
if lib.wrapped then
let main_module_name = String.capitalize_ascii 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 inputs_and_odoc_files = modules_and_odoc_files @ mld_and_odoc_files in
SC.Doc.setup_deps sctx (dir, lib) (List.map inputs_and_odoc_files ~f:snd);
(*
let modules_and_odoc_files =
if lib.wrapped then
let main_module_name = String.capitalize_ascii 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 modules_and_odoc_files ~f:(fun (m, odoc_file) ->
to_html sctx m odoc_file ~doc_dir ~odoc ~dir ~includes ~lib
~lib_unique_name)
in
let lib_index_html =
lib_index sctx ~dir ~lib ~lib_unique_name ~lib_name ~doc_dir
~modules ~includes ~odoc
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 = SC.Doc.root sctx in
SC.add_alias_deps sctx (Build_system.Alias.doc ~dir)
(css_file ~doc_dir
:: toplevel_index ~doc_dir
:: lib_index_html
(css_file ~doc_dir:doc_root
:: toplevel_index ~doc_dir:doc_root
:: html_files)
let setup_css_rule sctx =
let context = SC.context sctx in
let doc_dir = doc_dir ~context in
let doc_dir = SC.Doc.root sctx in
SC.add_rule sctx
(Build.run ~context
~dir:context.build_dir
@ -224,8 +265,7 @@ let setup_toplevel_index_rule sctx =
</html>
|} list_items
in
let context = SC.context sctx in
let doc_dir = doc_dir ~context in
let doc_dir = SC.Doc.root sctx in
SC.add_rule sctx @@ Build.write_file (toplevel_index ~doc_dir) html
let gen_rules sctx ~dir rest =

View File

@ -8,6 +8,7 @@ val setup_library_rules
-> Library.t
-> dir:Path.t
-> modules:Module.t String_map.t
-> mld_files:string list
-> requires:(unit, Lib.t list) Build.t
-> dep_graph:Ocamldep.dep_graph
-> unit

View File

@ -366,6 +366,30 @@ module Libs = struct
Alias.dep (lib_files_alias lib ~ext)
end
module Doc = struct
let root t = Path.relative t.context.Context.build_dir "_doc"
let dir t lib =
let name = unique_library_name t (Lib.Internal lib) in
Path.relative (root t) name
let alias t ((_, lib) as ilib) =
let doc_dir = dir t ilib in
Alias.make (sprintf "odoc-%s%s-all" lib.name ".odoc") ~dir:doc_dir
let deps t =
Build.dyn_paths (Build.arr (
List.fold_left ~init:[] ~f:(fun acc (lib : Lib.t) ->
match lib with
| External _ -> acc
| Internal lib -> (Alias.stamp_file (alias t lib)) :: acc
)))
let static_deps t lib = Alias.dep (alias t lib)
let setup_deps t lib files = add_alias_deps t (alias t lib) files
end
module Deps = struct
open Build.O
open Dep_conf

View File

@ -164,6 +164,18 @@ module Deps : sig
-> (unit, Path.t list) Build.t
end
module Doc : sig
val root : t -> Path.t
val dir : t -> Lib.Internal.t -> Path.t
val deps : t -> (Lib.t list, Lib.t list) Build.t
val static_deps : t -> Lib.Internal.t -> ('a, 'a) Build.t
val setup_deps : t -> Lib.Internal.t -> Path.t list -> unit
end
(** Interpret action written in jbuild files *)
module Action : sig
type targets =

View File

@ -7,9 +7,11 @@ This test checks that there is no clash when two private libraries have the same
ocamldep b/test.depends.ocamldep-output
ocamldep b/test.dependsi.ocamldep-output
ocamlc a/test.{cmi,cmo,cmt}
odoc _doc/test@a/page-index.odoc
ocamlc b/test.{cmi,cmo,cmt}
odoc a/test.odoc
odoc b/test.odoc
odoc _doc/test@b/page-index.odoc
odoc _doc/test@a/test.odoc
odoc _doc/test@b/test.odoc
odoc _doc/test@a/index.html
odoc _doc/test@a/Test/.jbuilder-keep,_doc/test@a/Test/index.html
odoc _doc/test@b/index.html

View File

@ -5,12 +5,18 @@
ocamldep foo.dependsi.ocamldep-output
odoc _doc/odoc.css
ocamlc foo_byte.{cmi,cmo,cmt}
odoc _doc/foo.byte/page-index.odoc
odoc _doc/foo.byte/page-test.odoc
ocamlc foo.{cmi,cmo,cmt}
odoc foo_byte.odoc
odoc foo.odoc
odoc _doc/foo/page-index.odoc
odoc _doc/foo/page-test.odoc
odoc _doc/foo.byte/foo_byte.odoc
odoc _doc/foo/foo.odoc
odoc _doc/foo.byte/index.html
odoc _doc/foo.byte/test.html
odoc _doc/foo.byte/Foo_byte/.jbuilder-keep,_doc/foo.byte/Foo_byte/index.html
odoc _doc/foo/index.html
odoc _doc/foo/test.html
odoc _doc/foo/Foo/.jbuilder-keep,_doc/foo/Foo/index.html
$ $JBUILDER runtest -j1 --root .
<!DOCTYPE html>