let o_file = change_file_extension "o" c_file in
target (more_recent [o_file] [c_file]);
- sh "cc -c %s -o %s" c_file o_file
+ sh "cd $builddir && cc -c %s -o %s" c_file o_file
}
In the second example, the rule requires that several files
require (compiled source);
let object = change_file_extension "o" source in
- sh "cc %s -o %s" object program
+ sh "cd $builddir && cc %s -o %s" object program
}
*)
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 command ||: }
to ignore the result of a command.
- Each shell runs in a new temporary directory. The temporary directory
- and all its contents is deleted after the shell exits. If you
- want to save any data, [cd] somewhere. For example you could start
- the command sequence with:
- {v cd $HOME/data/ }
+ Each shell runs in a new temporary directory. The temporary
+ directory and all its contents is deleted after the shell exits.
+ If you want to save any data, [cd] somewhere. The environment
+ variable [$builddir] is exported to the script. This is the
+ current directory when the goaljobs program was started.
+
+ For example you could start the command sequence with
+ [cd $HOME/data/] or [cd $builddir].
*)
val sh : ('a, unit, string, unit) format4 -> 'a
Any lines printed to stdout is returned as a list of strings.
Trailing [\n] characters are not returned. *)
-(*
val shell : string ref
(** Set this variable to override the default shell ([/bin/sh]). *)
-*)
(** {2 String functions}