Skip to content

Commit 85dcde2

Browse files
committed
Create dynamically markdown rules
1 parent 4c83d4e commit 85dcde2

File tree

1 file changed

+87
-46
lines changed

1 file changed

+87
-46
lines changed

src/dune_rules/odoc.ml

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,6 @@ let setup_generate sctx ~search_db odoc_file out =
486486
, [ Command.Args.A "-o"
487487
; Command.Args.Path (Path.build (Paths.markdown_root ctx))
488488
; Command.Args.Dep (Path.build odoc_file.odocl_file)
489-
; Command.Args.Hidden_targets [ Output_format.target out odoc_file ]
490489
] )
491490
| Html | Json ->
492491
let search_args =
@@ -520,9 +519,6 @@ let setup_generate_html_and_json sctx ~search_db odoc_file =
520519
setup_generate sctx ~search_db:(Some search_db) odoc_file Json
521520
;;
522521

523-
let setup_generate_markdown sctx odoc_file =
524-
setup_generate sctx ~search_db:None odoc_file Markdown
525-
;;
526522

527523
let setup_css_rule sctx =
528524
let ctx = Super_context.context sctx in
@@ -891,10 +887,15 @@ let out_files ctx (output : Output_format.t) odocs =
891887
;;
892888

893889
let add_format_alias_deps ctx format target odocs =
894-
let paths = out_files ctx format odocs in
895-
Rules.Produce.Alias.add_deps
896-
(Dep.format_alias format ctx target)
897-
(Action_builder.paths paths)
890+
match (format : Output_format.t) with
891+
| Markdown ->
892+
(* skip intermediate aliases since package directories are directory targets *)
893+
Memo.return ()
894+
| Html | Json ->
895+
let paths = out_files ctx format odocs in
896+
Rules.Produce.Alias.add_deps
897+
(Dep.format_alias format ctx target)
898+
(Action_builder.paths paths)
898899
;;
899900

900901
let setup_lib_html_rules_def =
@@ -994,9 +995,7 @@ let setup_pkg_html_rules sctx ~pkg : unit Memo.t =
994995
;;
995996

996997
let setup_lib_markdown_rules sctx lib =
997-
let target = Lib lib in
998-
let* odocs = odoc_artefacts sctx target in
999-
let* () = Memo.parallel_iter odocs ~f:(fun odoc -> setup_generate_markdown sctx odoc) in
998+
(* we don't call setup_generate_markdown per module because all modules in a package write to the same directory. Instead, the package-level rule will handle all generation. *)
1000999
Memo.With_implicit_output.exec setup_lib_markdown_rules_def (sctx, lib)
10011000
;;
10021001

@@ -1009,8 +1008,45 @@ let setup_pkg_markdown_rules_def =
10091008
Memo.List.concat_map libs ~f:(fun lib -> odoc_artefacts sctx (Lib lib))
10101009
in
10111010
let all_odocs = pkg_odocs @ lib_odocs in
1011+
(* Generate all markdown for this package in ONE rule with directory target.
1012+
Since odoc generates multiple .md files per .odocl and we can't predict the filenames, we declare the entire package directory
1013+
as a directory target. *)
1014+
let* () =
1015+
if List.is_empty all_odocs
1016+
then Memo.return ()
1017+
else
1018+
let pkg_markdown_dir = Paths.markdown ctx (Pkg pkg) in
1019+
let markdown_root = Paths.markdown_root ctx in
1020+
let rule =
1021+
let bash_cmd_args =
1022+
let open Action_builder.O in
1023+
let* odoc_prog = odoc_program sctx (Context.build_dir ctx) in
1024+
let odoc_path = Action.Prog.ok_exn odoc_prog |> Path.to_string in
1025+
let bash_cmd =
1026+
List.map all_odocs ~f:(fun odoc ->
1027+
let odocl_rel = Path.reach (Path.build odoc.odocl_file) ~from:(Path.build markdown_root) in
1028+
Printf.sprintf "%s markdown-generate -o . %s" odoc_path odocl_rel)
1029+
|> String.concat ~sep:" && "
1030+
in
1031+
let* () =
1032+
List.map all_odocs ~f:(fun odoc -> Action_builder.path (Path.build odoc.odocl_file))
1033+
|> Action_builder.all
1034+
>>| ignore
1035+
in
1036+
Action_builder.return (Command.Args.S [ A "-c"; A bash_cmd ])
1037+
in
1038+
let deps = Action_builder.env_var "ODOC_SYNTAX" in
1039+
let open Action_builder.With_targets.O in
1040+
Action_builder.with_no_targets deps
1041+
>>> Command.run
1042+
~dir:(Path.build markdown_root)
1043+
(Ok (Path.of_string "/bin/bash"))
1044+
[ Dyn bash_cmd_args ]
1045+
|> Action_builder.With_targets.add_directories ~directory_targets:[ pkg_markdown_dir ]
1046+
in
1047+
add_rule sctx rule
1048+
in
10121049
let* () = Memo.parallel_iter libs ~f:(setup_lib_markdown_rules sctx) in
1013-
let* () = Memo.parallel_iter pkg_odocs ~f:(setup_generate_markdown sctx) in
10141050
add_format_alias_deps ctx Markdown (Pkg pkg) all_odocs
10151051
in
10161052
setup_pkg_rules_def "setup-package-markdown-rules" f
@@ -1031,11 +1067,21 @@ let setup_package_aliases_format sctx (pkg : Package.t) (output : Output_format.
10311067
let* libs =
10321068
Context.name ctx |> libs_of_pkg ~pkg:name >>| List.map ~f:(fun lib -> Lib lib)
10331069
in
1034-
Pkg name :: libs
1035-
|> List.map ~f:(Dep.format_alias output ctx)
1036-
|> Dune_engine.Dep.Set.of_list_map ~f:(fun f -> Dune_engine.Dep.alias f)
1037-
|> Action_builder.deps
1038-
|> Rules.Produce.Alias.add_deps alias
1070+
let deps =
1071+
match (output : Output_format.t) with
1072+
| Markdown ->
1073+
let pkg_markdown_dir = Paths.markdown ctx (Pkg name) in
1074+
let index_path = Paths.markdown_index ctx in
1075+
let open Action_builder.O in
1076+
let* () = Action_builder.path (Path.build pkg_markdown_dir) in
1077+
Action_builder.path (Path.build index_path)
1078+
| Html | Json ->
1079+
Pkg name :: libs
1080+
|> List.map ~f:(Dep.format_alias output ctx)
1081+
|> Dune_engine.Dep.Set.of_list_map ~f:(fun f -> Dune_engine.Dep.alias f)
1082+
|> Action_builder.deps
1083+
in
1084+
Rules.Produce.Alias.add_deps alias deps
10391085
;;
10401086

10411087
let setup_package_aliases sctx (pkg : Package.t) =
@@ -1171,36 +1217,31 @@ let gen_rules sctx ~dir rest =
11711217
>>> setup_css_rule sctx
11721218
>>> setup_toplevel_index_rule sctx Html
11731219
>>> setup_toplevel_index_rule sctx Json)
1174-
| [ "_markdown" ] -> has_rules (setup_toplevel_index_rule sctx Markdown)
1175-
| [ "_markdown"; lib_unique_name_or_pkg ] ->
1220+
| [ "_markdown" ] ->
1221+
(* Generate all markdown and declare package directories as directory targets *)
1222+
let* packages = Dune_load.packages () in
1223+
let ctx = Super_context.context sctx in
1224+
(* Collect all package directories that will be generated as directory targets *)
1225+
let pkg_dirs =
1226+
Package.Name.Map.to_list packages
1227+
|> List.map ~f:(fun (_, (pkg : Package.t)) ->
1228+
let pkg_name = Package.name pkg in
1229+
Paths.markdown ctx (Pkg pkg_name))
1230+
in
1231+
let directory_targets =
1232+
List.fold_left pkg_dirs ~init:Path.Build.Map.empty ~f:(fun acc dir ->
1233+
Path.Build.Map.set acc dir Loc.none)
1234+
in
11761235
has_rules
1177-
(
1178-
let ctx = Super_context.context sctx in
1179-
let* lib, lib_db = Scope_key.of_string (Context.name ctx) lib_unique_name_or_pkg in
1180-
let* lib =
1181-
let+ lib = Lib.DB.find lib_db lib in
1182-
Option.bind ~f:Lib.Local.of_lib lib
1183-
in
1184-
let+ () =
1185-
match lib with
1186-
| None -> Memo.return ()
1187-
| Some lib ->
1188-
(match Lib_info.package (Lib.Local.info lib) with
1189-
| None ->
1190-
(* lib with no package above it *)
1191-
setup_lib_markdown_rules sctx lib
1192-
| Some pkg -> setup_pkg_markdown_rules sctx ~pkg)
1193-
and+ () =
1194-
let* packages = Dune_load.packages () in
1195-
match
1196-
Package.Name.Map.find packages (Package.Name.of_string lib_unique_name_or_pkg)
1197-
with
1198-
| None -> Memo.return ()
1199-
| Some pkg ->
1200-
let name = Package.name pkg in
1201-
setup_pkg_markdown_rules sctx ~pkg:name
1202-
in
1203-
())
1236+
~directory_targets
1237+
(let* () = setup_toplevel_index_rule sctx Markdown in
1238+
Package.Name.Map.to_seq packages
1239+
|> Memo.parallel_iter_seq ~f:(fun (_, (pkg : Package.t)) ->
1240+
let pkg_name = Package.name pkg in
1241+
setup_pkg_markdown_rules sctx ~pkg:pkg_name))
1242+
| [ "_markdown"; _lib_unique_name_or_pkg ] ->
1243+
(* Package directories are directory targets, no rules here *)
1244+
Memo.return Gen_rules.no_rules
12041245
| [ "_mlds"; pkg ] ->
12051246
with_package pkg ~f:(fun pkg ->
12061247
let pkg = Package.name pkg in

0 commit comments

Comments
 (0)