Add support for locks in rules
This is to allow users define more complicated tests without having to resort to -j1.
This commit is contained in:
parent
24e9fbf046
commit
5de73ca740
|
@ -20,6 +20,9 @@ next
|
||||||
`merge_into` field that were in `jbuild` files in sub-directories
|
`merge_into` field that were in `jbuild` files in sub-directories
|
||||||
where incorectly interpreted (#264)
|
where incorectly interpreted (#264)
|
||||||
|
|
||||||
|
- Add support for locks in actions, for tests that can't be run
|
||||||
|
concurrently (#263)
|
||||||
|
|
||||||
1.0+beta13 (05/09/2017)
|
1.0+beta13 (05/09/2017)
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
|
|
@ -301,6 +301,9 @@ See the `User actions`_ section for more details.
|
||||||
rules is to generate default configuration files that may be
|
rules is to generate default configuration files that may be
|
||||||
generated by a configure script.
|
generated by a configure script.
|
||||||
|
|
||||||
|
- ``(locks (<lock-names>))`` specify that the action must be run while
|
||||||
|
holding the following locks. See the `Locks`_ section for more details.
|
||||||
|
|
||||||
Note that contrary to makefiles or other build systems, user rules currently
|
Note that contrary to makefiles or other build systems, user rules currently
|
||||||
don't support patterns, such as a rule to produce ``%.y`` from ``%.x`` for any
|
don't support patterns, such as a rule to produce ``%.y`` from ``%.x`` for any
|
||||||
given ``%``. This might be supported in the future.
|
given ``%``. This might be supported in the future.
|
||||||
|
@ -426,6 +429,9 @@ The syntax is as follows:
|
||||||
``<name>`` and should be filtered out if ``<name>`` is filtered out from the
|
``<name>`` and should be filtered out if ``<name>`` is filtered out from the
|
||||||
command line, either with ``--only-packages <pkgs>`` or ``-p <pkgs>``
|
command line, either with ``--only-packages <pkgs>`` or ``-p <pkgs>``
|
||||||
|
|
||||||
|
- ``(locks (<lock-names>))`` specify that the action must be run while
|
||||||
|
holding the following locks. See the `Locks`_ section for more details.
|
||||||
|
|
||||||
The typical use of the ``alias`` stanza is to define tests:
|
The typical use of the ``alias`` stanza is to define tests:
|
||||||
|
|
||||||
.. code:: scheme
|
.. code:: scheme
|
||||||
|
@ -1026,6 +1032,56 @@ of your project. What you should write instead is:
|
||||||
(deps (blah.mll))
|
(deps (blah.mll))
|
||||||
(action (chdir ${ROOT} (run ocamllex -o ${@} ${<})))))
|
(action (chdir ${ROOT} (run ocamllex -o ${@} ${<})))))
|
||||||
|
|
||||||
|
Locks
|
||||||
|
-----
|
||||||
|
|
||||||
|
Given two rules that are independant, Jbuilder will assume that there
|
||||||
|
associated action can be run concurrently. Two rules are considered
|
||||||
|
independant if none of them depend on the other, either directly or
|
||||||
|
through a chain of dependencies. This basic assumption allows to
|
||||||
|
parallelize the build.
|
||||||
|
|
||||||
|
However, it is sometimes the case that two independant rules cannot be
|
||||||
|
executed concurrently. For instance this can happen for more
|
||||||
|
complicated tests. In order to prevent jbuilder from running the
|
||||||
|
actions at the same time, you can sepcify that both actions take the
|
||||||
|
same lock:
|
||||||
|
|
||||||
|
.. code:: scheme
|
||||||
|
|
||||||
|
(alias
|
||||||
|
((name runtest)
|
||||||
|
(deps (foo))
|
||||||
|
(locks (m))
|
||||||
|
(action (run test.exe ${<}))))
|
||||||
|
|
||||||
|
(alias
|
||||||
|
((name runtest)
|
||||||
|
(deps (bar))
|
||||||
|
(locks (m))
|
||||||
|
(action (run test.exe ${<}))))
|
||||||
|
|
||||||
|
Jbuilder will make sure that the executions of ``test.exe foo`` and
|
||||||
|
``test.exe bar`` are serialized.
|
||||||
|
|
||||||
|
Although they don't live in the filesystem, lock names are interpreted
|
||||||
|
as file names. So for instance ``(with-lock m ...)`` in ``src/jbuild``
|
||||||
|
and ``(with-lock ../src/m)`` in ``test/jbuild`` refer to the same
|
||||||
|
lock.
|
||||||
|
|
||||||
|
Note also that locks are per build context. So if your workspace has
|
||||||
|
two build contexts setup, the same rule might still be executed
|
||||||
|
concurrently between the two build contexts. If you want a lock that
|
||||||
|
is global to all build contexts, simply use an absolute filename:
|
||||||
|
|
||||||
|
.. code:: scheme
|
||||||
|
|
||||||
|
(alias
|
||||||
|
((name runtest)
|
||||||
|
(deps (foo))
|
||||||
|
(locks (/tcp-port/1042))
|
||||||
|
(action (run test.exe ${<}))))
|
||||||
|
|
||||||
.. _ocaml-syntax:
|
.. _ocaml-syntax:
|
||||||
|
|
||||||
OCaml syntax
|
OCaml syntax
|
||||||
|
|
|
@ -154,16 +154,18 @@ module Rule = struct
|
||||||
; targets : Target.t list
|
; targets : Target.t list
|
||||||
; sandbox : bool
|
; sandbox : bool
|
||||||
; fallback : Jbuild.Rule.Fallback.t
|
; fallback : Jbuild.Rule.Fallback.t
|
||||||
|
; locks : Path.t list
|
||||||
; loc : Loc.t option
|
; loc : Loc.t option
|
||||||
}
|
}
|
||||||
|
|
||||||
let make ?(sandbox=false) ?(fallback=Jbuild.Rule.Fallback.Not_possible)
|
let make ?(sandbox=false) ?(fallback=Jbuild.Rule.Fallback.Not_possible)
|
||||||
?context ?loc build =
|
?context ?(locks=[]) ?loc build =
|
||||||
{ context
|
{ context
|
||||||
; build
|
; build
|
||||||
; targets = targets build
|
; targets = targets build
|
||||||
; sandbox
|
; sandbox
|
||||||
; fallback
|
; fallback
|
||||||
|
; locks
|
||||||
; loc
|
; loc
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@ module Rule : sig
|
||||||
; targets : Target.t list
|
; targets : Target.t list
|
||||||
; sandbox : bool
|
; sandbox : bool
|
||||||
; fallback : Jbuild.Rule.Fallback.t
|
; fallback : Jbuild.Rule.Fallback.t
|
||||||
|
; locks : Path.t list
|
||||||
; loc : Loc.t option
|
; loc : Loc.t option
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ module Rule : sig
|
||||||
: ?sandbox:bool
|
: ?sandbox:bool
|
||||||
-> ?fallback:Jbuild.Rule.Fallback.t
|
-> ?fallback:Jbuild.Rule.Fallback.t
|
||||||
-> ?context:Context.t
|
-> ?context:Context.t
|
||||||
|
-> ?locks:Path.t list
|
||||||
-> ?loc:Loc.t
|
-> ?loc:Loc.t
|
||||||
-> (unit, Action.t) Build.t
|
-> (unit, Action.t) Build.t
|
||||||
-> t
|
-> t
|
||||||
|
|
|
@ -405,6 +405,16 @@ let make_local_parent_dirs t paths ~map_path =
|
||||||
|
|
||||||
let sandbox_dir = Path.of_string "_build/.sandbox"
|
let sandbox_dir = Path.of_string "_build/.sandbox"
|
||||||
|
|
||||||
|
let locks : (Path.t, Future.Mutex.t) Hashtbl.t = Hashtbl.create 32
|
||||||
|
|
||||||
|
let rec with_locks mutexes ~f =
|
||||||
|
match mutexes with
|
||||||
|
| [] -> f ()
|
||||||
|
| m :: mutexes ->
|
||||||
|
Future.Mutex.with_lock
|
||||||
|
(Hashtbl.find_or_add locks m ~f:(fun _ -> Future.Mutex.create ()))
|
||||||
|
(fun () -> with_locks mutexes ~f)
|
||||||
|
|
||||||
let compile_rule t ~all_targets_by_dir ?(copy_source=false) pre_rule =
|
let compile_rule t ~all_targets_by_dir ?(copy_source=false) pre_rule =
|
||||||
let { Pre_rule.
|
let { Pre_rule.
|
||||||
context
|
context
|
||||||
|
@ -412,6 +422,7 @@ let compile_rule t ~all_targets_by_dir ?(copy_source=false) pre_rule =
|
||||||
; targets = target_specs
|
; targets = target_specs
|
||||||
; sandbox
|
; sandbox
|
||||||
; fallback
|
; fallback
|
||||||
|
; locks
|
||||||
; loc
|
; loc
|
||||||
} =
|
} =
|
||||||
pre_rule
|
pre_rule
|
||||||
|
@ -500,7 +511,8 @@ let compile_rule t ~all_targets_by_dir ?(copy_source=false) pre_rule =
|
||||||
action
|
action
|
||||||
in
|
in
|
||||||
make_local_dirs t (Action.chdirs action);
|
make_local_dirs t (Action.chdirs action);
|
||||||
Action.exec ~targets action >>| fun () ->
|
with_locks locks ~f:(fun () ->
|
||||||
|
Action.exec ~targets action) >>| fun () ->
|
||||||
Option.iter sandbox_dir ~f:Path.rm_rf;
|
Option.iter sandbox_dir ~f:Path.rm_rf;
|
||||||
(* All went well, these targets are no longer pending *)
|
(* All went well, these targets are no longer pending *)
|
||||||
pending_targets := Pset.diff !pending_targets targets_to_remove;
|
pending_targets := Pset.diff !pending_targets targets_to_remove;
|
||||||
|
|
|
@ -98,6 +98,17 @@ let with_exn_handler f ~handler =
|
||||||
handler exn bt;
|
handler exn bt;
|
||||||
reraise exn
|
reraise exn
|
||||||
|
|
||||||
|
let finalize f ~finally =
|
||||||
|
let finalize = lazy(finally ()) in
|
||||||
|
with_exn_handler
|
||||||
|
(fun () ->
|
||||||
|
f () >>| fun x ->
|
||||||
|
Lazy.force finalize;
|
||||||
|
x)
|
||||||
|
~handler:(fun exn _ ->
|
||||||
|
Lazy.force finalize;
|
||||||
|
reraise exn)
|
||||||
|
|
||||||
let both a b =
|
let both a b =
|
||||||
a >>= fun a ->
|
a >>= fun a ->
|
||||||
b >>= fun b ->
|
b >>= fun b ->
|
||||||
|
@ -134,6 +145,41 @@ let rec all_unit = function
|
||||||
x >>= fun () ->
|
x >>= fun () ->
|
||||||
all_unit l
|
all_unit l
|
||||||
|
|
||||||
|
type to_fill = To_fill : 'a Ivar.t * 'a -> to_fill
|
||||||
|
|
||||||
|
let to_fill = Queue.create ()
|
||||||
|
|
||||||
|
module Mutex = struct
|
||||||
|
type t =
|
||||||
|
{ mutable locked : bool
|
||||||
|
; mutable waiters : unit Ivar.t Queue.t
|
||||||
|
}
|
||||||
|
|
||||||
|
let lock t =
|
||||||
|
if t.locked then
|
||||||
|
create (fun ivar -> Queue.push ivar t.waiters)
|
||||||
|
else begin
|
||||||
|
t.locked <- true;
|
||||||
|
return ()
|
||||||
|
end
|
||||||
|
|
||||||
|
let unlock t =
|
||||||
|
assert t.locked;
|
||||||
|
if Queue.is_empty t.waiters then
|
||||||
|
t.locked <- false
|
||||||
|
else
|
||||||
|
Queue.push (To_fill (Queue.pop t.waiters, ())) to_fill
|
||||||
|
|
||||||
|
let with_lock t f =
|
||||||
|
lock t >>= fun () ->
|
||||||
|
finalize f ~finally:(fun () -> unlock t)
|
||||||
|
|
||||||
|
let create () =
|
||||||
|
{ locked = false
|
||||||
|
; waiters = Queue.create ()
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
type accepted_codes =
|
type accepted_codes =
|
||||||
| These of int list
|
| These of int list
|
||||||
| All
|
| All
|
||||||
|
@ -655,6 +701,10 @@ module Scheduler = struct
|
||||||
done;
|
done;
|
||||||
let job, status = Running_jobs.wait () in
|
let job, status = Running_jobs.wait () in
|
||||||
process_done job status;
|
process_done job status;
|
||||||
|
while not (Queue.is_empty to_fill) do
|
||||||
|
let (To_fill (ivar, x)) = Queue.pop to_fill in
|
||||||
|
Ivar.fill ivar x
|
||||||
|
done;
|
||||||
go_rec cwd log t
|
go_rec cwd log t
|
||||||
|
|
||||||
let go ?(log=Log.no_log) t =
|
let go ?(log=Log.no_log) t =
|
||||||
|
|
|
@ -15,6 +15,13 @@ val all_unit : unit t list -> unit t
|
||||||
|
|
||||||
val with_exn_handler : (unit -> 'a) -> handler:(exn -> Printexc.raw_backtrace -> unit) -> 'a
|
val with_exn_handler : (unit -> 'a) -> handler:(exn -> Printexc.raw_backtrace -> unit) -> 'a
|
||||||
|
|
||||||
|
module Mutex : sig
|
||||||
|
type 'a future = 'a t
|
||||||
|
type t
|
||||||
|
val create : unit -> t
|
||||||
|
val with_lock : t -> (unit -> 'a future) -> 'a future
|
||||||
|
end with type 'a future := 'a t
|
||||||
|
|
||||||
type accepted_codes =
|
type accepted_codes =
|
||||||
| These of int list
|
| These of int list
|
||||||
| All
|
| All
|
||||||
|
|
|
@ -549,6 +549,10 @@ module Gen(P : Params) = struct
|
||||||
| User rules |
|
| User rules |
|
||||||
+-----------------------------------------------------------------+ *)
|
+-----------------------------------------------------------------+ *)
|
||||||
|
|
||||||
|
let interpret_locks ~dir ~scope locks =
|
||||||
|
List.map locks ~f:(fun s ->
|
||||||
|
Path.relative dir (SC.expand_vars sctx ~dir ~scope s))
|
||||||
|
|
||||||
let user_rule (rule : Rule.t) ~dir ~scope =
|
let user_rule (rule : Rule.t) ~dir ~scope =
|
||||||
let targets : SC.Action.targets =
|
let targets : SC.Action.targets =
|
||||||
match rule.targets with
|
match rule.targets with
|
||||||
|
@ -556,6 +560,7 @@ module Gen(P : Params) = struct
|
||||||
| Static fns -> Static (List.map fns ~f:(Path.relative dir))
|
| Static fns -> Static (List.map fns ~f:(Path.relative dir))
|
||||||
in
|
in
|
||||||
SC.add_rule sctx ~fallback:rule.fallback ~loc:rule.loc
|
SC.add_rule sctx ~fallback:rule.fallback ~loc:rule.loc
|
||||||
|
~locks:(interpret_locks ~dir ~scope rule.locks)
|
||||||
(SC.Deps.interpret sctx ~scope ~dir rule.deps
|
(SC.Deps.interpret sctx ~scope ~dir rule.deps
|
||||||
>>>
|
>>>
|
||||||
SC.Action.run
|
SC.Action.run
|
||||||
|
@ -584,6 +589,7 @@ module Gen(P : Params) = struct
|
||||||
Alias.add_deps (SC.aliases sctx) alias [digest_path];
|
Alias.add_deps (SC.aliases sctx) alias [digest_path];
|
||||||
let deps = SC.Deps.interpret sctx ~scope ~dir alias_conf.deps in
|
let deps = SC.Deps.interpret sctx ~scope ~dir alias_conf.deps in
|
||||||
SC.add_rule sctx
|
SC.add_rule sctx
|
||||||
|
~locks:(interpret_locks ~dir ~scope alias_conf.locks)
|
||||||
(match alias_conf.action with
|
(match alias_conf.action with
|
||||||
| None ->
|
| None ->
|
||||||
deps
|
deps
|
||||||
|
|
|
@ -724,6 +724,7 @@ module Rule = struct
|
||||||
; deps : Dep_conf.t list
|
; deps : Dep_conf.t list
|
||||||
; action : Action.Unexpanded.t
|
; action : Action.Unexpanded.t
|
||||||
; fallback : Fallback.t
|
; fallback : Fallback.t
|
||||||
|
; locks : String_with_vars.t list
|
||||||
; loc : Loc.t
|
; loc : Loc.t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,18 +735,21 @@ module Rule = struct
|
||||||
; deps = []
|
; deps = []
|
||||||
; action = Action.Unexpanded.t sexp
|
; action = Action.Unexpanded.t sexp
|
||||||
; fallback = No
|
; fallback = No
|
||||||
; loc = Loc.none
|
; locks = []
|
||||||
|
; loc = Loc.none
|
||||||
}
|
}
|
||||||
| _ ->
|
| _ ->
|
||||||
record
|
record
|
||||||
(field "targets" (list file_in_current_dir) >>= fun targets ->
|
(field "targets" (list file_in_current_dir) >>= fun targets ->
|
||||||
field "deps" (list Dep_conf.t) ~default:[] >>= fun deps ->
|
field "deps" (list Dep_conf.t) ~default:[] >>= fun deps ->
|
||||||
field "action" Action.Unexpanded.t >>= fun action ->
|
field "action" Action.Unexpanded.t >>= fun action ->
|
||||||
|
field "locks" (list String_with_vars.t) ~default:[] >>= fun locks ->
|
||||||
field_b "fallback" >>= fun fallback ->
|
field_b "fallback" >>= fun fallback ->
|
||||||
return { targets = Static targets
|
return { targets = Static targets
|
||||||
; deps
|
; deps
|
||||||
; action
|
; action
|
||||||
; fallback = if fallback then Yes else No
|
; fallback = if fallback then Yes else No
|
||||||
|
; locks
|
||||||
; loc = Loc.none
|
; loc = Loc.none
|
||||||
})
|
})
|
||||||
sexp
|
sexp
|
||||||
|
@ -767,6 +771,7 @@ module Rule = struct
|
||||||
; S.virt_var __POS__"<"
|
; S.virt_var __POS__"<"
|
||||||
]))
|
]))
|
||||||
; fallback = Not_possible
|
; fallback = Not_possible
|
||||||
|
; locks = []
|
||||||
; loc
|
; loc
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -782,6 +787,7 @@ module Rule = struct
|
||||||
Run (S.virt_text __POS__ "ocamlyacc",
|
Run (S.virt_text __POS__ "ocamlyacc",
|
||||||
[S.virt_var __POS__ "<"]))
|
[S.virt_var __POS__ "<"]))
|
||||||
; fallback = Not_possible
|
; fallback = Not_possible
|
||||||
|
; locks = []
|
||||||
; loc
|
; loc
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -820,7 +826,8 @@ module Menhir = struct
|
||||||
(S.virt_var __POS__ "ROOT",
|
(S.virt_var __POS__ "ROOT",
|
||||||
Run (S.virt_text __POS__ "menhir",
|
Run (S.virt_text __POS__ "menhir",
|
||||||
t.flags @ [S.virt_var __POS__ "<"]))
|
t.flags @ [S.virt_var __POS__ "<"]))
|
||||||
; fallback = Not_possible
|
; fallback = Not_possible
|
||||||
|
; locks = []
|
||||||
; loc
|
; loc
|
||||||
})
|
})
|
||||||
| Some merge_into ->
|
| Some merge_into ->
|
||||||
|
@ -840,6 +847,7 @@ module Menhir = struct
|
||||||
; [ S.virt_var __POS__ "!^" ]
|
; [ S.virt_var __POS__ "!^" ]
|
||||||
]))
|
]))
|
||||||
; fallback = Not_possible
|
; fallback = Not_possible
|
||||||
|
; locks = []
|
||||||
; loc
|
; loc
|
||||||
}]
|
}]
|
||||||
end
|
end
|
||||||
|
@ -869,9 +877,10 @@ end
|
||||||
|
|
||||||
module Alias_conf = struct
|
module Alias_conf = struct
|
||||||
type t =
|
type t =
|
||||||
{ name : string
|
{ name : string
|
||||||
; deps : Dep_conf.t list
|
; deps : Dep_conf.t list
|
||||||
; action : Action.Unexpanded.t option
|
; action : Action.Unexpanded.t option
|
||||||
|
; locks : String_with_vars.t list
|
||||||
; package : Package.t option
|
; package : Package.t option
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,11 +890,13 @@ module Alias_conf = struct
|
||||||
field "deps" (list Dep_conf.t) ~default:[] >>= fun deps ->
|
field "deps" (list Dep_conf.t) ~default:[] >>= fun deps ->
|
||||||
field_o "package" (Scope.package pkgs) >>= fun package ->
|
field_o "package" (Scope.package pkgs) >>= fun package ->
|
||||||
field_o "action" Action.Unexpanded.t >>= fun action ->
|
field_o "action" Action.Unexpanded.t >>= fun action ->
|
||||||
|
field "locks" (list String_with_vars.t) ~default:[] >>= fun locks ->
|
||||||
return
|
return
|
||||||
{ name
|
{ name
|
||||||
; deps
|
; deps
|
||||||
; action
|
; action
|
||||||
; package
|
; package
|
||||||
|
; locks
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -205,6 +205,7 @@ module Rule : sig
|
||||||
; deps : Dep_conf.t list
|
; deps : Dep_conf.t list
|
||||||
; action : Action.Unexpanded.t
|
; action : Action.Unexpanded.t
|
||||||
; fallback : Fallback.t
|
; fallback : Fallback.t
|
||||||
|
; locks : String_with_vars.t list
|
||||||
; loc : Loc.t
|
; loc : Loc.t
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -218,9 +219,10 @@ end
|
||||||
|
|
||||||
module Alias_conf : sig
|
module Alias_conf : sig
|
||||||
type t =
|
type t =
|
||||||
{ name : string
|
{ name : string
|
||||||
; deps : Dep_conf.t list
|
; deps : Dep_conf.t list
|
||||||
; action : Action.Unexpanded.t option
|
; action : Action.Unexpanded.t option
|
||||||
|
; locks : String_with_vars.t list
|
||||||
; package : Package.t option
|
; package : Package.t option
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -213,9 +213,12 @@ let create
|
||||||
| _ -> Chdir (context.build_dir, action))
|
| _ -> Chdir (context.build_dir, action))
|
||||||
}
|
}
|
||||||
|
|
||||||
let add_rule t ?sandbox ?fallback ?loc build =
|
let add_rule t ?sandbox ?fallback ?locks ?loc build =
|
||||||
let build = Build.O.(>>>) build t.chdir in
|
let build = Build.O.(>>>) build t.chdir in
|
||||||
let rule = Build_interpret.Rule.make ?sandbox ?fallback ?loc ~context:t.context build in
|
let rule =
|
||||||
|
Build_interpret.Rule.make ?sandbox ?fallback ?locks ?loc
|
||||||
|
~context:t.context build
|
||||||
|
in
|
||||||
t.rules <- rule :: t.rules;
|
t.rules <- rule :: t.rules;
|
||||||
t.known_targets_by_src_dir_so_far <-
|
t.known_targets_by_src_dir_so_far <-
|
||||||
List.fold_left rule.targets ~init:t.known_targets_by_src_dir_so_far
|
List.fold_left rule.targets ~init:t.known_targets_by_src_dir_so_far
|
||||||
|
|
|
@ -45,6 +45,7 @@ val add_rule
|
||||||
: t
|
: t
|
||||||
-> ?sandbox:bool
|
-> ?sandbox:bool
|
||||||
-> ?fallback:Jbuild.Rule.Fallback.t
|
-> ?fallback:Jbuild.Rule.Fallback.t
|
||||||
|
-> ?locks:Path.t list
|
||||||
-> ?loc:Loc.t
|
-> ?loc:Loc.t
|
||||||
-> (unit, Action.t) Build.t
|
-> (unit, Action.t) Build.t
|
||||||
-> unit
|
-> unit
|
||||||
|
|
53
test/jbuild
53
test/jbuild
|
@ -21,3 +21,56 @@
|
||||||
(deps ((alias sleep5)
|
(deps ((alias sleep5)
|
||||||
(alias sleep4-and-fail)
|
(alias sleep4-and-fail)
|
||||||
(alias sleep1-and-fail)))))
|
(alias sleep1-and-fail)))))
|
||||||
|
|
||||||
|
;; execute this to test locks
|
||||||
|
;;
|
||||||
|
;; $ ./_build/default/bin/main.exe build -j10 @test/locks
|
||||||
|
;;
|
||||||
|
|
||||||
|
(alias
|
||||||
|
((name locks)
|
||||||
|
(deps ((glob_files *.{foo,bar})))
|
||||||
|
(action (bash "\
|
||||||
|
echo 'expected result: 10'
|
||||||
|
echo 'without locking:' $(< x)
|
||||||
|
echo 'with locking: ' $(< y)
|
||||||
|
rm -f *.{foo,bar} x y"))))
|
||||||
|
|
||||||
|
(rule (with-stdout-to incr.ml (echo "\
|
||||||
|
let fn = Sys.argv.(1) in
|
||||||
|
let x =
|
||||||
|
match open_in fn with
|
||||||
|
| ic ->
|
||||||
|
let x = int_of_string (input_line ic) in
|
||||||
|
close_in ic;
|
||||||
|
x
|
||||||
|
| exception _ -> 0
|
||||||
|
in
|
||||||
|
Unix.sleepf 0.2;
|
||||||
|
Printf.fprintf (open_out fn) \"%d\\n\" (x + 1);
|
||||||
|
Printf.fprintf (open_out Sys.argv.(2)) \"%g\n%!\" (Sys.time ())
|
||||||
|
")))
|
||||||
|
|
||||||
|
(executable ((name incr) (libraries (unix))))
|
||||||
|
|
||||||
|
(rule ((targets (01.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (02.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (03.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (04.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (05.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (06.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (07.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (08.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (09.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
(rule ((targets (10.foo)) (action (run ./incr.exe x ${@}))))
|
||||||
|
|
||||||
|
(rule ((targets (01.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (02.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (03.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (04.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (05.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (06.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (07.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (08.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (09.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
(rule ((targets (10.bar)) (action (run ./incr.exe y ${@})) (locks (m))))
|
||||||
|
|
Loading…
Reference in New Issue