@@ -784,21 +784,7 @@ let odoc_artefacts sctx target =
784784 | Lib lib ->
785785 let info = Lib.Local. info lib in
786786 let obj_dir = Lib_info. obj_dir info in
787- let + modules =
788- let + modules = Dir_contents. modules_of_local_lib sctx lib in
789- Modules. fold modules ~init: [] ~f: (fun m acc ->
790- (* Only include modules that:
791- 1. Have public visibility
792- 2. Are not wrapped library implementation modules (those with __ in the name) *)
793- if Module. visibility m = Public
794- && (Module. obj_name m
795- |> Module_name.Unique. to_name ~loc: Loc. none
796- |> Module_name. to_string
797- |> String. contains_double_underscore
798- |> not )
799- then m :: acc
800- else acc)
801- in
787+ let + modules = entry_modules_by_lib sctx lib in
802788 List. map modules ~f: (fun m ->
803789 let odoc_file = Obj_dir.Module. odoc obj_dir m in
804790 create_odoc ctx ~target odoc_file)
@@ -905,10 +891,15 @@ let out_files ctx (output : Output_format.t) odocs =
905891;;
906892
907893let 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)
894+ match (format : Output_format.t ) with
895+ | Markdown ->
896+ (* skip intermediate aliases since package directories are directory targets *)
897+ Memo. return ()
898+ | Html | Json ->
899+ let paths = out_files ctx format odocs in
900+ Rules.Produce.Alias. add_deps
901+ (Dep. format_alias format ctx target)
902+ (Action_builder. paths paths)
912903;;
913904
914905let setup_lib_html_rules_def =
@@ -1010,7 +1001,15 @@ let setup_pkg_html_rules sctx ~pkg : unit Memo.t =
10101001let setup_lib_markdown_rules sctx lib =
10111002 let target = Lib lib in
10121003 let * odocs = odoc_artefacts sctx target in
1013- let * () = Memo. parallel_iter odocs ~f: (fun odoc -> setup_generate_markdown sctx odoc) in
1004+ (* For libraries WITH a package, generation happens in the package-level rule.
1005+ Only generate for libraries WITHOUT a package. *)
1006+ let * () =
1007+ match Lib_info. package (Lib.Local. info lib) with
1008+ | Some _ -> Memo. return () (* Package-level rule handles it *)
1009+ | None ->
1010+ (* No package, so we need individual rules *)
1011+ Memo. parallel_iter odocs ~f: (fun odoc -> setup_generate_markdown sctx odoc)
1012+ in
10141013 Memo.With_implicit_output. exec setup_lib_markdown_rules_def (sctx, lib)
10151014;;
10161015
@@ -1023,8 +1022,45 @@ let setup_pkg_markdown_rules_def =
10231022 Memo.List. concat_map libs ~f: (fun lib -> odoc_artefacts sctx (Lib lib))
10241023 in
10251024 let all_odocs = pkg_odocs @ lib_odocs in
1025+ (* Generate ALL markdown for this package in ONE rule with directory target.
1026+ Since odoc generates unpredictable files (Belt.md, Belt-Array.md, Belt-List.md, etc.)
1027+ from nested submodules, we must use a directory target and batch all odoc commands. *)
1028+ let * () =
1029+ if List. is_empty all_odocs
1030+ then Memo. return ()
1031+ else
1032+ let pkg_markdown_dir = Paths. markdown ctx (Pkg pkg) in
1033+ let markdown_root = Paths. markdown_root ctx in
1034+ let rule =
1035+ let bash_cmd_args =
1036+ let open Action_builder.O in
1037+ let * odoc_prog = odoc_program sctx (Context. build_dir ctx) in
1038+ let odoc_path = Action.Prog. ok_exn odoc_prog |> Path. to_string in
1039+ let bash_cmd =
1040+ List. map all_odocs ~f: (fun odoc ->
1041+ let odocl_rel = Path. reach (Path. build odoc.odocl_file) ~from: (Path. build markdown_root) in
1042+ Printf. sprintf " %s markdown-generate -o . %s" odoc_path odocl_rel)
1043+ |> String. concat ~sep: " && "
1044+ in
1045+ let * () =
1046+ List. map all_odocs ~f: (fun odoc -> Action_builder. path (Path. build odoc.odocl_file))
1047+ |> Action_builder. all
1048+ >> | ignore
1049+ in
1050+ Action_builder. return (Command.Args. S [ A " -c" ; A bash_cmd ])
1051+ in
1052+ let deps = Action_builder. env_var " ODOC_SYNTAX" in
1053+ let open Action_builder.With_targets.O in
1054+ Action_builder. with_no_targets deps
1055+ >>> Command. run
1056+ ~dir: (Path. build markdown_root)
1057+ (Ok (Path. of_string " /bin/bash" ))
1058+ [ Dyn bash_cmd_args ]
1059+ |> Action_builder.With_targets. add_directories ~directory_targets: [ pkg_markdown_dir ]
1060+ in
1061+ add_rule sctx rule
1062+ in
10261063 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
10281064 add_format_alias_deps ctx Markdown (Pkg pkg) all_odocs
10291065 in
10301066 setup_pkg_rules_def " setup-package-markdown-rules" f
@@ -1045,11 +1081,20 @@ let setup_package_aliases_format sctx (pkg : Package.t) (output : Output_format.
10451081 let * libs =
10461082 Context. name ctx |> libs_of_pkg ~pkg: name >> | List. map ~f: (fun lib -> Lib lib)
10471083 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
1084+ let deps =
1085+ match (output : Output_format.t ) with
1086+ | Markdown ->
1087+ let directory_target = Paths. markdown ctx (Pkg name) in
1088+ let toplevel_index = Paths. markdown_index ctx in
1089+ let * () = Action_builder. path (Path. build directory_target) in
1090+ Action_builder. path (Path. build toplevel_index)
1091+ | Html | Json ->
1092+ Pkg name :: libs
1093+ |> List. map ~f: (Dep. format_alias output ctx)
1094+ |> Dune_engine.Dep.Set. of_list_map ~f: (fun f -> Dune_engine.Dep. alias f)
1095+ |> Action_builder. deps
1096+ in
1097+ Rules.Produce.Alias. add_deps alias deps
10531098;;
10541099
10551100let setup_package_aliases sctx (pkg : Package.t ) =
@@ -1185,36 +1230,29 @@ let gen_rules sctx ~dir rest =
11851230 >>> setup_css_rule sctx
11861231 >>> setup_toplevel_index_rule sctx Html
11871232 >>> setup_toplevel_index_rule sctx Json )
1188- | [ " _markdown" ] -> has_rules (setup_toplevel_index_rule sctx Markdown )
1189- | [ " _markdown" ; lib_unique_name_or_pkg ] ->
1233+ | [ " _markdown" ] ->
1234+ let * packages = Dune_load. packages () in
1235+ let ctx = Super_context. context sctx in
1236+ let all_package_dirs =
1237+ Package.Name.Map. to_list packages
1238+ |> List. map ~f: (fun (_ , (pkg : Package.t )) ->
1239+ let pkg_name = Package. name pkg in
1240+ Paths. markdown ctx (Pkg pkg_name))
1241+ in
1242+ let directory_targets =
1243+ List. fold_left all_package_dirs ~init: Path.Build.Map. empty ~f: (fun acc dir ->
1244+ Path.Build.Map. set acc dir Loc. none)
1245+ in
11901246 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- () )
1247+ ~directory_targets
1248+ (let * () = setup_toplevel_index_rule sctx Markdown in
1249+ Package.Name.Map. to_seq packages
1250+ |> Memo. parallel_iter_seq ~f: (fun (_ , (pkg : Package.t )) ->
1251+ let pkg_name = Package. name pkg in
1252+ setup_pkg_markdown_rules sctx ~pkg: pkg_name))
1253+ | [ " _markdown" ; _lib_unique_name_or_pkg ] ->
1254+ (* package directories are directory targets *)
1255+ Memo. return Gen_rules. no_rules
12181256 | [ " _mlds" ; pkg ] ->
12191257 with_package pkg ~f: (fun pkg ->
12201258 let pkg = Package. name pkg in
0 commit comments