@@ -905,10 +905,15 @@ let out_files ctx (output : Output_format.t) odocs =
905905;;
906906
907907let add_format_alias_deps ctx format target odocs =
908- let paths = out_files ctx format odocs in
909- Rules.Produce.Alias. add_deps
910- (Dep. format_alias format ctx target)
911- (Action_builder. paths paths)
908+ match (format : Output_format.t ) with
909+ | Markdown ->
910+ (* For markdown, skip intermediate aliases since package directories are directory targets *)
911+ Memo. return ()
912+ | Html | Json ->
913+ let paths = out_files ctx format odocs in
914+ Rules.Produce.Alias. add_deps
915+ (Dep. format_alias format ctx target)
916+ (Action_builder. paths paths)
912917;;
913918
914919let setup_lib_html_rules_def =
@@ -1010,7 +1015,15 @@ let setup_pkg_html_rules sctx ~pkg : unit Memo.t =
10101015let setup_lib_markdown_rules sctx lib =
10111016 let target = Lib lib in
10121017 let * odocs = odoc_artefacts sctx target in
1013- let * () = Memo. parallel_iter odocs ~f: (fun odoc -> setup_generate_markdown sctx odoc) in
1018+ (* For libraries WITH a package, generation happens in the package-level rule.
1019+ Only generate for libraries WITHOUT a package. *)
1020+ let * () =
1021+ match Lib_info. package (Lib.Local. info lib) with
1022+ | Some _ -> Memo. return () (* Package-level rule handles it *)
1023+ | None ->
1024+ (* No package, so we need individual rules *)
1025+ Memo. parallel_iter odocs ~f: (fun odoc -> setup_generate_markdown sctx odoc)
1026+ in
10141027 Memo.With_implicit_output. exec setup_lib_markdown_rules_def (sctx, lib)
10151028;;
10161029
@@ -1023,8 +1036,45 @@ let setup_pkg_markdown_rules_def =
10231036 Memo.List. concat_map libs ~f: (fun lib -> odoc_artefacts sctx (Lib lib))
10241037 in
10251038 let all_odocs = pkg_odocs @ lib_odocs in
1039+ (* Generate ALL markdown for this package in ONE rule with directory target.
1040+ Since odoc generates unpredictable files (Belt.md, Belt-Array.md, Belt-List.md, etc.)
1041+ from nested submodules, we must use a directory target and batch all odoc commands. *)
1042+ let * () =
1043+ if List. is_empty all_odocs
1044+ then Memo. return ()
1045+ else
1046+ let pkg_markdown_dir = Paths. markdown ctx (Pkg pkg) in
1047+ let markdown_root = Paths. markdown_root ctx in
1048+ let rule =
1049+ let bash_cmd_args =
1050+ let open Action_builder.O in
1051+ let * odoc_prog = odoc_program sctx (Context. build_dir ctx) in
1052+ let odoc_path = Action.Prog. ok_exn odoc_prog |> Path. to_string in
1053+ let bash_cmd =
1054+ List. map all_odocs ~f: (fun odoc ->
1055+ let odocl_rel = Path. reach (Path. build odoc.odocl_file) ~from: (Path. build markdown_root) in
1056+ Printf. sprintf " %s markdown-generate -o . %s" odoc_path odocl_rel)
1057+ |> String. concat ~sep: " && "
1058+ in
1059+ let * () =
1060+ List. map all_odocs ~f: (fun odoc -> Action_builder. path (Path. build odoc.odocl_file))
1061+ |> Action_builder. all
1062+ >> | ignore
1063+ in
1064+ Action_builder. return (Command.Args. S [ A " -c" ; A bash_cmd ])
1065+ in
1066+ let deps = Action_builder. env_var " ODOC_SYNTAX" in
1067+ let open Action_builder.With_targets.O in
1068+ Action_builder. with_no_targets deps
1069+ >>> Command. run
1070+ ~dir: (Path. build markdown_root)
1071+ (Ok (Path. of_string " /bin/bash" ))
1072+ [ Dyn bash_cmd_args ]
1073+ |> Action_builder.With_targets. add_directories ~directory_targets: [ pkg_markdown_dir ]
1074+ in
1075+ add_rule sctx rule
1076+ in
10261077 let * () = Memo. parallel_iter libs ~f: (setup_lib_markdown_rules sctx) in
1027- let * () = Memo. parallel_iter pkg_odocs ~f: (setup_generate_markdown sctx) in
10281078 add_format_alias_deps ctx Markdown (Pkg pkg) all_odocs
10291079 in
10301080 setup_pkg_rules_def " setup-package-markdown-rules" f
@@ -1045,11 +1095,24 @@ let setup_package_aliases_format sctx (pkg : Package.t) (output : Output_format.
10451095 let * libs =
10461096 Context. name ctx |> libs_of_pkg ~pkg: name >> | List. map ~f: (fun lib -> Lib lib)
10471097 in
1048- Pkg name :: libs
1049- |> List. map ~f: (Dep. format_alias output ctx)
1050- |> Dune_engine.Dep.Set. of_list_map ~f: (fun f -> Dune_engine.Dep. alias f)
1051- |> Action_builder. deps
1052- |> Rules.Produce.Alias. add_deps alias
1098+ let deps =
1099+ match (output : Output_format.t ) with
1100+ | Markdown ->
1101+ (* For markdown, depend on:
1102+ 1. The package directory target (contains all module .md files)
1103+ 2. The toplevel index (so it gets built as part of @doc-markdown) *)
1104+ let pkg_markdown_dir = Paths. markdown ctx (Pkg name) in
1105+ let index_path = Paths. markdown_index ctx in
1106+ let open Action_builder.O in
1107+ let * () = Action_builder. path (Path. build pkg_markdown_dir) in
1108+ Action_builder. path (Path. build index_path)
1109+ | Html | Json ->
1110+ Pkg name :: libs
1111+ |> List. map ~f: (Dep. format_alias output ctx)
1112+ |> Dune_engine.Dep.Set. of_list_map ~f: (fun f -> Dune_engine.Dep. alias f)
1113+ |> Action_builder. deps
1114+ in
1115+ Rules.Produce.Alias. add_deps alias deps
10531116;;
10541117
10551118let setup_package_aliases sctx (pkg : Package.t ) =
@@ -1185,36 +1248,31 @@ let gen_rules sctx ~dir rest =
11851248 >>> setup_css_rule sctx
11861249 >>> setup_toplevel_index_rule sctx Html
11871250 >>> setup_toplevel_index_rule sctx Json )
1188- | [ " _markdown" ] -> has_rules (setup_toplevel_index_rule sctx Markdown )
1189- | [ " _markdown" ; lib_unique_name_or_pkg ] ->
1251+ | [ " _markdown" ] ->
1252+ (* Generate all markdown and declare package directories as directory targets *)
1253+ let * packages = Dune_load. packages () in
1254+ let ctx = Super_context. context sctx in
1255+ (* Collect all package directories that will be generated as directory targets *)
1256+ let pkg_dirs =
1257+ Package.Name.Map. to_list packages
1258+ |> List. map ~f: (fun (_ , (pkg : Package.t )) ->
1259+ let pkg_name = Package. name pkg in
1260+ Paths. markdown ctx (Pkg pkg_name))
1261+ in
1262+ let directory_targets =
1263+ List. fold_left pkg_dirs ~init: Path.Build.Map. empty ~f: (fun acc dir ->
1264+ Path.Build.Map. set acc dir Loc. none)
1265+ in
11901266 has_rules
1191- (
1192- let ctx = Super_context. context sctx in
1193- let * lib, lib_db = Scope_key. of_string (Context. name ctx) lib_unique_name_or_pkg in
1194- let * lib =
1195- let + lib = Lib.DB. find lib_db lib in
1196- Option. bind ~f: Lib.Local. of_lib lib
1197- in
1198- let + () =
1199- match lib with
1200- | None -> Memo. return ()
1201- | Some lib ->
1202- (match Lib_info. package (Lib.Local. info lib) with
1203- | None ->
1204- (* lib with no package above it *)
1205- setup_lib_markdown_rules sctx lib
1206- | Some pkg -> setup_pkg_markdown_rules sctx ~pkg )
1207- and + () =
1208- let * packages = Dune_load. packages () in
1209- match
1210- Package.Name.Map. find packages (Package.Name. of_string lib_unique_name_or_pkg)
1211- with
1212- | None -> Memo. return ()
1213- | Some pkg ->
1214- let name = Package. name pkg in
1215- setup_pkg_markdown_rules sctx ~pkg: name
1216- in
1217- () )
1267+ ~directory_targets
1268+ (let * () = setup_toplevel_index_rule sctx Markdown in
1269+ Package.Name.Map. to_seq packages
1270+ |> Memo. parallel_iter_seq ~f: (fun (_ , (pkg : Package.t )) ->
1271+ let pkg_name = Package. name pkg in
1272+ setup_pkg_markdown_rules sctx ~pkg: pkg_name))
1273+ | [ " _markdown" ; _lib_unique_name_or_pkg ] ->
1274+ (* Package directories are directory targets, no rules here *)
1275+ Memo. return Gen_rules. no_rules
12181276 | [ " _mlds" ; pkg ] ->
12191277 with_package pkg ~f: (fun pkg ->
12201278 let pkg = Package. name pkg in
0 commit comments