Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 81 additions & 43 deletions bin/describe/aliases_targets.ml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -49,33 +49,47 @@ let ls_term (fetch_results : Path.Build.t -> string list Action_builder.t) =
(Path.to_string_maybe_quoted dir)
]
in
(* Check if the directory exists. *)
(* Check if the directory exists (in source tree, as a directory target,
or as a build-only directory). *)
let* () =
Action_builder.of_memo
@@
let open Memo.O in
Source_tree.find_dir src_dir
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 _ ->
(* Directory target - allow it through for inspection *)
Memo.return ()
| 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 [])
Expand All @@ -92,7 +106,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 =
Expand All @@ -105,7 +119,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
Expand All @@ -114,28 +128,52 @@ 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
let* targets =
Action_builder.of_memo (Build_system.targets_of ~dir:(Path.build dir))
in
let+ subdirs =
Action_builder.of_memo
(let open Memo.O in
Load_rules.load_dir ~dir:(Path.build dir)
>>| function
| Load_rules.Loaded.Build { allowed_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.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 target_names =
match Dune_targets.validate targets with
| Valid validated ->
Dune_targets.Validated.fold
validated
~init:[]
~file:(fun p acc -> (Path.Build.basename p |> Filename.to_string) :: acc)
~dir:(fun p acc ->
((Path.Build.basename p |> Filename.to_string) ^ Filename.dir_sep) :: acc)
| No_targets -> []
| Inconsistent_parent_dir | File_and_directory_target_with_the_same_name _ -> []
in
List.sort ~compare:String.compare (target_names @ 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
Expand Down
3 changes: 3 additions & 0 deletions doc/changes/changed/14665.md
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 2 additions & 0 deletions doc/changes/changed/14666.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `dune show targets` now looks inside directory targets, so users can inspect
the files produced under a directory target. (#14666, fixes #13780, @Alizter)
2 changes: 2 additions & 0 deletions doc/changes/fixed/14664.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `dune show targets` now accepts build-only directories such as
`_build/default/.simple.objs`. (#14664, fixes #13782, @Alizter)
7 changes: 6 additions & 1 deletion doc/reference/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ 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, a build-only directory under
``_build/`` (such as ``_build/default/.lib.objs``), or a directory
target (including subdirectories of one). Subdirectories containing
build targets are listed alongside the targets themselves; pass
``-a``/``--all`` to also include hidden directories.

.. describe:: dune describe workspace

Expand Down
51 changes: 51 additions & 0 deletions src/dune_engine/build_system.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,57 @@ let files_of ~dir =
Filename_set.create ~dir filenames
;;

let targets_of ~dir =
Load_rules.load_dir ~dir
>>= function
| Source _ | External _ ->
(* Source/external dirs have no build targets *)
Memo.return Targets.empty
| Build { rules_here; _ } ->
let files =
Path.Build.Map.keys rules_here.by_file_targets |> Path.Build.Set.of_list
in
let dirs =
Path.Build.Map.keys rules_here.by_directory_targets |> Path.Build.Set.of_list
in
Memo.return (Targets.create ~files ~dirs)
| Build_under_directory_target { directory_target_ancestor } ->
let+ produced = build_dir (Path.build directory_target_ancestor) in
let build_dir_path = Path.as_in_build_dir_exn dir in
(* Navigate to the requested directory within the produced targets *)
let rec find_dir_contents (contents : _ Targets.Produced.dir_contents) path =
match path with
| [] -> Some contents
| component :: rest ->
(match Filename.Map.find contents.subdirs component with
| Some sub -> find_dir_contents sub rest
| None -> None)
in
let path_within =
match
Path.Local.descendant
(Path.Build.local build_dir_path)
~of_:(Path.Build.local produced.root)
with
| Some p -> Path.Local.explode p
| None -> []
in
(match find_dir_contents produced.contents path_within with
| Some contents ->
let files =
Filename.Map.keys contents.files
|> List.map ~f:(Path.Build.relative_fname build_dir_path)
|> Path.Build.Set.of_list
in
let dirs =
Filename.Map.keys contents.subdirs
|> List.map ~f:(Path.Build.relative_fname build_dir_path)
|> Path.Build.Set.of_list
in
Targets.create ~files ~dirs
| None -> Targets.empty)
;;

let caused_by_cancellation (exn : Exn_with_backtrace.t) =
match exn.exn with
| Scheduler.Run.Build_cancelled -> true
Expand Down
4 changes: 4 additions & 0 deletions src/dune_engine/build_system.mli
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ val eval_pred : File_selector.t -> Filename_set.t Memo.t
(** Same as [eval_pred] with [Predicate.true_] as predicate. *)
val files_of : dir:Path.t -> Filename_set.t Memo.t

(** Return all targets (files and directories) in a directory.
Handles directory targets by building them first. *)
val targets_of : dir:Path.t -> Targets.t Memo.t

(** Execute an action. The execution is cached. *)
val execute_action : observing_facts:Dep.Facts.t -> Rule.Anonymous_action.t -> unit Memo.t

Expand Down
4 changes: 2 additions & 2 deletions test/blackbox-tests/test-cases/describe/targets.t/dune
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
(targets (dir d))
(action
(progn
(run mkdir d)
(run cat > d/foo))))
(run mkdir -p d/subdir)
(run touch d/foo d/bar d/subdir/nested))))
58 changes: 50 additions & 8 deletions test/blackbox-tests/test-cases/describe/targets.t/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -64,21 +73,54 @@ The command also works with files in the _build directory.
simple2.cmxs
simple2.ml-gen

We cannot see inside directory targets
We can see inside directory targets. The directory target `d` contains files
and a subdirectory.

$ dune show targets d
Error: Directory d is a directory target. This command does not support the
inspection of directory targets.
bar
foo
subdir/

We cannot show targets in build-only directories that don't exist in the source
tree, like .simple.objs:
We can also inspect directory targets using the _build path:

$ dune show targets _build/default/d
bar
foo
subdir/

We can also look inside subdirectories of directory targets:

$ dune show targets d/subdir
nested

$ dune show targets _build/default/d/subdir
nested

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

Expand Down
Loading