Skip to content

Commit 569b03d

Browse files
committed
named targets
Signed-off-by: Anthony Onah <[email protected]>
1 parent e6cba77 commit 569b03d

File tree

6 files changed

+204
-23
lines changed

6 files changed

+204
-23
lines changed

src/dune_lang/action.ml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
open Stdune
22
open Dune_sexp
33
open Dune_util.Action
4+
open Pform.Macro
5+
6+
module Named_targets = struct
7+
type t = (string * String_with_vars.t) list
8+
end
49

510
module Action_plugin = struct
611
let syntax =
@@ -12,6 +17,55 @@ module Action_plugin = struct
1217
;;
1318
end
1419

20+
module Pform = struct
21+
module Var = struct
22+
type t =
23+
| Values
24+
| Loc
25+
| C_flags
26+
| Cxx_flags
27+
| Cpp_flags
28+
| Target of string (* New variant for named targets *)
29+
| Ocaml
30+
| Ocamlc
31+
| Ocamlopt
32+
| Ocamldep
33+
| Ocamlmklib
34+
| Dev_null
35+
| Null
36+
| Ext_obj
37+
| Ext_asm
38+
| Ext_lib
39+
| Ext_dll
40+
| Ext_exe
41+
| Ext_plugin
42+
| Profile
43+
| Context_name
44+
| Os_type
45+
| Architecture
46+
| System
47+
| Model
48+
| Ignoring_promoted_rules
49+
| Project_root
50+
| Workspace_root
51+
| Build_context
52+
| First_dep
53+
| Input_file
54+
| Library_name
55+
| Partition
56+
| Impl_files
57+
| Intf_files
58+
| Test
59+
| Corrected_suffix
60+
| Inline_tests
61+
| Toolchain
62+
63+
let compare = compare
64+
end
65+
type t =
66+
| Var of Var.t
67+
end
68+
1569
module Diff = struct
1670
include Diff
1771

@@ -178,14 +232,63 @@ type t =
178232
| When of Slang.blang * t
179233
| Format_dune_file of String_with_vars.t * String_with_vars.t
180234

235+
type expansion_context = {
236+
dir : Path.t;
237+
(* ... other fields ... *)
238+
named_targets : Named_targets.t;
239+
}
181240
let is_dev_null t = String_with_vars.is_pform t (Var Dev_null)
182241

183242
let translate_to_ignore fn output action =
184243
if is_dev_null fn
185244
then Ignore (output, action)
186245
else Redirect_out (output, fn, Normal, action)
187246
;;
247+
let expand_target_var ~loc ~named_targets name =
248+
match List.assoc_opt name named_targets with
249+
| Some target ->
250+
(match String_with_vars.text_only target with
251+
| Some text -> text
252+
| None ->
253+
User_error.raise ~loc [
254+
Pp.textf "Named target '%s' contains variables and cannot be expanded here" name
255+
])
256+
| None ->
257+
User_error.raise ~loc [
258+
Pp.textf "Undefined named target: %s" name;
259+
Pp.text "Available named targets:";
260+
Pp.enumerate named_targets ~f:(fun (name, _) -> Pp.text name)
261+
]
262+
;;
263+
let expand_str ~context sw =
264+
let module S = String_with_vars in
265+
266+
match S.text_only sw with
267+
| Some s -> s
268+
| None ->
269+
let expand_var = function
270+
| Pform.Var var ->
271+
if String.equal (Pform.Var.to_string var) "target" then
272+
(match context.named_targets with
273+
| (name, target) :: _ -> S.to_string target
274+
| [] -> "%{target}")
275+
else
276+
S.to_string (S.make_var (S.loc sw) (Pform.Var var))
277+
in
278+
S.expand sw ~f:expand_var
279+
;;
188280

281+
let create_action targets action =
282+
let named_targets =
283+
List.filter_map targets ~f:(fun (target, _, name) ->
284+
Option.map name ~f:(fun n -> (n, target)))
285+
in
286+
{ action with
287+
expansion_context = {
288+
dir = Path.root;
289+
named_targets
290+
}
291+
}
189292
let two_or_more decode =
190293
let open Decoder in
191294
let+ n1 = decode
@@ -255,6 +358,19 @@ let decode_with_accepted_exit_codes =
255358
]
256359
;;
257360

361+
let decode_rule =
362+
let open Decoder in
363+
let* targets =
364+
repeat (
365+
let* target = String_with_vars.decode in
366+
let* name = optional string in
367+
return (target, (), name)
368+
)
369+
in
370+
let* action = decode_dune_file in
371+
return (create_action targets action)
372+
;;
373+
258374
let sw = String_with_vars.decode
259375

260376
let cstrs_dune_file t =

src/dune_lang/targets_spec.ml

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
open Stdune
22

3+
4+
module Hashtbl = Stdlib.Hashtbl
5+
36
module Multiplicity = struct
47
type t =
58
| One
@@ -33,43 +36,53 @@ module Kind = struct
3336
end
3437

3538
module Static = struct
36-
type 'path t =
37-
{ targets : ('path * Kind.t) list
38-
; multiplicity : Multiplicity.t
39-
}
39+
type 'path t = {
40+
targets : ('path * Kind.t * string option) list;
41+
multiplicity : Multiplicity.t;
42+
}
43+
4044
end
4145

4246
type 'a t =
4347
| Static of 'a Static.t
4448
| Infer
4549

50+
let named_targets : (string, String_with_vars.t) Hashtbl.t = Hashtbl.create 16
4651
let decode_target ~allow_directory_targets =
4752
let open Dune_sexp.Decoder in
48-
let file =
49-
let+ file = String_with_vars.decode in
50-
file, Kind.File
53+
let base_target =
54+
let file =
55+
let+ file = String_with_vars.decode in
56+
(file, Kind.File, None)
57+
in
58+
let dir =
59+
let+ dir = sum ~force_parens:true [ "dir", String_with_vars.decode ] in
60+
if not allow_directory_targets then
61+
User_error.raise ~loc:(String_with_vars.loc dir)
62+
[ Pp.text "Directory targets require extension" ];
63+
(dir, Kind.Directory, None)
64+
in
65+
file <|> dir
5166
in
52-
let dir =
53-
let+ dir = sum ~force_parens:true [ "dir", String_with_vars.decode ] in
54-
if not allow_directory_targets
55-
then
56-
User_error.raise
57-
~loc:(String_with_vars.loc dir)
58-
[ Pp.text "Directory targets require the 'directory-targets' extension" ];
59-
dir, Kind.Directory
67+
68+
let named_target =
69+
let+ name, (target, kind, _) =
70+
sum ~force_parens:true [ "named", pair string base_target ] in
71+
Hashtbl.add named_targets name target;
72+
(target, kind, Some name)
6073
in
61-
file <|> dir
74+
75+
named_target <|> base_target
6276
;;
6377

78+
(* src/dune_lang/targets_spec.ml *)
6479
let decode_static ~allow_directory_targets =
6580
let open Dune_sexp.Decoder in
6681
let+ syntax_version = Dune_sexp.Syntax.get_exn Stanza.syntax
6782
and+ targets = repeat (decode_target ~allow_directory_targets) in
68-
if syntax_version < (1, 3)
69-
then
70-
List.iter targets ~f:(fun (target, (_ : Kind.t)) ->
71-
if String_with_vars.has_pforms target
72-
then
83+
if syntax_version < (1, 3) then
84+
List.iter targets ~f:(fun (target, (_ : Kind.t), _) ->
85+
if String_with_vars.has_pforms target then
7386
Dune_sexp.Syntax.Error.since
7487
(String_with_vars.loc target)
7588
Stanza.syntax
@@ -78,13 +91,15 @@ let decode_static ~allow_directory_targets =
7891
Static { targets; multiplicity = Multiple }
7992
;;
8093

94+
8195
let decode_one_static ~allow_directory_targets =
8296
let open Dune_sexp.Decoder in
8397
let+ () = Dune_sexp.Syntax.since Stanza.syntax (1, 11)
8498
and+ target = decode_target ~allow_directory_targets in
8599
Static { targets = [ target ]; multiplicity = One }
86100
;;
87101

102+
88103
let field ~allow_directory_targets =
89104
let open Dune_sexp.Decoder in
90105
fields_mutually_exclusive
@@ -93,3 +108,6 @@ let field ~allow_directory_targets =
93108
; "targets", decode_static ~allow_directory_targets
94109
]
95110
;;
111+
112+
let get_target_by_name name =
113+
Hashtbl.find_opt named_targets name

src/dune_lang/targets_spec.mli

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ end
1818

1919
module Static : sig
2020
type 'path t =
21-
{ targets : ('path * Kind.t) list
22-
; multiplicity : Multiplicity.t
21+
{ targets : ('path * Kind.t * string option) list;
22+
multiplicity : Multiplicity.t
2323
}
2424
end
2525

@@ -34,3 +34,5 @@ type 'a t =
3434
val field
3535
: allow_directory_targets:bool
3636
-> String_with_vars.t t Dune_sexp.Decoder.fields_parser
37+
38+
val get_target_by_name : string -> String_with_vars.t option

test/blackbox-tests/test-cases/dune

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,7 @@
174174
(applies_to hidden-deps-unsupported)
175175
(enabled_if
176176
(< %{ocaml_version} 5.2.0)))
177+
178+
(rule
179+
(alias named-targets)
180+
(action (run %{bin:bash} ./named-targets.t)))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Setup test environment
5+
WORKDIR=$(mktemp -d)
6+
trap 'rm -rf "$WORKDIR"' EXIT
7+
cd "$WORKDIR"
8+
9+
# Create test files
10+
cat > dune-project <<'EOF'
11+
(lang dune 3.0)
12+
EOF
13+
14+
cat > dune <<'EOF'
15+
(rule
16+
(targets output.txt secondary.log)
17+
(action
18+
(progn
19+
(write-file output.txt "Primary content")
20+
(write-file secondary.log "Log content"))))
21+
22+
(rule
23+
(alias runtest)
24+
(action
25+
(progn
26+
(diff output.txt output.expected)
27+
(diff secondary.log secondary.expected))))
28+
EOF
29+
30+
# Create expected files - MUST match exactly what's generated
31+
printf "Primary content" > output.expected
32+
printf "Log content" > secondary.expected
33+
34+
# Run the test
35+
dune build @runtest

test/dune

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
;; $ ./_build/default/bin/main.exe build @test/fail-with-background-jobs-running
44
;;
55

6+
(include_subdirs no) ; Disable automatic inclusion
7+
68
(rule
79
(alias sleep5)
810
(action
@@ -157,3 +159,7 @@
157159
(action
158160
(run ./incr.exe y %{targets}))
159161
(locks m))
162+
163+
(rule
164+
(alias runtest)
165+
(action (diff output.txt output.expected)))

0 commit comments

Comments
 (0)