From d92b74edca53e8e44f95989949b3a79ecf62be67 Mon Sep 17 00:00:00 2001 From: Stephen Sherratt Date: Thu, 5 Sep 2024 12:27:11 +1000 Subject: [PATCH 1/3] Add ocamllsp dev tool Adds metadata about ocamllsp and generalizes the dev-tool fetching code to support all dev tools rather than specifically ocamlformat. Signed-off-by: Stephen Sherratt --- src/dune_pkg/dev_tool.ml | 9 ++++++++- src/dune_pkg/dev_tool.mli | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dune_pkg/dev_tool.ml b/src/dune_pkg/dev_tool.ml index b2fbda64ec6..45b02f4bd9c 100644 --- a/src/dune_pkg/dev_tool.ml +++ b/src/dune_pkg/dev_tool.ml @@ -3,40 +3,47 @@ open! Import type t = | Ocamlformat | Odoc + | Ocamllsp -let all = [ Ocamlformat; Odoc ] +let all = [ Ocamlformat; Odoc; Ocamllsp ] let equal a b = match a, b with | Ocamlformat, Ocamlformat -> true | Odoc, Odoc -> true + | Ocamllsp, Ocamllsp -> true | _ -> false ;; let package_name = function | Ocamlformat -> Package_name.of_string "ocamlformat" | Odoc -> Package_name.of_string "odoc" + | Ocamllsp -> Package_name.of_string "ocaml-lsp-server" ;; let of_package_name package_name = match Package_name.to_string package_name with | "ocamlformat" -> Ocamlformat | "odoc" -> Odoc + | "ocaml-lsp-server" -> Ocamllsp | other -> User_error.raise [ Pp.textf "No such dev tool: %s" other ] ;; let exe_name = function | Ocamlformat -> "ocamlformat" | Odoc -> "odoc" + | Ocamllsp -> "ocamllsp" ;; let exe_path_components_within_package t = match t with | Ocamlformat -> [ "bin"; exe_name t ] | Odoc -> [ "bin"; exe_name t ] + | Ocamllsp -> [ "bin"; exe_name t ] ;; let needs_to_build_with_same_compiler_as_project = function | Ocamlformat -> false | Odoc -> true + | Ocamllsp -> true ;; diff --git a/src/dune_pkg/dev_tool.mli b/src/dune_pkg/dev_tool.mli index 66c1e474c4a..dde24555a63 100644 --- a/src/dune_pkg/dev_tool.mli +++ b/src/dune_pkg/dev_tool.mli @@ -3,6 +3,7 @@ open! Import type t = | Ocamlformat | Odoc + | Ocamllsp val all : t list val equal : t -> t -> bool From 5d674c44a42d03b4bee5223a7eb761ba5d596625 Mon Sep 17 00:00:00 2001 From: Stephen Sherratt Date: Thu, 5 Sep 2024 12:28:57 +1000 Subject: [PATCH 2/3] Add command to build and run ocamllsp This command will exit with an error message unless it's run within a dune project with a dune.lock directory containing a lockfile for the package "ocaml" so it's not ready for use by editors. Support for falling back to a system or opam-managed installation of ocamllsp will come in a later change. Signed-off-by: Stephen Sherratt --- bin/lock_dev_tool.ml | 1 + bin/lock_dev_tool.mli | 1 + bin/main.ml | 1 + bin/tools/ocamllsp.ml | 62 ++++++++++++++++++++++++++++++++++++ bin/tools/ocamllsp.mli | 4 +++ bin/tools/tools.ml | 11 +++++++ bin/tools/tools.mli | 3 ++ src/dune_rules/dune_rules.ml | 2 ++ 8 files changed, 85 insertions(+) create mode 100644 bin/tools/ocamllsp.ml create mode 100644 bin/tools/ocamllsp.mli create mode 100644 bin/tools/tools.ml create mode 100644 bin/tools/tools.mli diff --git a/bin/lock_dev_tool.ml b/bin/lock_dev_tool.ml index 1bfc6593983..7aa04163df9 100644 --- a/bin/lock_dev_tool.ml +++ b/bin/lock_dev_tool.ml @@ -208,3 +208,4 @@ let lock_ocamlformat () = ;; let lock_odoc () = lock_dev_tool Odoc None +let lock_ocamllsp () = lock_dev_tool Ocamllsp None diff --git a/bin/lock_dev_tool.mli b/bin/lock_dev_tool.mli index 82b4f41e665..eb4309e7960 100644 --- a/bin/lock_dev_tool.mli +++ b/bin/lock_dev_tool.mli @@ -3,3 +3,4 @@ open! Import val is_enabled : bool Lazy.t val lock_ocamlformat : unit -> unit Memo.t val lock_odoc : unit -> unit Memo.t +val lock_ocamllsp : unit -> unit Memo.t diff --git a/bin/main.ml b/bin/main.ml index 61ca4904449..96bd2b6c8bc 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -39,6 +39,7 @@ let all : _ Cmdliner.Cmd.t list = ; Promotion.group ; Pkg.group ; Pkg.Alias.group + ; Tools.group ] in terms @ groups diff --git a/bin/tools/ocamllsp.ml b/bin/tools/ocamllsp.ml new file mode 100644 index 00000000000..8f3ca407d43 --- /dev/null +++ b/bin/tools/ocamllsp.ml @@ -0,0 +1,62 @@ +open! Import +module Pkg_dev_tool = Dune_rules.Pkg_dev_tool + +let ocamllsp_exe_path = Path.build @@ Pkg_dev_tool.exe_path Ocamllsp +let ocamllsp_exe_name = Pkg_dev_tool.exe_name Ocamllsp + +(* Replace the current dune process with ocamllsp. *) +let run_ocamllsp common ~args = + let exe_path_string = Path.to_string ocamllsp_exe_path in + Console.print_user_message + (Dune_rules.Pkg_build_progress.format_user_message + ~verb:"Running" + ~object_: + (User_message.command (String.concat ~sep:" " (ocamllsp_exe_name :: args)))); + Console.finish (); + restore_cwd_and_execve common exe_path_string (exe_path_string :: args) Env.initial +;; + +let build_ocamllsp common = + let open Fiber.O in + let+ result = + Build_cmd.run_build_system ~common ~request:(fun _build_system -> + Action_builder.path ocamllsp_exe_path) + in + match result with + | Error `Already_reported -> raise Dune_util.Report_error.Already_reported + | Ok () -> () +;; + +let is_in_dune_project builder = + Workspace_root.create + ~default_is_cwd:(Common.Builder.default_root_is_cwd builder) + ~specified_by_user:(Common.Builder.root builder) + |> Result.is_ok +;; + +let term = + let+ builder = Common.Builder.term + and+ args = Arg.(value & pos_all string [] (info [] ~docv:"ARGS")) in + match is_in_dune_project builder with + | false -> + User_error.raise + [ Pp.textf + "Unable to run %s as a dev-tool because you don't appear to be inside a dune \ + project." + ocamllsp_exe_name + ] + | true -> + let common, config = Common.init builder in + Scheduler.go ~common ~config (fun () -> + let open Fiber.O in + let* () = Lock_dev_tool.lock_ocamllsp () |> Memo.run in + let+ () = build_ocamllsp common in + run_ocamllsp common ~args) +;; + +let info = + let doc = "Run ocamllsp, installing it as a dev tool if necessary." in + Cmd.info "ocamllsp" ~doc +;; + +let command = Cmd.v info term diff --git a/bin/tools/ocamllsp.mli b/bin/tools/ocamllsp.mli new file mode 100644 index 00000000000..ad07b0fd314 --- /dev/null +++ b/bin/tools/ocamllsp.mli @@ -0,0 +1,4 @@ +open! Import + +(** Command to run ocamllsp, installing it if necessary *) +val command : unit Cmd.t diff --git a/bin/tools/tools.ml b/bin/tools/tools.ml new file mode 100644 index 00000000000..dd399f81b5b --- /dev/null +++ b/bin/tools/tools.ml @@ -0,0 +1,11 @@ +open! Import + +module Exec = struct + let doc = "Command group for running wrapped tools." + let info = Cmd.info ~doc "exec" + let group = Cmd.group info [ Ocamllsp.command ] +end + +let doc = "Command group for wrapped tools." +let info = Cmd.info ~doc "tools" +let group = Cmd.group info [ Exec.group ] diff --git a/bin/tools/tools.mli b/bin/tools/tools.mli new file mode 100644 index 00000000000..d4c5902fcd6 --- /dev/null +++ b/bin/tools/tools.mli @@ -0,0 +1,3 @@ +open Import + +val group : unit Cmd.t diff --git a/src/dune_rules/dune_rules.ml b/src/dune_rules/dune_rules.ml index 9c71a562ff8..8ea18a09c52 100644 --- a/src/dune_rules/dune_rules.ml +++ b/src/dune_rules/dune_rules.ml @@ -70,6 +70,8 @@ module Executables = Executables module Tests = Tests module Stanzas = Stanzas module Lock_dir = Lock_dir +module Pkg_dev_tool = Pkg_dev_tool +module Pkg_build_progress = Pkg_build_progress module Install_rules = struct let install_file = Install_rules.install_file From 32fc569576dda4425e0c77e87c98ad3bb90b35df Mon Sep 17 00:00:00 2001 From: Stephen Sherratt Date: Thu, 12 Sep 2024 14:22:35 +1000 Subject: [PATCH 3/3] Add tests for ocamllsp dev tool Signed-off-by: Stephen Sherratt --- .../pkg/ocamllsp/dev-tool-ocamllsp-basic.t | 32 ++++++++++ .../ocamllsp/dev-tool-ocamllsp-no-lock-dir.t | 15 +++++ .../dev-tool-ocamllsp-no-ocaml-lockfile.t | 20 ++++++ .../dev-tool-ocamllsp-outside-dune-project.t | 17 +++++ ...-ocamllsp-relock-on-ocaml-version-change.t | 63 +++++++++++++++++++ .../test-cases/pkg/ocamllsp/dune | 3 + .../test-cases/pkg/ocamllsp/helpers.sh | 27 ++++++++ 7 files changed, 177 insertions(+) create mode 100644 test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-basic.t create mode 100644 test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-lock-dir.t create mode 100644 test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-ocaml-lockfile.t create mode 100644 test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-outside-dune-project.t create mode 100644 test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-relock-on-ocaml-version-change.t create mode 100644 test/blackbox-tests/test-cases/pkg/ocamllsp/dune create mode 100644 test/blackbox-tests/test-cases/pkg/ocamllsp/helpers.sh diff --git a/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-basic.t b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-basic.t new file mode 100644 index 00000000000..81de4661ec6 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-basic.t @@ -0,0 +1,32 @@ +Test that the "dune tools exec ocamllsp" command causes ocamllsp to be +locked, built and run when the command is run from a dune project with +a lockdir containing an "ocaml" lockfile. + + $ . ../helpers.sh + $ . ./helpers.sh + + $ mkrepo + $ make_mock_ocamllsp_package + $ mkpkg ocaml 5.2.0 + + $ setup_ocamllsp_workspace + + $ cat > dune-project < (lang dune 3.16) + > + > (package + > (name foo) + > (allow_empty)) + > EOF + + $ make_lockdir + $ cat > dune.lock/ocaml.pkg < (version 5.2.0) + > EOF + + $ dune tools exec ocamllsp + Solution for dev-tools.locks/ocaml-lsp-server: + - ocaml.5.2.0 + - ocaml-lsp-server.0.0.1 + Running 'ocamllsp' + hello from fake ocamllsp diff --git a/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-lock-dir.t b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-lock-dir.t new file mode 100644 index 00000000000..3f6c5dc7cdf --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-lock-dir.t @@ -0,0 +1,15 @@ +Exercise the behaviour of "dune tools exec ocamllsp" when run in a +dune project with no lockdir. + + $ cat > dune-project < (lang dune 3.16) + > + > (package + > (name foo) + > (allow_empty)) + > EOF + + $ dune tools exec ocamllsp + Error: Unable to load the lockdir for the default build context. + Hint: Try running 'dune pkg lock' + [1] diff --git a/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-ocaml-lockfile.t b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-ocaml-lockfile.t new file mode 100644 index 00000000000..227b228e372 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-no-ocaml-lockfile.t @@ -0,0 +1,20 @@ +Exercise the behaviour of "dune tools exec ocamllsp" when the lockdir +doesn't contain a lockfile for the "ocaml" package. + + $ . ../helpers.sh + + $ cat > dune-project < (lang dune 3.16) + > + > (package + > (name foo) + > (allow_empty)) + > EOF + + $ make_lockdir + + $ dune tools exec ocamllsp + Error: The lockdir doesn't contain a lockfile for the package "ocaml". + Hint: Add a dependency on "ocaml" to one of the packages in dune-project and + then run 'dune pkg lock' + [1] diff --git a/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-outside-dune-project.t b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-outside-dune-project.t new file mode 100644 index 00000000000..b4c444d45ef --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-outside-dune-project.t @@ -0,0 +1,17 @@ +Exercise the behaviour of "dune tools exec ocamllsp" when run outside +of a dune project. + + +This is necessary for dune to act as it normally would outside of a +dune workspace. + $ unset INSIDE_DUNE + +Run the wrapper command from a temporary directory. With INSIDE_DUNE +unset dune would otherwise pick up the dune project itself as the +current workspace. + $ cd $(mktemp -d) + + $ dune tools exec ocamllsp + Error: Unable to run ocamllsp as a dev-tool because you don't appear to be + inside a dune project. + [1] diff --git a/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-relock-on-ocaml-version-change.t b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-relock-on-ocaml-version-change.t new file mode 100644 index 00000000000..eaa8e2ad6a8 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/ocamllsp/dev-tool-ocamllsp-relock-on-ocaml-version-change.t @@ -0,0 +1,63 @@ +Test that if the version of the "ocaml" package in the project's +lockdir changes then the ocamllsp dev tool is re-locked to be built +with the version of the ocaml compiler now in the project's +lockdir. This is necessary because ocamllsp must be compiled with the +same version of the ocaml compiler as the code that it's analyzing. + + $ . ../helpers.sh + $ . ./helpers.sh + + $ mkrepo + $ make_mock_ocamllsp_package + $ mkpkg ocaml 5.2.0 + $ mkpkg ocaml 5.1.0 + + $ setup_ocamllsp_workspace + + $ cat > dune-project < (lang dune 3.16) + > + > (package + > (name foo) + > (allow_empty)) + > EOF + + $ make_lockdir + $ cat > dune.lock/ocaml.pkg < (version 5.2.0) + > EOF + +Initially ocamllsp will be depend on ocaml.5.2.0 to match the project. + $ dune tools exec ocamllsp + Solution for dev-tools.locks/ocaml-lsp-server: + - ocaml.5.2.0 + - ocaml-lsp-server.0.0.1 + Running 'ocamllsp' + hello from fake ocamllsp + $ cat dev-tools.locks/ocaml-lsp-server/ocaml.pkg + (version 5.2.0) + +We can re-run "dune tools exec ocamllsp" without relocking or rebuilding. + $ dune tools exec ocamllsp + Running 'ocamllsp' + hello from fake ocamllsp + +Change the version of ocaml that the project depends on. + $ cat > dune.lock/ocaml.pkg < (version 5.1.0) + > EOF + +Running "dune tools exec ocamllsp" causes ocamllsp to be relocked and rebuilt +before running. Ocamllsp now depends on ocaml.5.1.0. + $ dune tools exec ocamllsp + The version of the compiler package ("ocaml") in this project's lockdir has + changed to 5.1.0 (formerly the compiler version was 5.2.0). The dev-tool + "ocaml-lsp-server" will be re-locked and rebuilt with this version of the + compiler. + Solution for dev-tools.locks/ocaml-lsp-server: + - ocaml.5.1.0 + - ocaml-lsp-server.0.0.1 + Running 'ocamllsp' + hello from fake ocamllsp + $ cat dev-tools.locks/ocaml-lsp-server/ocaml.pkg + (version 5.1.0) diff --git a/test/blackbox-tests/test-cases/pkg/ocamllsp/dune b/test/blackbox-tests/test-cases/pkg/ocamllsp/dune new file mode 100644 index 00000000000..552dd6cf4a9 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/ocamllsp/dune @@ -0,0 +1,3 @@ +(cram + (deps helpers.sh) + (applies_to :whole_subtree)) diff --git a/test/blackbox-tests/test-cases/pkg/ocamllsp/helpers.sh b/test/blackbox-tests/test-cases/pkg/ocamllsp/helpers.sh new file mode 100644 index 00000000000..89a12023640 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/ocamllsp/helpers.sh @@ -0,0 +1,27 @@ +# Create a dune-workspace file with mock repos set up for the main +# project and the ocamllsp lockdir. +setup_ocamllsp_workspace() { + cat > dune-workspace < %{bin}%/ocamllsp" ] + [ "sh" "-c" "echo 'echo hello from fake ocamllsp' >> %{bin}%/ocamllsp" ] + [ "sh" "-c" "chmod a+x %{bin}%/ocamllsp" ] +] +EOF +}