cc %s -o %s
" object program
+=head1 SPECIAL VALUES INSIDE GOALS
+
+=head2 goalname
+
+Inside goals, you can use C<goalname> to get the name of the goal, ie:
+
+ let goal foo () =
+ printf "my name is %s\n" goalname
+
+would print:
+
+ my name is foo
+
+=head2 goalloc
+
+Inside goals, you can use C<goalloc> to get a printable source
+location of the goal, ie:
+
+ let goal foo () =
+ printf "%s\n" goalloc
+
+would print:
+
+ File "source.ml", line 2, characters 13-71 (end at line 3, character 23)
+
+Note that the actual string format depends on the internal OCaml
+function C<Loc.to_string> so it might change in future.
+
+=head2 onfail, onsuccess, onrun
+
+Inside goals you can register function(s) which run if the goal
+completes successfully (C<onsuccess>), if the goal completes
+successfully after running to the end (C<onrun>), or if the goal fails
+(C<onfail>).
+
+For example:
+
+ let goal built () =
+ onfail (fun _ -> eprintf "goal '%s' failed\n" goalname);
+ sh "
+ cc -o program main.o
+ "
+
+If the shell command (or another part of the goal) fails, then this
+would print out:
+
+ goal 'built' failed
+
+The single parameter passed to C<onfail> is the exception that was
+thrown.
+
+Note that the helper function C<Goaljobs.mailto> is a useful function
+to call from an C<onfail> handler:
+
+ let from = "me@example.com"
+ let to_ = "you@example.com"
+ let logfile = log_program_output ()
+
+ let goal built () =
+ onfail (fun _ ->
+ let subject = sprintf "goal: %s: BUILD FAILED" goalname in
+ mailto ~from ~subject ~attach:[logfile] to_);
+ sh "
+ cc -o program main.o
+ "
+
+C<onsuccess> and C<onrun> are slightly different from C<onfail> and
+from each other:
+
+C<onsuccess> functions can be called if a C<target> condition is met
+and the rest of the goal is short-circuited. C<onrun> will only be
+called if all the instructions in the goal actually run and succeed.
+
+The single unit C<()> parameter is passed to the C<onsuccess> and
+C<onrun> functions.
+
+You can register as many functions as you want for each handler. The
+order in which the functions are called is not defined.
+
=head1 PERIODIC JOBS
If you want to have a goal that runs when some outside event happens
let goal git_commit_tested commit =
let key = sprintf "repo-tested-%s" commit in
target (memory_exists key);
+ onrun (fun () -> memory_set key "1");
sh "
git clone %s test
./configure
make
make check
- " repo_url;
-
- (* Record that this commit was tested successfully. *)
- memory_set key "1"
+ " repo_url
every 30 minutes (fun () ->
let commit = shout "cd %s && git rev-parse HEAD" repo in
let goal tested version =
let key = "myapp-tested-" ^ version in
target (memory_exists key);
+ onrun (fun () -> memory_set key "1");
(* some code to test this version *)
-
- memory_set key "1"
Note in that example the value C<1> is arbitrary. You just want to
store I<any> value so that a later call to C<memory_exists> will