diff --git a/bin/describe/aliases_targets.ml b/bin/describe/aliases_targets.ml index 976c0f4a77e..71c2db340cc 100644 --- a/bin/describe/aliases_targets.ml +++ b/bin/describe/aliases_targets.ml @@ -1,13 +1,13 @@ open Import -let ls_term (fetch_results : Path.Build.t -> string list Action_builder.t) = +let ls_term_gen extra_args fetch_results = let+ builder = Common.Builder.term (* CR-someday Alizter: document this option *) and+ paths = Arg.(value & pos_all string [ "." ] & info [] ~docv:"DIR" ~doc:None) and+ context = Common.context_arg ~doc:(Some "The context to look in. Defaults to the default context.") - in + and+ extra = extra_args in let common, config = Common.init builder in let request (_ : Dune_rules.Main.build_system) = let header = List.length paths > 1 in @@ -54,28 +54,46 @@ let ls_term (fetch_results : Path.Build.t -> string list Action_builder.t) = Action_builder.of_memo @@ let open Memo.O in - Source_tree.find_dir src_dir + (* First check if it's a directory target *) + Load_rules.load_dir ~dir:(Path.build build_dir) >>= function - | Some _ -> Memo.return () - | None -> - (* The directory didn't exist. We therefore check if it was a - directory target and error for the user accordingly. *) - let+ is_dir_target = - Load_rules.is_under_directory_target (Path.build build_dir) - in - if is_dir_target - then - User_error.raise - [ Pp.textf - "Directory %s is a directory target. This command does not support \ - the inspection of directory targets." - (Path.to_string dir) - ] - else - User_error.raise - [ Pp.textf "Directory %s does not exist." (Path.to_string dir) ] + | Load_rules.Loaded.Build_under_directory_target _ -> + User_error.raise + [ Pp.textf + "Directory %s is a directory target. This command does not support the \ + inspection of directory targets." + (Path.to_string dir) + ] + | External _ | Build _ | Source _ -> + (* Check if directory exists in source tree or is a valid build-only directory *) + Source_tree.find_dir src_dir + >>= (function + | Some _ -> Memo.return () (* Exists in source tree *) + | None -> + (* Not in source tree, check if it's a valid build-only subdirectory *) + (match Path.Build.parent build_dir with + | None -> + (* Build context root always exists *) + Memo.return () + | Some parent_dir -> + Load_rules.load_dir ~dir:(Path.build parent_dir) + >>| (function + | Load_rules.Loaded.Build { allowed_subdirs; _ } -> + let subdir_set = + Dune_engine.Dir_set.descend + allowed_subdirs + (Path.Build.basename build_dir) + in + if Dune_engine.Dir_set.here subdir_set + then () + else + User_error.raise + [ Pp.textf "Directory %s does not exist." (Path.to_string dir) ] + | _ -> + User_error.raise + [ Pp.textf "Directory %s does not exist." (Path.to_string dir) ]))) in - let+ targets = fetch_results build_dir in + let+ targets = fetch_results extra build_dir in (* If we are printing multiple directories, we print the directory name as a header. *) (if header then [ Pp.textf "%s:" (Path.to_string dir) ] else []) @@ -92,7 +110,7 @@ let ls_term (fetch_results : Path.Build.t -> string list Action_builder.t) = ;; module Aliases_cmd = struct - let fetch_results (dir : Path.Build.t) = + let fetch_results () (dir : Path.Build.t) = let open Action_builder.O in let+ alias_targets = let+ load_dir = @@ -105,7 +123,7 @@ module Aliases_cmd = struct List.map ~f:Dune_engine.Alias.Name.to_string alias_targets ;; - let term = ls_term fetch_results + let term = ls_term_gen (Term.const ()) fetch_results let command = let doc = "Print aliases in a given directory. Works similarly to ls." in @@ -114,28 +132,48 @@ module Aliases_cmd = struct end module Targets_cmd = struct - let fetch_results (dir : Path.Build.t) = + let fetch_results all (dir : Path.Build.t) = let open Action_builder.O in - let+ targets = - let open Memo.O in - Target.all_direct_targets (Some (Path.Build.drop_build_context_exn dir)) - >>| Path.Build.Map.to_list - |> Action_builder.of_memo - in - List.filter_map targets ~f:(fun (path, kind) -> - match Path.Build.equal (Path.Build.parent_exn path) dir with - | false -> None - | true -> - (* directory targets can be distinguied by the trailing path separator - *) - Some - (match kind with - | Target.File -> Path.Build.basename path |> Filename.to_string - | Directory -> - (Path.Build.basename path |> Filename.to_string) ^ Filename.dir_sep)) + let+ load_dir = Action_builder.of_memo (Load_rules.load_dir ~dir:(Path.build dir)) in + match load_dir with + | Load_rules.Loaded.Build { rules_here; allowed_subdirs; _ } -> + let file_targets = + Path.Build.Map.keys rules_here.by_file_targets + |> List.filter_map ~f:(fun path -> + if Path.Build.equal (Path.Build.parent_exn path) dir + then Some (Path.Build.basename path |> Filename.to_string) + else None) + in + let dir_targets = + Path.Build.Map.keys rules_here.by_directory_targets + |> List.filter_map ~f:(fun path -> + if Path.Build.equal (Path.Build.parent_exn path) dir + then Some ((Path.Build.basename path |> Filename.to_string) ^ Filename.dir_sep) + else None) + in + let subdirs = + match Dune_engine.Dir_set.toplevel_subdirs allowed_subdirs with + | Infinite -> [] + | Finite set -> + Filename.Set.to_list set + |> Filename.L.to_string + |> List.filter ~f:(fun name -> all || String.length name = 0 || name.[0] <> '.') + |> List.map ~f:(fun name -> name ^ Filename.dir_sep) + in + List.sort ~compare:String.compare (file_targets @ dir_targets @ subdirs) + | _ -> [] + ;; + + let extra_args = + Arg.( + value + & flag + & info + [ "a"; "all" ] + ~doc:(Some "Show hidden directories (those starting with '.').")) ;; - let term = ls_term fetch_results + let term = ls_term_gen extra_args fetch_results let command = let doc = "Print targets in a given directory. Works similarly to ls." in diff --git a/doc/changes/changed/14665.md b/doc/changes/changed/14665.md new file mode 100644 index 00000000000..52ba9cb042e --- /dev/null +++ b/doc/changes/changed/14665.md @@ -0,0 +1,3 @@ +- `dune show targets` now includes subdirectories that contain build targets, + and accepts an `-a`/`--all` flag to also include hidden directories (those + starting with `.`). (#14665, fixes #13783, @Alizter) diff --git a/doc/changes/fixed/14664.md b/doc/changes/fixed/14664.md new file mode 100644 index 00000000000..c4f66283751 --- /dev/null +++ b/doc/changes/fixed/14664.md @@ -0,0 +1,2 @@ +- `dune show targets` now accepts build-only directories such as + `_build/default/.simple.objs`. (#14664, fixes #13782, @Alizter) diff --git a/doc/reference/cli.rst b/doc/reference/cli.rst index 3e9aca7f7f7..e3fd1942059 100644 --- a/doc/reference/cli.rst +++ b/doc/reference/cli.rst @@ -71,7 +71,11 @@ documentation for each command is available through ``dune COMMAND --help``. .. describe:: dune describe targets - Print targets in a given directory. Works similarly to ls. + Print targets in a given directory. Works similarly to ls. The directory + may be a path in the source tree, or a build-only directory under + ``_build/`` (such as ``_build/default/.lib.objs``). Subdirectories + containing build targets are listed alongside the targets themselves; + pass ``-a``/``--all`` to also include hidden directories. .. describe:: dune describe workspace diff --git a/test/blackbox-tests/test-cases/describe/targets.t/run.t b/test/blackbox-tests/test-cases/describe/targets.t/run.t index e7f701a39fd..0a86df0d807 100644 --- a/test/blackbox-tests/test-cases/describe/targets.t/run.t +++ b/test/blackbox-tests/test-cases/describe/targets.t/run.t @@ -8,7 +8,10 @@ With no directory provided to the command, it should default to the current working directory. $ dune show targets + _doc/ + _doc_new/ a.ml + b/ d/ dune dune-project @@ -23,7 +26,10 @@ used, and only the targets available in that directory will be displayed. $ dune show targets . b/ .: + _doc/ + _doc_new/ a.ml + b/ d/ dune dune-project @@ -45,7 +51,10 @@ used, and only the targets available in that directory will be displayed. The command also works with files in the _build directory. $ dune show targets _build/default/ + _doc/ + _doc_new/ a.ml + b/ d/ dune dune-project @@ -70,15 +79,31 @@ We cannot see inside directory targets Error: Directory d is a directory target. This command does not support the inspection of directory targets. -We cannot show targets in build-only directories that don't exist in the source -tree, like .simple.objs: +Build-only directories that don't exist in the source tree, like .simple.objs +(Dune's internal object directory for the `simple` library), can now be +queried: $ dune show targets .simple.objs - Error: Directory .simple.objs does not exist. + byte/ + cctx.ocaml-index + jsoo/ + native/ -CR-soon Alizter: This should work $ dune show targets _build/default/.simple.objs - Error: Directory _build/default/.simple.objs does not exist. + byte/ + cctx.ocaml-index + jsoo/ + native/ + +With --all, hidden directories are also shown: + + $ dune show targets --all .simple.objs + .bin/ + .utop/ + byte/ + cctx.ocaml-index + jsoo/ + native/ And we error on non-existent directories