Skip to content

Commit bf8c0ff

Browse files
Rudi Hornjeremiedimino
authored andcommitted
Add dune compute to call internal functions
Signed-off-by: Rudi Horn <[email protected]> Signed-off-by: Jeremie Dimino <[email protected]>
1 parent a687489 commit bf8c0ff

File tree

11 files changed

+212
-60
lines changed

11 files changed

+212
-60
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ unreleased
3030
one from [this paper](https://doi.org/10.1145/2756553) (#1489,
3131
@rudihorn)
3232

33+
- Add `dune compute` to call internal memoized functions (#1528,
34+
@rudihorn, @diml)
35+
3336
1.6.2 (05/12/2018)
3437
------------------
3538

bin/main.ml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,71 @@ let external_lib_deps =
363363
in
364364
(term, Term.info "external-lib-deps" ~doc ~man)
365365

366+
let compute =
367+
let doc = "Compute internal function." in
368+
let man =
369+
[ `S "DESCRIPTION"
370+
; `P {|Run a registered memoize function with the given input and
371+
print the output. |}
372+
; `P {|This should only be used for debugging dune.|}
373+
; `Blocks Common.help_secs
374+
]
375+
in
376+
let term =
377+
let%map common = Common.term
378+
and fn =
379+
Arg.(required
380+
& pos 0 (some string) None
381+
& info [] ~docv:"FUNCTION"
382+
~doc:"Compute $(docv) for a given input.")
383+
and inp =
384+
Arg.(required
385+
& pos 1 (some string) None
386+
& info [] ~docv:"INPUT"
387+
~doc:"Use $(docv) as the input to the function.")
388+
in
389+
Common.set_common common ~targets:[];
390+
let log = Log.create common in
391+
let res =
392+
Scheduler.go ~log ~common
393+
(Main.setup ~log common ~external_lib_deps_mode:true
394+
>>= fun _setup ->
395+
let sexp =
396+
Dune_lang.parse_string
397+
~fname:"<command-line>"
398+
~mode:Dune_lang.Parser.Mode.Single inp
399+
in
400+
Memo.call fn sexp)
401+
in
402+
Format.printf "%a\n%!" Sexp.pp res
403+
in
404+
(term, Term.info "compute" ~doc ~man)
405+
406+
let list_functions =
407+
let doc = "List internal functions." in
408+
let man =
409+
[ `S "DESCRIPTION"
410+
; `P {|Print the list of internal functions that can be used with
411+
$(b,dune compute).|}
412+
; `Blocks Common.help_secs
413+
]
414+
in
415+
let term =
416+
let%map common = Common.term in
417+
Common.set_common common ~targets:[];
418+
let log = Log.create common in
419+
let _setup =
420+
Scheduler.go ~log ~common
421+
(Main.setup ~log common ~external_lib_deps_mode:true)
422+
in
423+
let fns = Memo.registered_functions () in
424+
let longest = String.longest_map fns ~f:(fun info -> info.name) in
425+
List.iter fns ~f:(fun { Memo.Function_info.name; doc } ->
426+
Printf.printf "%-*s : %s\n" longest name doc);
427+
flush stdout
428+
in
429+
(term, Term.info "list-functions" ~doc ~man)
430+
366431
let rules =
367432
let doc = "Dump internal rules." in
368433
let man =
@@ -1187,6 +1252,8 @@ let all =
11871252
; printenv
11881253
; Help.help
11891254
; fmt
1255+
; compute
1256+
; list_functions
11901257
]
11911258

11921259
let default =

doc/dune.inc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@
1717
(package dune)
1818
(files dune-clean.1))
1919

20+
(rule
21+
(with-stdout-to dune-compute.1
22+
(run dune compute --help=groff)))
23+
24+
(install
25+
(section man)
26+
(package dune)
27+
(files dune-compute.1))
28+
2029
(rule
2130
(with-stdout-to dune-exec.1
2231
(run dune exec --help=groff)))
@@ -62,6 +71,15 @@
6271
(package dune)
6372
(files dune-installed-libraries.1))
6473

74+
(rule
75+
(with-stdout-to dune-list-functions.1
76+
(run dune list-functions --help=groff)))
77+
78+
(install
79+
(section man)
80+
(package dune)
81+
(files dune-list-functions.1))
82+
6583
(rule
6684
(with-stdout-to dune-printenv.1
6785
(run dune printenv --help=groff)))

src/build_system.ml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ module Internal_rule = struct
8989

9090
let loc ~file_tree ~dir t = rule_loc ~file_tree ~dir ~loc:t.loc
9191

92+
let decode : t Dune_lang.Decoder.t =
93+
let open Dune_lang.Decoder in
94+
loc >>= fun loc ->
95+
Errors.fail loc "<not-implemented>"
96+
9297
let to_sexp t : Sexp.t =
9398
Sexp.Encoder.record
9499
[ "id", Id.to_sexp t.id
@@ -382,7 +387,10 @@ module Action_and_deps = struct
382387
end
383388

384389
module Rule_fn = Memo.Make(Internal_rule)
385-
module Path_fn = Memo.Make(Path)
390+
module Path_fn = Memo.Make(struct
391+
include Path
392+
let decode = Path_dune_lang.decode
393+
end)
386394

387395
type t =
388396
{ (* File specification by targets *)
@@ -1354,15 +1362,18 @@ let create ~contexts ~file_tree ~hook =
13541362
in
13551363
Fdecl.set t.prepare_rule_def
13561364
(Rule_fn.create "prepare-rule" (module Action_and_deps) (prepare_rule t)
1365+
~doc:"Evaluate the build arrow part of a rule."
13571366
|> Rule_fn.exec);
13581367
Fdecl.set t.build_rule_def
1359-
(Rule_fn.create "build-rule" (module Action_and_deps) (build_rule t));
1368+
(Rule_fn.create "build-rule" (module Action_and_deps) (build_rule t)
1369+
~doc:"Execute a rule.");
13601370
Fdecl.set t.build_rule_internal_def
13611371
(Rule_fn.create "build-rule-internal" (module Unit)
1362-
(build_rule_internal t)
1372+
(build_rule_internal t) ~doc:"-"
13631373
|> Rule_fn.exec);
13641374
Fdecl.set t.build_file_def
1365-
(Path_fn.create "build-file" (module Unit) (build_file t));
1375+
(Path_fn.create "build-file" (module Unit) (build_file t)
1376+
~doc:"Build a file.");
13661377
Hooks.End_of_build.once (fun () -> finalize t);
13671378
t
13681379

src/memo/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
(library
22
(name memo)
3-
(libraries stdune dag fiber)
3+
(libraries stdune dune_lang dag fiber)
44
(synopsis "Function memoizer"))

src/memo/memo.ml

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
open !Stdune
22
open Fiber.O
33

4-
module type Data = Memo_intf.Data
4+
module type Input = Memo_intf.Input
5+
module type Output = Memo_intf.Output
56

67
module Function_name = Interned.Make(struct
78
let initial_size = 1024
@@ -15,10 +16,22 @@ module Spec = struct
1516
type ('a, 'b) t =
1617
{ name : Function_name.t
1718
; allow_cutoff : bool
18-
; input : (module Data with type t = 'a)
19-
; output : (module Data with type t = 'b)
19+
; input : (module Input with type t = 'a)
20+
; output : (module Output with type t = 'b)
2021
; witness : 'a witness
22+
; f : 'a -> 'b Fiber.t
23+
; doc : string
2124
}
25+
26+
type packed = T : (_, _) t -> packed [@@unboxed]
27+
28+
let by_name = Function_name.Table.create ~default_value:None
29+
30+
let register t =
31+
Function_name.Table.set by_name ~key:t.name ~data:(Some (T t))
32+
33+
let find name =
34+
Function_name.Table.get by_name name
2235
end
2336

2437
module Id = Id.Make()
@@ -104,7 +117,7 @@ module Cached_value = struct
104117

105118
let dep_changed (type a) (node : (_, a) Dep_node.t) prev_output curr_output =
106119
if node.spec.allow_cutoff then
107-
let (module Output : Data with type t = a) = node.spec.output in
120+
let (module Output : Output with type t = a) = node.spec.output in
108121
not (Output.equal prev_output curr_output)
109122
else
110123
true
@@ -154,7 +167,7 @@ module Cached_value = struct
154167
end
155168

156169
let ser_input (type a) (node : (a, _) Dep_node.t) =
157-
let (module Input : Data with type t = a) = node.spec.input in
170+
let (module Input : Input with type t = a) = node.spec.input in
158171
Input.to_sexp node.input
159172

160173
let dag_node (dep_node : _ Dep_node.t) = Lazy.force dep_node.dag_node
@@ -209,13 +222,12 @@ let dump_stack v =
209222
)
210223
>>| (fun _ -> v)
211224

212-
module Make(Input : Data) : S with type input := Input.t = struct
225+
module Make(Input : Input) : S with type input := Input.t = struct
213226
module Table = Hashtbl.Make(Input)
214227

215228
type 'a t =
216229
{ spec : (Input.t, 'a) Spec.t
217230
; cache : (Input.t, 'a) Dep_node.t Table.t
218-
; f : Input.t -> 'a Fiber.t
219231
}
220232

221233
type _ Spec.witness += W : Input.t Spec.witness
@@ -242,17 +254,27 @@ module Make(Input : Data) : S with type input := Input.t = struct
242254
Some (List.map cv.deps ~f:(fun (Last_dep.T (n,_u)) ->
243255
(Function_name.to_string n.spec.name, ser_input n)))
244256

245-
let create name ?(allow_cutoff=true) output f =
257+
let create name ?(allow_cutoff=true) ~doc output f =
246258
let name = Function_name.make name in
259+
let spec =
260+
{ Spec.
261+
name
262+
; input = (module Input); output
263+
; allow_cutoff
264+
; witness = W
265+
; f
266+
; doc
267+
}
268+
in
269+
Spec.register spec;
247270
{ cache = Table.create 1024
248-
; spec = { name; input = (module Input); output; allow_cutoff; witness = W }
249-
; f
271+
; spec
250272
}
251273

252274
let compute t inp ivar dep_node =
253275
(* define the function to update / double check intermediate result *)
254276
(* set context of computation then run it *)
255-
push_stack_frame (T dep_node) (t.f inp) >>= fun res ->
277+
push_stack_frame (T dep_node) (t.spec.f inp) >>= fun res ->
256278
(* update the output cache with the correct value *)
257279
let deps =
258280
Dag.children (dag_node dep_node)
@@ -336,3 +358,35 @@ module Make(Input : Data) : S with type input := Input.t = struct
336358
dep_node.spec.name = of_.spec.name
337359
end
338360
end
361+
362+
let call name input =
363+
match
364+
let open Option.O in
365+
Function_name.get name >>= Spec.find
366+
with
367+
| None -> Exn.fatalf "@{<error>Error@}: function %s doesn't exist!" name
368+
| Some (Spec.T spec) ->
369+
let (module Input : Input with type t = _) = spec.input in
370+
let (module Output : Output with type t = _) = spec.output in
371+
let input = Dune_lang.Decoder.parse Input.decode Univ_map.empty input in
372+
spec.f input >>| fun output ->
373+
Output.to_sexp output
374+
375+
module Function_info = struct
376+
type t =
377+
{ name : string
378+
; doc : string
379+
}
380+
381+
let of_spec (Spec.T spec) =
382+
{ name = Function_name.to_string spec.name
383+
; doc = spec.doc
384+
}
385+
end
386+
387+
let registered_functions () =
388+
Function_name.all ()
389+
|> List.filter_map ~f:(Function_name.Table.get Spec.by_name)
390+
|> List.map ~f:Function_info.of_spec
391+
|> List.sort ~compare:(fun a b ->
392+
String.compare a.Function_info.name b.Function_info.name)

src/memo/memo.mli

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ end
2828
val reset : unit -> unit
2929

3030
module type S = Memo_intf.S with type stack_frame := Stack_frame.t
31-
module type Data = Memo_intf.Data
31+
module type Input = Memo_intf.Input
32+
module type Output = Memo_intf.Output
3233

33-
module Make(Input : Data) : S with type input := Input.t
34+
module Make(Input : Input) : S with type input := Input.t
3435

3536
(** Print the memoized call stack during execution. This is useful for debugging purposes.
3637
Example code:
@@ -45,3 +46,16 @@ val dump_stack : 'a -> 'a Fiber.t
4546

4647
(** Get the memoized call stack during the execution of a memoized function. *)
4748
val get_call_stack : Stack_frame.t list Fiber.t
49+
50+
(** Call a memoized function by name *)
51+
val call : string -> Dune_lang.Ast.t -> Sexp.t Fiber.t
52+
53+
module Function_info : sig
54+
type t =
55+
{ name : string
56+
; doc : string
57+
}
58+
end
59+
60+
(** Return the list of registered functions *)
61+
val registered_functions : unit -> Function_info.t list

src/memo/memo_intf.ml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ module type Data = sig
77
val to_sexp : t -> Sexp.t
88
end
99

10+
module type Input = sig
11+
include Data
12+
val decode : t Dune_lang.Decoder.t
13+
end
14+
15+
module type Output = Data
16+
1017
module type S = sig
1118
type input
1219

@@ -27,7 +34,8 @@ module type S = sig
2734
val create
2835
: string
2936
-> ?allow_cutoff:bool
30-
-> (module Data with type t = 'a)
37+
-> doc:string
38+
-> (module Output with type t = 'a)
3139
-> (input -> 'a Fiber.t)
3240
-> 'a t
3341

src/stdune/interned.ml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module type S = sig
55
val pp: t Fmt.t
66
val make : string -> t
77
val get : string -> t option
8+
val all : unit -> t list
89
module Set : sig
910
include Set.S with type elt = t
1011
val make : string list -> t
@@ -89,6 +90,8 @@ module Make(R : Settings)()
8990

9091
let to_string t = Table.get names t
9192

93+
let all () = List.init ~len:!next ~f:(fun t -> t)
94+
9295
module T = struct
9396
type nonrec t = int
9497

@@ -118,10 +121,12 @@ module No_interning(R : Settings)() = struct
118121
type t = string
119122

120123
let compare = String.compare
121-
let make s = s
124+
let all = ref String.Set.empty
125+
let make s = all := String.Set.add !all s; s
122126
let to_string s = s
123127
let pp fmt s = Format.fprintf fmt "%S" (to_string s)
124128
let get s = Some s
129+
let all () = String.Set.to_list !all
125130

126131
module Set = struct
127132
include String.Set

0 commit comments

Comments
 (0)