diff --git a/bin/lock_dev_tool.ml b/bin/lock_dev_tool.ml index bcf676e1b492..de3267a2f180 100644 --- a/bin/lock_dev_tool.ml +++ b/bin/lock_dev_tool.ml @@ -66,17 +66,21 @@ let solve ~local_packages ~lock_dirs = ~lock_dirs ;; -let lock_ocamlformat () : unit Fiber.t = - let version = Dune_pkg.Ocamlformat.version_of_current_project's_ocamlformat_config () in - let ocamlformat_dev_tool_lock_dir = - Dune_pkg.Lock_dir.dev_tool_lock_dir_path Ocamlformat - in - if not (Path.exists @@ Path.source ocamlformat_dev_tool_lock_dir) +let lock_dev_tool dev_tool version = + let dev_tool_lock_dir = Dune_pkg.Lock_dir.dev_tool_lock_dir_path dev_tool in + if not (Path.exists @@ Path.source dev_tool_lock_dir) then ( let local_pkg = - make_local_package_wrapping_dev_tool ~dev_tool:Ocamlformat ~dev_tool_version:version + make_local_package_wrapping_dev_tool ~dev_tool ~dev_tool_version:version in let local_packages = Package_name.Map.singleton local_pkg.name local_pkg in - solve ~local_packages ~lock_dirs:[ ocamlformat_dev_tool_lock_dir ]) + solve ~local_packages ~lock_dirs:[ dev_tool_lock_dir ]) else Fiber.return () ;; + +let lock_ocamlformat () = + let version = Dune_pkg.Ocamlformat.version_of_current_project's_ocamlformat_config () in + lock_dev_tool Ocamlformat version +;; + +let lock_odoc () = lock_dev_tool Odoc None diff --git a/bin/lock_dev_tool.mli b/bin/lock_dev_tool.mli index 85f185ff83fd..b6e3c843ed93 100644 --- a/bin/lock_dev_tool.mli +++ b/bin/lock_dev_tool.mli @@ -1,2 +1,3 @@ val is_enabled : bool Lazy.t val lock_ocamlformat : unit -> unit Fiber.t +val lock_odoc : unit -> unit Fiber.t diff --git a/bin/ocaml/doc.ml b/bin/ocaml/doc.ml index 584438b2f999..e2490b54b416 100644 --- a/bin/ocaml/doc.ml +++ b/bin/ocaml/doc.ml @@ -13,12 +13,20 @@ let man = let info = Cmd.info "doc" ~doc ~man +let lock_odoc_if_dev_tool_enabled () = + match Lazy.force Lock_dev_tool.is_enabled with + | false -> Action_builder.return () + | true -> + Action_builder.of_memo (Memo.of_reproducible_fiber (Lock_dev_tool.lock_odoc ())) +;; + let term = let+ builder = Common.Builder.term in let common, config = Common.init builder in let request (setup : Main.build_system) = let dir = Path.(relative root) (Common.prefix_target common ".") in let open Action_builder.O in + let* () = lock_odoc_if_dev_tool_enabled () in let+ () = Alias.in_dir ~name:Dune_rules.Alias.doc ~recursive:true ~contexts:setup.contexts dir |> Alias.request diff --git a/src/dune_pkg/dev_tool.ml b/src/dune_pkg/dev_tool.ml index 9d927d36469f..590ada5899f9 100644 --- a/src/dune_pkg/dev_tool.ml +++ b/src/dune_pkg/dev_tool.ml @@ -1,29 +1,37 @@ open! Import -type t = Ocamlformat +type t = + | Ocamlformat + | Odoc -let all = [ Ocamlformat ] +let all = [ Ocamlformat; Odoc ] let equal a b = match a, b with | Ocamlformat, Ocamlformat -> true + | Odoc, Odoc -> true + | _ -> false ;; let package_name = function | Ocamlformat -> Package_name.of_string "ocamlformat" + | Odoc -> Package_name.of_string "odoc" ;; let of_package_name package_name = match Package_name.to_string package_name with | "ocamlformat" -> Ocamlformat + | "odoc" -> Odoc | other -> User_error.raise [ Pp.textf "No such dev tool: %s" other ] ;; let exe_name = function | Ocamlformat -> "ocamlformat" + | Odoc -> "odoc" ;; let exe_path_components_within_package t = match t with | Ocamlformat -> [ "bin"; exe_name t ] + | Odoc -> [ "bin"; exe_name t ] ;; diff --git a/src/dune_pkg/dev_tool.mli b/src/dune_pkg/dev_tool.mli index a59e0adf40d2..06e808fd1815 100644 --- a/src/dune_pkg/dev_tool.mli +++ b/src/dune_pkg/dev_tool.mli @@ -1,6 +1,8 @@ open! Import -type t = Ocamlformat +type t = + | Ocamlformat + | Odoc val all : t list val equal : t -> t -> bool diff --git a/src/dune_rules/odoc.ml b/src/dune_rules/odoc.ml index 9a8304c8d0b1..f612be09ebb0 100644 --- a/src/dune_rules/odoc.ml +++ b/src/dune_rules/odoc.ml @@ -245,14 +245,33 @@ let odoc_base_flags quiet build_dir = | Nonfatal -> S [] ;; +let odoc_dev_tool_lock_dir_exists () = + let path = Dune_pkg.Lock_dir.dev_tool_lock_dir_path Odoc in + Fs_memo.dir_exists (Path.source path |> Path.as_outside_build_dir_exn) +;; + +let odoc_dev_tool_exe_path_building_if_necessary () = + let open Action_builder.O in + let path = Path.build (Pkg_dev_tool.exe_path Odoc) in + let+ () = Action_builder.path path in + Ok path +;; + let odoc_program sctx dir = - Super_context.resolve_program - sctx - ~dir - ~where:Original_path - "odoc" - ~loc:None - ~hint:"opam install odoc" + let open Action_builder.O in + let* odoc_dev_tool_lock_dir_exists = + Action_builder.of_memo (odoc_dev_tool_lock_dir_exists ()) + in + match odoc_dev_tool_lock_dir_exists with + | true -> odoc_dev_tool_exe_path_building_if_necessary () + | false -> + Super_context.resolve_program + sctx + ~dir + ~where:Original_path + "odoc" + ~loc:None + ~hint:"opam install odoc" ;; let run_odoc sctx ~dir command ~quiet ~flags_for args = diff --git a/test/blackbox-tests/test-cases/pkg/odoc/dev-tool-odoc-basic.t b/test/blackbox-tests/test-cases/pkg/odoc/dev-tool-odoc-basic.t new file mode 100644 index 000000000000..316df64826e7 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/odoc/dev-tool-odoc-basic.t @@ -0,0 +1,34 @@ +Test that the "dune ocaml doc" command causes odoc to be locked and +build, and then used to generate documentation. + + $ . ../helpers.sh + $ . ./helpers.sh + + $ mkrepo + $ make_mock_odoc_package + + $ setup_odoc_workspace + + $ cat > dune-project < (lang dune 3.16) + > + > (package + > (name foo) + > (allow_empty)) + > EOF + +Lock and build the fake odoc executable, and then use the fake +executable to attempt to generate documentation. The fake executable +doesn't actually do anything so the command fails but this +demonstrates that it was run. + $ DUNE_CONFIG__LOCK_DEV_TOOL=enabled dune ocaml doc + Solution for dev-tools.locks/odoc: + - odoc.0.0.1 + hello from fake odoc + hello from fake odoc + File "_doc/_html/_unknown_", line 1, characters 0-0: + Error: Rule failed to produce directory "_doc/_html/odoc.support" + File "_doc/_odoc/pkg/foo/_unknown_", line 1, characters 0-0: + Error: Rule failed to generate the following targets: + - _doc/_odoc/pkg/foo/page-index.odoc + [1] diff --git a/test/blackbox-tests/test-cases/pkg/odoc/dune b/test/blackbox-tests/test-cases/pkg/odoc/dune new file mode 100644 index 000000000000..552dd6cf4a96 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/odoc/dune @@ -0,0 +1,3 @@ +(cram + (deps helpers.sh) + (applies_to :whole_subtree)) diff --git a/test/blackbox-tests/test-cases/pkg/odoc/helpers.sh b/test/blackbox-tests/test-cases/pkg/odoc/helpers.sh new file mode 100644 index 000000000000..bfc2ba3ddd62 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/odoc/helpers.sh @@ -0,0 +1,27 @@ +# Create a dune-workspace file with mock repos set up for the main +# project and the odoc lockdir. +setup_odoc_workspace() { + cat > dune-workspace < %{bin}%/odoc" ] + [ "sh" "-c" "echo 'echo hello from fake odoc' >> %{bin}%/odoc" ] + [ "sh" "-c" "chmod a+x %{bin}%/odoc" ] +] +EOF +}