diff --git a/src/dune_engine/action_builder.ml b/src/dune_engine/action_builder.ml index 0ee619f4394e..5a72b2b96da4 100644 --- a/src/dune_engine/action_builder.ml +++ b/src/dune_engine/action_builder.ml @@ -1,77 +1,38 @@ open Import include Action_builder0 -open O open struct module List = Stdune.List end -let register_action_deps : type a. a eval_mode -> Dep.Set.t -> a Dep.Map.t Memo.t = +let record_action_deps : type m. m eval_mode -> Dep.Set.t -> m Memo.t = fun mode deps -> match mode with | Eager -> Build_system.build_deps deps | Lazy -> Memo.return deps ;; -let dyn_memo_deps deps = +let record (deps : Dep.Set.t) = of_thunk { f = (fun mode -> - let open Memo.O in - let* deps, paths = deps in - let+ deps = register_action_deps mode deps in - paths, deps) + let open Memo.O in + let+ deps = record_action_deps mode deps in + (), deps) } ;; -let deps d = dyn_memo_deps (Memo.return (d, ())) -let dep d = deps (Dep.Set.singleton d) - -let dyn_deps t = - of_thunk - { f = - (fun mode -> - let open Memo.O in - let* (x, deps), deps_x = run t mode in - let+ deps = register_action_deps mode deps in - x, Deps_or_facts.union mode deps deps_x) - } -;; - -let path p = deps (Dep.Set.singleton (Dep.file p)) -let paths ps = deps (Dep.Set.of_files ps) -let path_set ps = deps (Dep.Set.of_files_set ps) - -let paths_matching - : type a. File_selector.t -> a eval_mode -> (Path.Set.t * a Dep.Map.t) Memo.t - = +let paths_matching : type m. File_selector.t -> m eval_mode -> (unit * m) Memo.t = fun g mode -> let open Memo.O in match mode with | Eager -> - let+ files = Build_system.build_pred g in - ( Path.Map.keys (Dep.Fact.Files.paths files) |> Path.Set.of_list - , Dep.Map.singleton (Dep.file_selector g) (Dep.Fact.file_selector g files) ) - | Lazy -> - let+ files = Build_system.eval_pred g in - files, Dep.Set.singleton (Dep.file_selector g) + let+ facts = Build_system.build_pred g in + (), Dep.Facts.singleton (Dep.file_selector g) (Dep.Fact.file_selector g facts) + | Lazy -> Memo.return ((), Dep.Set.singleton (Dep.file_selector g)) ;; let paths_matching ~loc:_ g = of_thunk { f = (fun mode -> paths_matching g mode) } -let paths_matching_unit ~loc g = ignore (paths_matching ~loc g) -let dyn_paths paths = dyn_deps (paths >>| fun (x, paths) -> x, Dep.Set.of_files paths) -let dyn_paths_unit paths = dyn_deps (paths >>| fun paths -> (), Dep.Set.of_files paths) - -let dyn_path_set paths = - dyn_deps (paths >>| fun (x, paths) -> x, Dep.Set.of_files_set paths) -;; - -let dyn_path_set_reuse paths = - dyn_deps (paths >>| fun paths -> paths, Dep.Set.of_files_set paths) -;; - -let env_var s = deps (Dep.Set.singleton (Dep.env s)) -let alias a = dep (Dep.alias a) let contents = let read_file = @@ -86,256 +47,23 @@ let contents = fun p -> of_thunk { f = - (fun _mode -> - let open Memo.O in - let+ x = read_file p in - x, Dep.Map.empty) + (fun mode -> + let open Memo.O in + let+ x = read_file p in + x, Deps_or_facts.empty mode) } ;; -let lines_of p = contents p >>| String.split_lines - -let read_sexp p = - let+ s = contents p in - Dune_sexp.Parser.parse_string s ~fname:(Path.to_string p) ~mode:Single -;; - let if_file_exists p ~then_ ~else_ = of_thunk { f = (fun mode -> - let open Memo.O in - Build_system.file_exists p - >>= function - | true -> run then_ mode - | false -> run else_ mode) + let open Memo.O in + Build_system.file_exists p + >>= function + | true -> run then_ mode + | false -> run else_ mode) } ;; let file_exists p = if_file_exists p ~then_:(return true) ~else_:(return false) - -let paths_existing paths = - all_unit - (List.map paths ~f:(fun file -> - if_file_exists file ~then_:(path file) ~else_:(return ()))) -;; - -let fail x = - let+ () = return () in - x.fail () -;; - -(* CR-someday amokhov: The set of targets is accumulated using information from - multiple sources by calling [Targets.combine], which performs set union and - hence duplicate declarations of the very same target can go unnoticed. I - think such redeclarations are not erroneous but are merely redundant; perhaps - we should detect and disallow them. *) -module With_targets = struct - type nonrec 'a t = - { build : 'a t - ; targets : Targets.t - } - - let map_build t ~f = { t with build = f t.build } - let return x = { build = return x; targets = Targets.empty } - - let add t ~file_targets = - { build = t.build - ; targets = - Targets.combine - t.targets - (Targets.Files.create (Path.Build.Set.of_list file_targets)) - } - ;; - - let add_directories t ~directory_targets = - { build = t.build - ; targets = - Targets.combine - t.targets - (Targets.create - ~dirs:(Path.Build.Set.of_list directory_targets) - ~files:Path.Build.Set.empty) - } - ;; - - let map { build; targets } ~f = { build = map build ~f; targets } - - let map2 x y ~f = - { build = map2 x.build y.build ~f; targets = Targets.combine x.targets y.targets } - ;; - - let both x y = - { build = both x.build y.build; targets = Targets.combine x.targets y.targets } - ;; - - let seq x y = - { build = x.build >>> y.build; targets = Targets.combine x.targets y.targets } - ;; - - module O = struct - let ( >>> ) = seq - let ( >>| ) t f = map t ~f - let ( and+ ) = both - let ( let+ ) a f = map ~f a - end - - open O - - let all xs = - match xs with - | [] -> return [] - | xs -> - let build, targets = - List.fold_left xs ~init:([], Targets.empty) ~f:(fun (builds, targets) x -> - x.build :: builds, Targets.combine x.targets targets) - in - { build = all (List.rev build); targets } - ;; - - let write_file_dyn ?(perm = Action.File_perm.Normal) fn s = - add - ~file_targets:[ fn ] - (let+ s = s in - Action.Full.make (Action.Write_file (fn, perm, s))) - ;; - - let memoize name t = { build = memoize name t.build; targets = t.targets } -end - -let with_targets build ~targets : _ With_targets.t = { build; targets } - -let with_file_targets build ~file_targets : _ With_targets.t = - { build; targets = Targets.Files.create (Path.Build.Set.of_list file_targets) } -;; - -let with_no_targets build : _ With_targets.t = { build; targets = Targets.empty } - -let write_file ?(perm = Action.File_perm.Normal) fn s = - with_file_targets - ~file_targets:[ fn ] - (return (Action.Full.make (Action.Write_file (fn, perm, s)))) -;; - -let write_file_dyn ?(perm = Action.File_perm.Normal) fn s = - with_file_targets - ~file_targets:[ fn ] - (let+ s = s in - Action.Full.make (Action.Write_file (fn, perm, s))) -;; - -let with_stdout_to ?(perm = Action.File_perm.Normal) fn t = - with_targets - ~targets:(Targets.File.create fn) - (let+ (act : Action.Full.t) = t in - Action.Full.map act ~f:(Action.with_stdout_to ~perm fn)) -;; - -let copy ~src ~dst = - with_file_targets - ~file_targets:[ dst ] - (path src >>> return (Action.Full.make (Action.Copy (src, dst)))) -;; - -let symlink ~src ~dst = - with_file_targets - ~file_targets:[ dst ] - (path src >>> return (Action.Full.make (Action.Symlink (src, dst)))) -;; - -let symlink_dir ~src ~dst = - with_targets - ~targets: - (Targets.create ~files:Path.Build.Set.empty ~dirs:(Path.Build.Set.singleton dst)) - (path src >>> return (Action.Full.make (Action.Symlink (src, dst)))) -;; - -let create_file ?(perm = Action.File_perm.Normal) fn = - with_file_targets - ~file_targets:[ fn ] - (return (Action.Full.make (Action.Redirect_out (Stdout, fn, perm, Action.empty)))) -;; - -let progn ts = - let open With_targets.O in - With_targets.all ts >>| Action.Full.reduce -;; - -let dyn_of_memo_deps t = dyn_deps (dyn_of_memo t) - -module Alias_status = struct - module T = struct - type t = - | Defined - | Not_defined - - let empty : t = Not_defined - - let combine : t -> t -> t = - fun x y -> - match x, y with - | _, Defined | Defined, _ -> Defined - | Not_defined, Not_defined -> Not_defined - ;; - end - - include T - include Monoid.Make (T) -end - -module Alias_build_info = struct - type t = - { alias_status : Alias_status.t - ; allowed_build_only_subdirs : Filename.Set.t - } - - let of_dir_set ~status dirs = - let allowed_build_only_subdirs = - match Dir_set.toplevel_subdirs dirs with - | Infinite -> Filename.Set.empty - | Finite sub_dirs -> sub_dirs - in - { alias_status = status; allowed_build_only_subdirs } - ;; -end - -let dep_on_alias_build_info_if_exists alias = - of_thunk - { f = - (fun mode -> - let open Memo.O in - Load_rules.load_dir ~dir:(Path.build (Alias.dir alias)) - >>= function - | Source _ | External _ -> - Code_error.raise "Alias in a non-build dir" [ "alias", Alias.to_dyn alias ] - | Build { aliases; allowed_subdirs; rules_here = _ } -> - (match Alias.Name.Map.find aliases (Alias.name alias) with - | None -> - Memo.return - ( Alias_build_info.of_dir_set ~status:Not_defined allowed_subdirs - , Dep.Map.empty ) - | Some _ -> - let deps = Dep.Set.singleton (Dep.alias alias) in - let+ deps = register_action_deps mode deps in - Alias_build_info.of_dir_set ~status:Defined allowed_subdirs, deps) - | Build_under_directory_target _ -> - Memo.return - ( Alias_build_info.of_dir_set ~status:Not_defined Dir_set.empty - , Dep.Map.empty )) - } -;; - -module Alias_rec (Traverse : sig - val traverse - : Path.Build.t - -> f:(path:Path.Build.t -> Alias_build_info.t t) - -> Alias_status.t t - end) = -struct - open Traverse - - let dep_on_alias_rec name dir = - let f ~path = dep_on_alias_build_info_if_exists (Alias.make ~dir:path name) in - traverse dir ~f - ;; -end diff --git a/src/dune_engine/action_builder.mli b/src/dune_engine/action_builder.mli index b5fc612e008e..3cfefbe41d84 100644 --- a/src/dune_engine/action_builder.mli +++ b/src/dune_engine/action_builder.mli @@ -1,193 +1,117 @@ -(** Action builder *) - -open! Import -include module type of Action_builder0 - -module With_targets : sig - type 'a build := 'a t - - type nonrec 'a t = - { build : 'a t - ; targets : Targets.t - } - - val map_build : 'a t -> f:('a build -> 'b build) -> 'b t - val return : 'a -> 'a t - val add : 'a t -> file_targets:Path.Build.t list -> 'a t - val add_directories : 'a t -> directory_targets:Path.Build.t list -> 'a t - val map : 'a t -> f:('a -> 'b) -> 'b t - val map2 : 'a t -> 'b t -> f:('a -> 'b -> 'c) -> 'c t - - val write_file_dyn - : ?perm:Action.File_perm.t - -> Path.Build.t - -> string t - -> Action.Full.t t - - val all : 'a t list -> 'a list t - - (** [memoize name t] is an action builder that behaves like [t] except that - its result is computed only once. *) - val memoize : string -> 'a t -> 'a t - - module O : sig - val ( >>> ) : unit t -> 'a t -> 'a t - val ( >>| ) : 'a t -> ('a -> 'b) -> 'b t - val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t - val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t - end +open Import + +type 'a t + +module O : sig + val ( >>> ) : unit t -> 'a t -> 'a t + val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t + val ( >>| ) : 'a t -> ('a -> 'b) -> 'b t + val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t + val ( and* ) : 'a t -> 'b t -> ('a * 'b) t + val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t + val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t end -(** Add targets to an action builder, turning a target-less [Action_builder.t] - into [Action_builder.With_targets.t]. *) -val with_targets : 'a t -> targets:Targets.t -> 'a With_targets.t +val return : 'a -> 'a t +val bind : 'a t -> f:('a -> 'b t) -> 'b t +val map : 'a t -> f:('a -> 'b) -> 'b t +val map2 : 'a t -> 'b t -> f:('a -> 'b -> 'c) -> 'c t +val both : 'a t -> 'b t -> ('a * 'b) t +val ignore : 'a t -> unit t +val all : 'a t list -> 'a list t +val all_unit : unit t list -> unit t -(** Like [with_targets] but specifies a list of file targets. *) -val with_file_targets : 'a t -> file_targets:Path.Build.t list -> 'a With_targets.t +module List : Monad.List with type 'a t := 'a t -(** Create a value of [With_targets.t] with the empty set of targets. *) -val with_no_targets : 'a t -> 'a With_targets.t +(** Delay a static computation until the description is evaluated *) +val delayed : (unit -> 'a) -> 'a t -(** CR-someday diml: this API is not great, what about: +type fail = { fail : 'a. unit -> 'a } - {[ - module Action_with_deps : sig - type t - val add_file_dependency : t -> Path.t -> t - end +(** Always fail when executed. We pass a function rather than an exception to + get a proper backtrace *) +val fail : fail -> _ t - (** Same as - [t >>> arr (fun x -> Action_with_deps.add_file_dependency x p)] - but better as [p] is statically known *) +(** [memoize ?cutoff name t] is an action builder that behaves like [t] except that its + result is computed only once, and the corresponding stack frame is augmented with a + [human_readable_description] (if provided). - val record_dependency - : Path.t - -> ('a, Action_with_deps.t) t - -> ('a, Action_with_deps.t) t - ]} *) - -(** [path p] records [p] as a file that is read by the action produced by the - action builder. *) -val path : Path.t -> unit t + If the caller provides the [cutoff] equality check, we will use it to check + if the result of the computation has changed. If it didn't, we will be able + to skip the recomputation of values that depend on it. *) +val memoize + : ?cutoff:('a -> 'a -> bool) + -> ?human_readable_description:(unit -> User_message.Style.t Pp.t) + -> string + -> 'a t + -> 'a t -val dep : Dep.t -> unit t -val deps : Dep.Set.t -> unit t -val dyn_deps : ('a * Dep.Set.t) t -> 'a t -val paths : Path.t list -> unit t -val path_set : Path.Set.t -> unit t +val memoize1 + : ?cutoff:('a -> 'a -> bool) + -> ?human_readable_description:(unit -> User_message.Style.t Pp.t) + -> string + -> 'a t + -> 'a t -(** Evaluate a predicate against all targets and record all the matched files as - dependencies of the action produced by the action builder. *) -val paths_matching : loc:Loc.t -> File_selector.t -> Path.Set.t t +type ('input, 'output) memo -(** Like [paths_matching], but don't return the resulting set. The action - dependency is still registered. *) -val paths_matching_unit : loc:Loc.t -> File_selector.t -> unit t +(** Same as [Memo.create] but for [Action_builder] *) +val create_memo + : string + -> input:(module Memo.Input with type t = 'i) + -> ?cutoff:('o -> 'o -> bool) + -> ?human_readable_description:('i -> User_message.Style.t Pp.t) + -> ('i -> 'o t) + -> ('i, 'o) memo -(** [paths_existing paths] will require as dependencies the files that actually - exist. *) -val paths_existing : Path.t list -> unit t +(** Same as [Memo.exec] but for [Action_builder]'s memos *) +val exec_memo : ('i, 'o) memo -> 'i -> 'o t -(** [env_var v] records [v] as an environment variable that is read by the - action produced by the action builder. *) -val env_var : string -> unit t +(** [goal t] ignores all facts that have been accumulated about the dependencies + of [t]. For example, [goal (path p)] declares that a path [p] contributes to + the "goal" of the resulting action builder, which means [p] must be built, + but the contents of [p] is irrelevant. *) +val goal : 'a t -> 'a t -module Alias_status : sig - type t = - | Defined - | Not_defined +(** If you're thinking of using [Process.run] here, check that: (i) you don't in + fact need [Command.run], and that (ii) [Process.run] only reads the declared + build rule dependencies. *) +val of_memo : 'a Memo.t -> 'a t - include Monoid.S with type t := t -end +(** Like [of_memo] but collapses the two levels of [t]. *) +val of_memo_join : 'a t Memo.t -> 'a t -val alias : Alias.t -> unit t +(** If you're thinking of using [Process.run] here, check that: (i) you don't in + fact need [Command.run], and that (ii) [Process.run] only reads the declared + build rule dependencies. *) +val dyn_of_memo : 'a Memo.t t -> 'a t -module Alias_build_info : sig - (** When looking up aliases, [alias_status] represents whether the alias was - defined in that directory or not. +(** {1 Execution} *) - For custom traversals, [allowed_build_only_subdirs] provides additional - information about immediate, build-only (not present in source) - sub-directories that the traversal might want to look under. *) - type t = - { alias_status : Alias_status.t - ; allowed_build_only_subdirs : Filename.Set.t - } -end +(** Evaluation mode. -module Alias_rec (_ : sig - (* This API isn't fully baked yet. We might move it to the rules *) - - (** [traverse dir ~f] traverses [dir] and evaluates [f] for every directory. - Returns [Defined] if [f] returned [Defined] at least once. [Not_defined] - otherwise. *) - val traverse - : Path.Build.t - -> f:(path:Path.Build.t -> Alias_build_info.t t) - -> Alias_status.t t - end) : sig - (** Depend on an alias recursively. Return [Defined] if the alias is defined - in at least one directory, and [Not_defined] otherwise. *) - val dep_on_alias_rec : Alias.Name.t -> Path.Build.t -> Alias_status.t t -end + In [Lazy] mode, dependencies are only collected. In [Eager] mode, + dependencies are build as soon as they are recorded and their facts are + returned. -(** [dyn_memo_deps m] adds the dependencies computed by [m] while returning the - extra value. *) -val dyn_memo_deps : (Dep.Set.t * 'a) Memo.t -> 'a t + If you want to both evaluate an action builder and build the collected + dependencies, using [Eager] mode will increase parallelism. If you only want + to know the set of dependencies, using [Lazy] will avoid unnecessary work. *) +type 'm eval_mode = + | Lazy : Dep.Set.t eval_mode + | Eager : Dep.Facts.t eval_mode -(** Record dynamic dependencies *) -val dyn_paths : ('a * Path.t list) t -> 'a t +(** Execute an action builder. *) +val run : 'a t -> 'm eval_mode -> ('a * 'm) Memo.t -val dyn_paths_unit : Path.t list t -> unit t -val dyn_path_set : ('a * Path.Set.t) t -> 'a t -val dyn_path_set_reuse : Path.Set.t t -> Path.Set.t t +(** {1 Low-level} *) -(** [contents path] returns a description that when run will return the contents - of the file at [path]. *) -val contents : Path.t -> string t +type 'a thunk = { f : 'm. 'm eval_mode -> ('a * 'm) Memo.t } [@@unboxed] -(** [lines_of path] returns a description that when run will return the contents - of the file at [path] as a list of lines. *) -val lines_of : Path.t -> string list t +val of_thunk : 'a thunk -> 'a t -(** Load an S-expression from a file *) -val read_sexp : Path.t -> Dune_sexp.Ast.t t - -(** Evaluates to [true] if the file is present on the file system or is the - target of a rule. It doesn't add the path as dependency *) -val file_exists : Path.t -> bool t - -(** [if_file_exists p ~then ~else] is a description that behaves like [then_] if - [file_exists p] evaluates to [true], and [else_] otherwise. *) -val if_file_exists : Path.t -> then_:'a t -> else_:'a t -> 'a t - -(** Create a file with the given contents. *) -val write_file - : ?perm:Action.File_perm.t - -> Path.Build.t - -> string - -> Action.Full.t With_targets.t - -val write_file_dyn - : ?perm:Action.File_perm.t - -> Path.Build.t - -> string t - -> Action.Full.t With_targets.t - -val with_stdout_to - : ?perm:Action.File_perm.t - -> Path.Build.t - -> Action.Full.t t - -> Action.Full.t With_targets.t - -val copy : src:Path.t -> dst:Path.Build.t -> Action.Full.t With_targets.t -val symlink : src:Path.t -> dst:Path.Build.t -> Action.Full.t With_targets.t -val symlink_dir : src:Path.t -> dst:Path.Build.t -> Action.Full.t With_targets.t -val create_file : ?perm:Action.File_perm.t -> Path.Build.t -> Action.Full.t With_targets.t - -(** Merge a list of actions accumulating the sets of their targets. *) -val progn : Action.Full.t With_targets.t list -> Action.Full.t With_targets.t - -(** A version of [dyn_of_memo] that makes it convenient to declare dynamic - action dependencies. *) -val dyn_of_memo_deps : ('a * Dep.Set.t) Memo.t t -> 'a t +module Deps_or_facts : sig + val empty : 'm eval_mode -> 'm + val union : 'm eval_mode -> 'm -> 'm -> 'm + val union_all : 'm eval_mode -> 'm list -> 'm +end diff --git a/src/dune_engine/action_builder0.ml b/src/dune_engine/action_builder0.ml index a34ef62e67bc..27cd5c6fdd81 100644 --- a/src/dune_engine/action_builder0.ml +++ b/src/dune_engine/action_builder0.ml @@ -1,20 +1,32 @@ open Import type 'a eval_mode = - | Lazy : unit eval_mode - | Eager : Dep.Fact.t eval_mode + | Lazy : Dep.Set.t eval_mode + | Eager : Dep.Facts.t eval_mode -type 'a thunk = { f : 'm. 'm eval_mode -> ('a * 'm Dep.Map.t) Memo.t } [@@unboxed] +type 'a thunk = { f : 'm. 'm eval_mode -> ('a * 'm) Memo.t } [@@unboxed] module Deps_or_facts = struct - let union : type a. a eval_mode -> a Dep.Map.t -> a Dep.Map.t -> a Dep.Map.t = + let empty : type m. m eval_mode -> m = function + | Lazy -> Dep.Set.empty + | Eager -> Dep.Facts.empty + ;; + + let return : type a m. a -> m eval_mode -> a * m = fun a mode -> a, empty mode + + let union : type m. m eval_mode -> m -> m -> m = fun mode a b -> match mode with | Lazy -> Dep.Set.union a b | Eager -> Dep.Facts.union a b ;; - let union_all mode l = List.fold_left l ~init:Dep.Map.empty ~f:(union mode) + let union_all : type m. m eval_mode -> m list -> m = + fun mode list -> + match mode with + | Lazy -> Dep.Set.union_all list + | Eager -> Dep.Facts.union_all list + ;; end module T = struct @@ -23,22 +35,22 @@ module T = struct module M = struct type 'a t = 'a thunk - let return x = { f = (fun _mode -> Memo.return (x, Dep.Map.empty)) } + let return x = { f = (fun mode -> Memo.return (Deps_or_facts.return x mode)) } let map t ~f = { f = (fun mode -> - let+ x, deps = t.f mode in - f x, deps) + let+ x, deps = t.f mode in + f x, deps) } ;; let bind t ~f = { f = (fun mode -> - let* x, deps1 = t.f mode in - let+ y, deps2 = (f x).f mode in - y, Deps_or_facts.union mode deps1 deps2) + let* x, deps1 = t.f mode in + let+ y, deps2 = (f x).f mode in + y, Deps_or_facts.union mode deps1 deps2) } ;; end @@ -48,26 +60,26 @@ module T = struct let both x y = { f = (fun mode -> - let+ x, deps1 = x.f mode - and+ y, deps2 = y.f mode in - (x, y), Deps_or_facts.union mode deps1 deps2) + let+ x, deps1 = x.f mode + and+ y, deps2 = y.f mode in + (x, y), Deps_or_facts.union mode deps1 deps2) } ;; let all xs = { f = (fun mode -> - let+ res = Memo.parallel_map xs ~f:(fun x -> x.f mode) in - let res, facts = List.split res in - res, Deps_or_facts.union_all mode facts) + let+ res = Memo.parallel_map xs ~f:(fun x -> x.f mode) in + let res, facts = List.split res in + res, Deps_or_facts.union_all mode facts) } ;; let of_memo m = { f = - (fun _mode -> - let+ x = m in - x, Dep.Map.empty) + (fun mode -> + let+ x = m in + x, Deps_or_facts.empty mode) } ;; @@ -75,10 +87,10 @@ module T = struct let ( >>> ) a b = { f = (fun mode -> - let+ ((), deps_a), (b, deps_b) = - Memo.fork_and_join (fun () -> a.f mode) (fun () -> b.f mode) - in - b, Deps_or_facts.union mode deps_a deps_b) + let+ ((), deps_a), (b, deps_b) = + Memo.fork_and_join (fun () -> a.f mode) (fun () -> b.f mode) + in + b, Deps_or_facts.union mode deps_a deps_b) } ;; @@ -99,18 +111,18 @@ module T = struct let map l ~f = { f = (fun mode -> - let+ res = Memo.parallel_map l ~f:(fun x -> (f x).f mode) in - let res, deps = List.split res in - res, Deps_or_facts.union_all mode deps) + let+ res = Memo.parallel_map l ~f:(fun x -> (f x).f mode) in + let res, deps = List.split res in + res, Deps_or_facts.union_all mode deps) } ;; let concat_map l ~f = { f = (fun mode -> - let+ res = Memo.parallel_map l ~f:(fun x -> (f x).f mode) in - let res, deps = List.split res in - List.concat res, Deps_or_facts.union_all mode deps) + let+ res = Memo.parallel_map l ~f:(fun x -> (f x).f mode) in + let res, deps = List.split res in + List.concat res, Deps_or_facts.union_all mode deps) } ;; end @@ -127,11 +139,11 @@ let of_thunk t = t let run t mode = t.f mode let force_lazy_or_eager - : type a b. - a eval_mode - -> (b * Dep.Set.t) Memo.Lazy.t Lazy.t - -> (b * Dep.Facts.t) Memo.Lazy.t - -> (b * a Dep.Map.t) Memo.t + : type a m. + m eval_mode + -> (a * Dep.Set.t) Memo.Lazy.t Lazy.t + -> (a * Dep.Facts.t) Memo.Lazy.t + -> (a * m) Memo.t = fun mode lazy_ eager -> match mode with @@ -143,7 +155,7 @@ let memoize ?cutoff name t = let lazy_ : ('a * Dep.Set.t) Memo.Lazy.t Lazy.t = lazy (let cutoff = - Option.map cutoff ~f:(fun equal -> Tuple.T2.equal equal Dep.Set.equal) + Option.map cutoff ~f:(fun equal x y -> Tuple.T2.equal equal Dep.Set.equal x y) in Memo.lazy_ ?cutoff ~name:(name ^ "(lazy)") (fun () -> t.f Lazy)) in @@ -151,50 +163,29 @@ let memoize ?cutoff name t = nodes end up getting forced during every build. *) let eager : ('a * Dep.Facts.t) Memo.Lazy.t = let cutoff = - Option.map cutoff ~f:(fun equal -> Tuple.T2.equal equal Dep.Facts.equal) + Option.map cutoff ~f:(fun equal x y -> Tuple.T2.equal equal Dep.Facts.equal x y) in Memo.lazy_ ?cutoff ~name (fun () -> t.f Eager) in { f = (fun mode -> force_lazy_or_eager mode lazy_ eager) } ;; -let ignore x = map x ~f:ignore - let map2 x y ~f = let+ x = x and+ y = y in f x y ;; -let push_stack_frame ~human_readable_description f = - { f = - (fun mode -> - Memo.push_stack_frame ~human_readable_description (fun () -> (f ()).f mode)) - } -;; - -let delayed f = - let+ () = return () in - f () -;; - let all_unit (xs : unit t list) = { f = (fun mode -> - let open Memo.O in - let+ res = Memo.parallel_map xs ~f:(fun x -> x.f mode) in - let deps = List.map res ~f:snd in - (), Deps_or_facts.union_all mode deps) + let open Memo.O in + let+ res = Memo.parallel_map xs ~f:(fun x -> x.f mode) in + let deps = List.map res ~f:snd in + (), Deps_or_facts.union_all mode deps) } ;; -type fail = { fail : 'a. unit -> 'a } - -let fail x = - let+ () = return () in - x.fail () -;; - type ('input, 'output) memo = { lazy_ : ('input, 'output * Dep.Set.t) Memo.Table.t Lazy.t ; eager : ('input, 'output * Dep.Facts.t) Memo.Table.t Lazy.t @@ -213,7 +204,7 @@ let create_memo name ~input ?cutoff ?human_readable_description f = lazy (let cutoff = Option.map cutoff ~f:(fun f (a, facts1) (b, facts2) -> - f a b && Dep.Map.equal facts1 facts2 ~equal:Dep.Fact.equal) + f a b && Dep.Facts.equal facts1 facts2) in Memo.create name ~input ?cutoff ?human_readable_description (fun x -> (f x).f Eager)) @@ -221,7 +212,7 @@ let create_memo name ~input ?cutoff ?human_readable_description f = { lazy_; eager } ;; -let exec_memo : type i o m. (i, o) memo -> i -> m eval_mode -> (o * m Dep.Map.t) Memo.t = +let exec_memo : type i o m. (i, o) memo -> i -> m eval_mode -> (o * m) Memo.t = fun memo i mode -> match mode with | Lazy -> Memo.exec (Lazy.force memo.lazy_) i @@ -233,19 +224,8 @@ let exec_memo m i = { f = (fun mode -> exec_memo m i mode) } let goal t = { f = (fun mode -> - let open Memo.O in - let+ a, (_irrelevant_for_goals : _ Dep.Map.t) = t.f mode in - a, Dep.Map.empty) + let open Memo.O in + let+ a, _facts_are_irrelevant_for_goals = t.f mode in + a, Deps_or_facts.empty mode) } ;; - -let of_memo_join f = - { f = - (fun mode -> - let open Memo.O in - let* t = f in - t.f mode) - } -;; - -let dyn_of_memo f = f >>= of_memo diff --git a/src/dune_engine/action_builder0.mli b/src/dune_engine/action_builder0.mli index 0a34e41d2b99..c82bb8b7b6dd 100644 --- a/src/dune_engine/action_builder0.mli +++ b/src/dune_engine/action_builder0.mli @@ -17,26 +17,11 @@ val bind : 'a t -> f:('a -> 'b t) -> 'b t val map : 'a t -> f:('a -> 'b) -> 'b t val map2 : 'a t -> 'b t -> f:('a -> 'b -> 'c) -> 'c t val both : 'a t -> 'b t -> ('a * 'b) t -val ignore : 'a t -> unit t val all : 'a t list -> 'a list t val all_unit : unit t list -> unit t module List : Monad.List with type 'a t := 'a t -val push_stack_frame - : human_readable_description:(unit -> User_message.Style.t Pp.t) - -> (unit -> 'a t) - -> 'a t - -(** Delay a static computation until the description is evaluated *) -val delayed : (unit -> 'a) -> 'a t - -type fail = { fail : 'a. unit -> 'a } - -(** Always fail when executed. We pass a function rather than an exception to - get a proper backtrace *) -val fail : fail -> _ t - (** [memoize ?cutoff name t] is an action builder that behaves like [t] except that its result is computed only once. @@ -70,14 +55,6 @@ val goal : 'a t -> 'a t build rule dependencies. *) val of_memo : 'a Memo.t -> 'a t -(** Like [of_memo] but collapses the two levels of [t]. *) -val of_memo_join : 'a t Memo.t -> 'a t - -(** If you're thinking of using [Process.run] here, check that: (i) you don't in - fact need [Command.run], and that (ii) [Process.run] only reads the declared - build rule dependencies. *) -val dyn_of_memo : 'a Memo.t t -> 'a t - (** {1 Execution} *) (** Evaluation mode. @@ -89,20 +66,19 @@ val dyn_of_memo : 'a Memo.t t -> 'a t If you want to both evaluate an action builder and build the collected dependencies, using [Eager] mode will increase parallelism. If you only want to know the set of dependencies, using [Lazy] will avoid unnecessary work. *) -type 'a eval_mode = - | Lazy : unit eval_mode - | Eager : Dep.Fact.t eval_mode +type 'm eval_mode = + | Lazy : Dep.Set.t eval_mode + | Eager : Dep.Facts.t eval_mode (** Execute an action builder. *) -val run : 'a t -> 'b eval_mode -> ('a * 'b Dep.Map.t) Memo.t +val run : 'a t -> 'm eval_mode -> ('a * 'm) Memo.t (** {1 Low-level} *) -type 'a thunk = { f : 'm. 'm eval_mode -> ('a * 'm Dep.Map.t) Memo.t } [@@unboxed] +type 'a thunk = { f : 'm. 'm eval_mode -> ('a * 'm) Memo.t } [@@unboxed] val of_thunk : 'a thunk -> 'a t module Deps_or_facts : sig - val union : 'a eval_mode -> 'a Dep.Map.t -> 'a Dep.Map.t -> 'a Dep.Map.t - val union_all : 'a eval_mode -> 'a Dep.Map.t list -> 'a Dep.Map.t + val empty : 'm eval_mode -> 'm end