debug: Print the name of goals that run, and goals required.
[goaljobs.git] / goaljobs-reference.pod
1 =encoding utf8
2
3 =head1 NAME
4
5 goaljobs-reference - reference documentation for writing goaljobs scripts
6
7 =head1 SUMMARY
8
9  open Unix
10  open Printf
11  open Goaljobs
12  
13  let goal name args... =
14    target (condition);
15    require (goal1 ...);
16    require (goal2 ...);
17    (* code to implement the goal *)
18  
19  let goal all () =
20    require (name args...)
21  
22  every 30 minutes (
23    fun () ->
24      require (goal1 ...)
25  )
26
27 =head1 DESCRIPTION
28
29 Goaljobs is a flexible build system and business rules manager similar
30 to make and cron, but much more powerful.  You can use it to automate
31 many complex tasks that have multiple steps (even with manual steps)
32 that have to be carried out in dependency order.
33
34 For a tutorial-like introduction to goaljobs, see
35 L<http://rwmj.wordpress.com/tag/goaljobs/>
36
37 For examples, see the C<examples/> directory in the source and
38 L<http://git.annexia.org/?p=goals.git;a=summary>
39
40 For reference documentation on how to write scripts, see below.
41
42 Note this man page does not cover the whole Goaljobs API.  To read
43 about the Goaljobs API, look for the file C<goaljobs.mli> (in the
44 source code or installed as part of the goaljobs package), or see the
45 HTML documentation installed as part of the goaljobs package.
46
47 =head1 THE SCRIPT FILE
48
49 The script file should usually start with opening these modules (none
50 of this are required, it's just useful to have them open):
51
52  open Unix
53  open Printf
54  open Goaljobs
55
56 This is followed by goals and/or functions and/or top-level OCaml
57 statements and/or C<every> statements (a.k.a periodic jobs).
58
59 You can use multiple script files to make up a goaljobs program.  You
60 have to list them in dependency order on the goaljobs command line
61 (earlier files required by later files), the same way that the OCaml
62 compiler works.  So usually you end up writing:
63
64  goaljobs utils.ml another_library.ml script.ml
65
66 where C<script.ml> requires the functions from the utils/library.
67 Note that circular dependencies are not possible.
68
69 =head1 GOALS
70
71 Each goal should have the following basic form:
72
73  let goal name args... =
74    target (condition);
75    require (goal1 ...);
76    require (goal2 ...);
77    (* code to implement the goal *)
78
79 There is no hard-and-fast rule about this.  In particular you can put
80 arbitrary OCaml statements anywhere inside a goal (since a goal is
81 just a special form of OCaml function), but sticking to this overall
82 plan is a good idea.
83
84 There should be zero or one target.  Multiple target statements should
85 not be used in a goal.  The target should come as early as possible,
86 and the target condition should be as simple and fast to evaluate as
87 is practical (see L</THE MEMORY> below).
88
89 There should be zero or any number of C<require> statements.  Each
90 require statement should name a single goal (with optional parameters
91 for that goal).
92
93 After that should come the code that implements the goal, which might
94 be, for example, a series of shell commands, but could even be
95 user-interactive.
96
97 As with ordinary OCaml functions, you can define goals recursively
98 or with mutual recursion using:
99
100  let rec goal1 args... =
101    ...
102  and goal2 args... =
103    ...
104  and goal3 args... =
105    ...
106
107 A goal can also have no arguments:
108
109  let goal all () =
110    ...
111
112 This defines the common goal called C<all>, which acts the same way as
113 C<make all>, ie. if you run the program without any arguments, it will
114 run the C<all> goal if one exists.
115
116 =head2 PUBLISHING GOALS
117
118 If a goal is "published" it means it is available to be run directly
119 from the command line.  All no-arg goals are published by default.
120 You do not need to do anything special for them.  For example:
121
122  let goal clean () = sh "rm *~"
123
124 can be used on the command line:
125
126  ./myscript clean
127
128 For goals which take any parameters, you have to define a small code
129 snippet that converts command line arguments to goal parameters (the
130 reason has to do with OCaml being strongly typed, and because goal
131 parameters might not all be strings).
132
133  let goal compile program sources =
134    target (more_recent [program] sources);
135    ...
136  
137  let () =
138    publish "compile" (
139      fun args ->
140        let program = List.hd args in
141        let sources = List.tl args in
142        require (compiled program sources)
143    )
144
145 Then you can use:
146
147  ./myscript compile program main.c utils.c
148
149 =head1 TARGET AND REQUIRE
150
151 The target is promise or contract that you make that the given
152 condition I<will> be true when the goal has finished running.
153
154 In the first example, the target is that the C<o_file> (object) exists
155 and is newer than the C<c_file> (source).  The goal meets that target
156 by running the C compiler (C<cc>) which, if it succeeds, will ensure
157 that the object file exists and is newer than the source file.
158
159  let goal compiled c_file =
160    let o_file = change_file_extension "o" c_file in
161    target (more_recent [o_file] [c_file]);
162  
163    sh "
164      cd $builddir
165      cc -c %s -o %s
166    " c_file o_file
167
168 In the second example, the goal requires that several files have been
169 compiled (C<require (compiled ...)>) before it can link the final
170 program:
171
172  let goal built program source =
173    target (more_recent [program] [source]);
174  
175    require (compiled source);
176  
177    let object = change_file_extension "o" source in
178    sh "
179      cd $builddir
180      cc %s -o %s
181    " object program
182
183 =head1 SPECIAL VALUES INSIDE GOALS
184
185 =head2 goalname
186
187 Inside goals, you can use C<goalname> to get the name of the goal, ie:
188
189  let goal foo () =
190    printf "my name is %s\n" goalname
191
192 would print:
193
194  my name is foo
195
196 =head2 goalloc
197
198 Inside goals, you can use C<goalloc> to get a printable source
199 location of the goal, ie:
200
201  let goal foo () =
202    printf "%s\n" goalloc
203
204 would print:
205
206  File "source.ml", line 2, characters 13-71 (end at line 3, character 23)
207
208 Note that the actual string format depends on the internal OCaml
209 function C<Loc.to_string> so it might change in future.
210
211 =head2 onfail, onsuccess, onrun
212
213 Inside goals you can register function(s) which run if the goal
214 completes successfully (C<onsuccess>), if the goal completes
215 successfully after running to the end (C<onrun>), or if the goal fails
216 (C<onfail>).
217
218 For example:
219
220  let goal built () =
221    onfail (fun _ -> eprintf "goal '%s' failed\n" goalname);
222    sh "
223      cc -o program main.o
224    "
225
226 If the shell command (or another part of the goal) fails, then this
227 would print out:
228
229  goal 'built' failed
230
231 The single parameter passed to C<onfail> is the exception that was
232 thrown.
233
234 Note that the helper function C<Goaljobs.mailto> is a useful function
235 to call from an C<onfail> handler:
236
237  let from = "me@example.com"
238  let to_ = "you@example.com"
239  let logfile = log_program_output ()
240  
241  let goal built () =
242    onfail (fun _ ->
243              let subject = sprintf "goal: %s: BUILD FAILED" goalname in
244              mailto ~from ~subject ~attach:[logfile] to_);
245    sh "
246      cc -o program main.o
247    "
248
249 C<onsuccess> and C<onrun> are slightly different from C<onfail> and
250 from each other:
251
252 C<onsuccess> functions can be called if a C<target> condition is met
253 and the rest of the goal is short-circuited.  C<onrun> will only be
254 called if all the instructions in the goal actually run and succeed.
255
256 The single unit C<()> parameter is passed to the C<onsuccess> and
257 C<onrun> functions.
258
259 You can register as many functions as you want for each handler.  The
260 order in which the functions are called is not defined.
261
262 =head1 PERIODIC JOBS
263
264 If you want to have a goal that runs when some outside event happens
265 you have three choices: Manually run the script (this is basically
266 what C<make> forces you to do).  Have some sort of hook that runs the
267 script (eg. a git hook).  Or use a periodic job to poll for an event
268 or change.
269
270 Periodic jobs run regularly to poll for an outside event or change.
271 If a script has periodic jobs, then it runs continuously (or until you
272 kill it).
273
274 An example of a script that checks for new git commits and when it
275 sees one it will ensure it passes the tests:
276
277  let repo = Sys.getenv "HOME" // "repo"
278  
279  let goal git_commit_tested commit =
280    let key = sprintf "repo-tested-%s" commit in
281    target (memory_exists key);
282  
283    sh "
284      git clone %s test
285      cd test
286      ./configure
287      make
288      make check
289    " repo_url;
290
291    (* Record that this commit was tested successfully. *)
292    memory_set key "1"
293  
294  every 30 minutes (fun () ->
295    let commit = shout "cd %s && git rev-parse HEAD" repo in
296    (* Require that this commit has been tested. *)
297    require (git_commit_tested commit)
298  )
299
300 Some notes about the above example: Firstly only the current HEAD
301 commit is required to be tested.  This is because older commits are
302 irrelevant and because if they failed the test before there is not
303 point retesting them (commits are immutable).  Secondly we use the
304 Memory to remember that we have successfully tested a commit.  This is
305 what stops the program from repeatedly testing the same commit.
306
307 =head1 SHELL
308
309 You can call out to the Unix shell using one of the functions
310 C<Goaljobs.sh>, C<Goaljobs.shout> or C<Goaljobs.shlines>.  (These
311 functions are documented in the C<goaljobs.mli> file / API
312 documentation.)
313
314 C<sh> runs the command(s).  C<shout> collects the output of the
315 command (to stdout only) and returns it as a single string.
316 C<shlines> collects the output and returns it as a list of lines.
317
318 C<sh>, C<shout>, C<shlines> work like printf.  ie. You can substitute
319 variables using C<%s>, C<%d> and so on.  For example:
320
321  sh "rsync foo-%s.tar.gz example.com:/html/" version
322
323 Each shell runs in a new temporary directory.  The temporary directory
324 and all its contents is deleted after the shell exits.  If you want to
325 save any data, C<cd> somewhere.  If you don't want the temporary
326 directory creation, use C<~tmpdir:false>.
327
328 The environment variable C<$builddir> is exported to the script.  This
329 is the current directory when the goaljobs program was started.
330
331 Each invocation of C<sh> (etc) is a single shell (this is slightly
332 different from how C<make> works).  For example:
333
334  sh "
335    package=foo-%s
336    tarball=$package.tar.gz
337    cp $HOME/$tarball .
338    tar zxf $tarball
339    cd $package
340    ./configure
341    make
342  " version
343
344 The shell error mode is set such that if any single command
345 returns an error then the C<sh> function as a whole exits with
346 an error.  Write:
347
348  command ||:
349
350 to ignore the result of a command.
351
352 C</bin/sh> is used unless you set C<Goaljobs.shell> to some other
353 value.  Note that the environment variable C<SHELL> is I<never> used.
354
355 =head1 THE MEMORY
356
357 "The Memory" is key/value storage which persists across goaljobs
358 sessions.  It is stored in the file C<$HOME/.goaljobs-memory> (which is
359 a binary file, but you can delete it if you want).
360
361 The Memory is locked during accesses, so it is safe to read or write
362 it from multiple parallel goaljobs sessions.
363
364 Keys and values are strings.  The keys should be globally unique, so
365 it is suggested you use some application-specific prefix.  eg:
366 C<myapp-key>
367
368 A common pattern is:
369
370 let goal tested version =
371   let key = "myapp-tested-" ^ version in
372   target (memory_exists key);
373  
374   (* some code to test this version *)
375  
376   memory_set key "1"
377
378 Note in that example the value C<1> is arbitrary.  You just want to
379 store I<any> value so that a later call to C<memory_exists> will
380 succeed.
381
382 For information about C<Goaljobs.memory_*> APIs see the
383 C<goaljobs.mli> file / API documentation.
384
385 =head1 FILES
386
387 =over 4
388
389 =item C</bin/sh>
390
391 This is the default shell used by C<sh*> APIs.  You can change
392 the shell by setting the C<Goaljobs.shell> reference.
393
394 =item C<curl>
395
396 The curl program (on the path) is used to check for and download
397 URLs by APIs such as C<Goaljobs.url_exists>.
398
399 =item C<~/.goaljobs-memory>
400
401 Persistent key/value store used when you use the C<Goaljobs.memory_*>
402 APIs.
403
404 =back
405
406 =head1 ENVIRONMENT VARIABLES
407
408 =over 4
409
410 =item B<builddir>
411
412 This environment variable is set to the current directory when the
413 goals program starts, and is available in goals, shell scripts, etc.
414
415 =back
416
417 =head1 SEE ALSO
418
419 L<goaljobs(1)>
420
421 =head1 AUTHORS
422
423 Richard W.M. Jones <rjones @ redhat . com>
424
425 =head1 COPYRIGHT
426
427 (C) Copyright 2013 Red Hat Inc.,
428
429 This program is free software; you can redistribute it and/or modify
430 it under the terms of the GNU General Public License as published by
431 the Free Software Foundation; either version 2 of the License, or
432 (at your option) any later version.
433
434 This program is distributed in the hope that it will be useful,
435 but WITHOUT ANY WARRANTY; without even the implied warranty of
436 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
437 GNU General Public License for more details.
438
439 You should have received a copy of the GNU General Public License
440 along with this program; if not, write to the Free Software
441 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.