the requirements of a rule can be placed anywhere within the
rule, as long as you put them before they are needed. *)
+(** {2 Periodic jobs}
+
+ If you want to have a rule that runs when some outside event
+ happens you have three choices: Manually run the script (this is
+ basically what [make] forces you to do). Have some sort of hook
+ that runs the script (eg. a git hook). Or use a periodic job to
+ poll for an event or change.
+
+ Periodic jobs run regularly to poll for an outside event or
+ change. If a script has periodic jobs, then it runs continuously
+ (or until you kill it).
+
+ An example of a script that checks for new git commits and when
+ it sees one it will ensure it passes the tests:
+
+ {v
+ let repo = Sys.getenv "HOME" // "repo"
+
+ let goal git_commit_tested commit =
+ let key = sprintf "repo-tested-%s" commit in
+ target (memory_exists key);
+
+ sh "
+ git clone %s test
+ cd test
+ ./configure
+ make
+ make check
+ ";
+
+ (* Record that this commit was tested successfully. *)
+ memory_set key "1"
+
+ every 30 minutes (fun () ->
+ let commit = shout "cd %s && git rev-parse HEAD" repo in
+ (* Require that this commit has been tested. *)
+ require (git_commit_tested commit)
+ )
+ }
+
+ Some notes about the above example: Firstly only the current HEAD
+ commit is required to be tested. This is because older commits
+ are irrelevant and because if they failed the test before there is
+ not point retesting them (commits are immutable). Secondly we use
+ the Memory to remember that we have successfully tested a commit.
+ This is what stops the program from repeatedly testing the same
+ commit. *)
+
+(* This is what lets you write '30 minutes' etc: *)
+type period_t = Seconds | Days | Months | Years
+val seconds : int * period_t
+val sec : int * period_t
+val secs : int * period_t
+val second : int * period_t
+val minutes : int * period_t
+val min : int * period_t
+val mins : int * period_t
+val minute : int * period_t
+val hours : int * period_t
+val hour : int * period_t
+val days : int * period_t
+val day : int * period_t
+val weeks : int * period_t
+val week : int * period_t
+val months : int * period_t
+val month : int * period_t
+val years : int * period_t
+val year : int * period_t
+
+val every : ?name:string -> int -> int * period_t -> (unit -> unit) -> unit
+ (** [every N (seconds|minutes|hours|days|weeks|months|years) f]
+ runs the function [f] periodically.
+
+ The optional [~name] parameter can be used to name the job
+ (for debugging). *)
+
(** {2 File and URL testing}
Various functions to test the existence of files, URLs.
{v require (file_exists "somefile");}
will die unless ["somefile"] exists. *)
+val directory_exists : string -> bool
+ (** Return true if the named directory exists.
+
+ There is also a goal version of this function. *)
+
val file_newer_than : string -> string -> bool
(** [file_newer_than file_a file_b] returns true if [file_a] is
newer than [file_b]. Note that if [file_a] does not exist, it
* if the predicate returns false.
*)
val goal_file_exists : string -> unit
+val goal_directory_exists : string -> unit
val goal_file_newer_than : string -> string -> unit
val goal_more_recent : string list -> string list -> unit
val goal_url_exists : string -> unit