From 565e0f4b4ab30130d1bb781ad9b02d637feb4d59 Mon Sep 17 00:00:00 2001 From: Ali Caglayan Date: Sun, 21 May 2023 02:52:42 +0200 Subject: [PATCH] feature: dune targets Add a dune targets command similar to ls that prints the available targets in a given directory. fix https://github.com/ocaml/dune/issues/265 Signed-off-by: Ali Caglayan --- CHANGES.md | 3 + bin/main.ml | 1 + bin/targets_cmd.ml | 69 +++++++++++++++++ bin/targets_cmd.mli | 5 ++ .../targets/dune-targets-simple.t/a.ml | 0 .../targets/dune-targets-simple.t/b/c.ml | 0 .../targets/dune-targets-simple.t/b/dune | 2 + .../targets/dune-targets-simple.t/dune | 10 +++ .../dune-targets-simple.t/dune-project | 2 + .../targets/dune-targets-simple.t/run.t | 75 +++++++++++++++++++ 10 files changed, 167 insertions(+) create mode 100644 bin/targets_cmd.ml create mode 100644 bin/targets_cmd.mli create mode 100644 test/blackbox-tests/test-cases/targets/dune-targets-simple.t/a.ml create mode 100644 test/blackbox-tests/test-cases/targets/dune-targets-simple.t/b/c.ml create mode 100644 test/blackbox-tests/test-cases/targets/dune-targets-simple.t/b/dune create mode 100644 test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune create mode 100644 test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune-project create mode 100644 test/blackbox-tests/test-cases/targets/dune-targets-simple.t/run.t diff --git a/CHANGES.md b/CHANGES.md index 3745e7ca72a6..1830a3ff3199 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ Unreleased ---------- +- Add a `dune targets` command similar to `ls` that displays all the available + targets in a given directory. (#7770, grants #265, @Alizter) + - Read `pkg-config` arguments from the `PKG_CONFIG_ARGN` environment variable (#1492, #7734, @anmonteiro) diff --git a/bin/main.ml b/bin/main.ml index a66ba690729c..0bcc4a9438d2 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -27,6 +27,7 @@ let all : _ Cmdliner.Cmd.t list = ; Ocaml_merlin.command ; Shutdown.command ; Diagnostics.command + ; Targets_cmd.command ] in let groups = diff --git a/bin/targets_cmd.ml b/bin/targets_cmd.ml new file mode 100644 index 000000000000..15a429d675b1 --- /dev/null +++ b/bin/targets_cmd.ml @@ -0,0 +1,69 @@ +open Import +open Stdune + +let doc = "Print available targets in a given directory. Works similalry to ls." + +let pp_all_direct_targets path = + let dir = Path.of_string path in + let root = + match (dir : Path.t) with + | External e -> + Code_error.raise "target_hint: external path" + [ ("path", Path.External.to_dyn e) ] + | In_source_tree d -> d + | In_build_dir d -> ( + match Path.Build.drop_build_context d with + | Some d -> d + | None -> Path.Source.root) + in + let open Action_builder.O in + let+ targets = + let open Memo.O in + Action_builder.of_memo + (Target.all_direct_targets (Some root) >>| Path.Build.Map.to_list) + in + let targets = + if Path.is_in_build_dir dir then + List.map ~f:(fun (path, k) -> (Path.build path, k)) targets + else + List.map targets ~f:(fun (path, k) -> + match Path.Build.extract_build_context path with + | None -> (Path.build path, k) + | Some (_, path) -> (Path.source path, k)) + in + let targets = + (* Only suggest hints for the basename, otherwise it's slow when there are + lots of files *) + List.filter_map targets ~f:(fun (path, kind) -> + if Path.equal (Path.parent_exn path) dir then + (* directory targets can be distinguied by the trailing path seperator *) + Some + (match kind with + | File -> Path.basename path + | Directory -> Path.basename path ^ Filename.dir_sep) + else None) + in + [ Pp.textf "%s:" (Path.to_string dir) + ; Pp.concat_map targets ~f:Pp.text ~sep:Pp.newline + ] + |> Pp.concat ~sep:Pp.newline + +let term = + let+ common = Common.term + and+ paths = Arg.(value & pos_all string [ "." ] & info [] ~docv:"DIR") in + let config = Common.init common in + let request _setup = + let open Action_builder.O in + let+ paragraphs = Action_builder.List.map paths ~f:pp_all_direct_targets in + paragraphs + |> Pp.concat ~sep:(Pp.seq Pp.newline Pp.newline) + |> List.singleton |> User_message.make |> User_message.print + in + Scheduler.go ~common ~config @@ fun () -> + let open Fiber.O in + let+ res = Build_cmd.run_build_system ~common ~request in + match res with + | Error `Already_reported -> raise Dune_util.Report_error.Already_reported + | Ok () -> () + +let command = Cmd.v (Cmd.info "targets" ~doc ~envs:Common.envs) term diff --git a/bin/targets_cmd.mli b/bin/targets_cmd.mli new file mode 100644 index 000000000000..f9b54f159167 --- /dev/null +++ b/bin/targets_cmd.mli @@ -0,0 +1,5 @@ +open Import + +(** The targets command lists all the targets available in the given directory, + defaulting to the current working direcctory. *) +val command : unit Cmd.t diff --git a/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/a.ml b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/a.ml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/b/c.ml b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/b/c.ml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/b/dune b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/b/dune new file mode 100644 index 000000000000..67231e9fe24b --- /dev/null +++ b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/b/dune @@ -0,0 +1,2 @@ +(library + (name simple2)) diff --git a/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune new file mode 100644 index 000000000000..81c6345a5334 --- /dev/null +++ b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune @@ -0,0 +1,10 @@ +(library + (name simple)) + +(rule + (targets + (dir d)) + (action + (progn + (run mkdir d) + (run cat > d/foo)))) diff --git a/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune-project b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune-project new file mode 100644 index 000000000000..c9932b31e04d --- /dev/null +++ b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/dune-project @@ -0,0 +1,2 @@ +(lang dune 3.8) +(using directory-targets 0.1) diff --git a/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/run.t b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/run.t new file mode 100644 index 000000000000..5ebbb9f25c20 --- /dev/null +++ b/test/blackbox-tests/test-cases/targets/dune-targets-simple.t/run.t @@ -0,0 +1,75 @@ +Testing the "dune targets" command in a simple OCaml project with an additional +directory target to see the behaviour there. + +We have two libraries with one in a subdirectory. We also have a directory +target d to see how the command will behave. + +With no directory provided to the command, it should default to the current +working directory. + + $ dune targets + .: + a.ml + d/ + dune + dune-project + simple.a + simple.cma + simple.cmxa + simple.cmxs + simple.ml-gen + +Multiple directories can be provided to the command. Also subdirectories may be +used, and only the targets available in that directory will be displayed. + + $ dune targets . b/ + .: + a.ml + d/ + dune + dune-project + simple.a + simple.cma + simple.cmxa + simple.cmxs + simple.ml-gen + + b: + c.ml + dune + simple2.a + simple2.cma + simple2.cmxa + simple2.cmxs + simple2.ml-gen + +The command also works with files in the _build directory. + + $ dune targets _build/default/ + _build/default: + a.ml + d/ + dune + dune-project + simple.a + simple.cma + simple.cmxa + simple.cmxs + simple.ml-gen + + $ dune targets _build/default/b + _build/default/b: + c.ml + dune + simple2.a + simple2.cma + simple2.cmxa + simple2.cmxs + simple2.ml-gen + +We cannot see inside directory targets + + $ dune targets d + d: + +