Implement: onfail, onsuccess, onrun, log_program_output, mailto.
[goaljobs.git] / goaljobs.mli
1 (* goaljobs
2  * Copyright (C) 2013 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *)
18
19 (** {1 Goaljobs library API} *)
20
21 (** {2 Target and require} *)
22
23 val target : bool -> unit
24   (** [target] {i condition} defines the target condition that {b will}
25       be met once the current goal has run.
26
27       You can think of the target as a promise or contract that you
28       make, which is met by running the rest of the goal.
29
30       Goaljobs is much more flexible than [make].  In [make] only a
31       single type of target is possible.  The following are roughly
32       equivalent:
33
34       {v
35       foo.o: foo.c
36         ...
37       v}
38
39       {v
40       let goal compiled () =
41         target (more_recent ["foo.o"] ["foo.c"]);
42         require (file_exists "foo.c");
43         ...
44       v}
45
46       Targets in goaljobs can be any arbitrary expression.  For
47       example, it can access network resources or test URLs.
48
49       Almost every goal should have one target, which should
50       accurately state the outcome once the goal has been run.
51
52       It is possible to have no target.  This means the goal
53       always runs (like using "force" in make).
54
55       You should not have multiple targets in a single goal.  They
56       won't work the way you expect, and future versions of goaljobs
57       will likely stop you from doing this.
58
59       Normally you put the target(s) early on in the goal, before any
60       running code and before any [require]s.  This is not a
61       hard-and-fast rule and it is not enforced, but doing it will
62       ensure the goal runs most efficiently since if the target is met
63       already then the rest of the goal doesn't run. *)
64
65 val target_all : bool list -> unit
66   (** [target_all [t1; t2; ...]] is the same as writing
67       [target (t1 && t2 && ...)] *)
68
69 val target_exists : bool list -> unit
70   (** [target_exists [t1; t2; ...]] is the same as writing
71       [target (t1 || t2 || ...)] *)
72
73 val require : (unit -> unit) -> unit
74   (** [require] {i goal} defines the requirements of this goal, that
75       is, other goals that have to be met before the rest of the
76       goal is able to run.
77
78       In terms of [make], [require]s are roughly equivalent to the
79       right hand side after the [:], but in goaljobs the requirements
80       can be much richer than simply "that file must exist".
81
82       Some very simple goals don't need any [require]s.  You can
83       have as many [require]s as you need in a goal, and you can
84       use a loop or make them conditional if you want.
85
86       Unlike [make], the requirements of a goal can be
87       placed anywhere within the goal, as long as you put them
88       before they are needed. *)
89
90 (** {2 Periodic jobs} *)
91
92 (* This is what lets you write '30 minutes' etc: *)
93 type period_t = Seconds | Days | Months | Years
94 val seconds : int * period_t
95 val sec : int * period_t
96 val secs : int * period_t
97 val second : int * period_t
98 val minutes : int * period_t
99 val min : int * period_t
100 val mins : int * period_t
101 val minute : int * period_t
102 val hours : int * period_t
103 val hour : int * period_t
104 val days : int * period_t
105 val day : int * period_t
106 val weeks : int * period_t
107 val week : int * period_t
108 val months : int * period_t
109 val month : int * period_t
110 val years : int * period_t
111 val year : int * period_t
112
113 val every : ?name:string -> int -> int * period_t -> (unit -> unit) -> unit
114   (** [every N (seconds|minutes|hours|days|weeks|months|years) f]
115       runs the function [f] periodically.
116
117       The optional [~name] parameter can be used to name the job
118       (for debugging). *)
119
120 (** {2 File and URL testing}
121
122     Various functions to test the existence of files, URLs.
123 *)
124
125 val file_exists : string -> bool
126   (** Return true if the named file exists.
127
128       This function also exists as a goal.  Writing:
129       {v require (file_exists "somefile"); v}
130       will die unless ["somefile"] exists. *)
131
132 val directory_exists : string -> bool
133   (** Return true if the named directory exists.
134
135       There is also a goal version of this function. *)
136
137 val file_newer_than : string -> string -> bool
138   (** [file_newer_than file_a file_b] returns true if [file_a] is
139       newer than [file_b].  Note that if [file_a] does not exist, it
140       returns false.  If [file_b] does not exist, it is an error.
141
142       There is also a goal version of this function. *)
143
144 val more_recent : string list -> string list -> bool
145   (** [more_recent objects sources] expresses the [make] relationship:
146
147       {v object(s) ...: source(s) ... v}
148
149       in a convenient way:
150
151       {v
152       let goal built objects sources =
153         target (more_recent objects sources);
154         ... code to rebuild ...
155       v}
156
157       It is roughly equivalent to checking that all the object files
158       exist and are newer than all of the source files.
159
160       Note that both parameters are lists (since in [make] you can
161       have a list of source files and a list of object files).  If you
162       don't want a list, pass a single-element list containing the
163       single the object/source file.
164
165       There is also a goal version of this function. *)
166
167 val url_exists : string -> bool
168   (** The URL is tested to see if it exists.
169
170       There is also a goal version of this function. *)
171
172 val file_contains_string : string -> string -> bool
173   (** [file_contains_string filename str] checks if the named file
174       contains the given substring [str].
175
176       There is also a goal version of this function. *)
177
178 val url_contains_string : string -> string -> bool
179   (** [url_contains_string url str] downloads the URL and checks
180       whether the content contains the given substring [str].
181
182       There is also a goal version of this function. *)
183
184 val (//) : string -> string -> string
185   (** Concatenate two paths. *)
186
187 val quote : string -> string
188   (** Quote the string to make it safe to pass directly to the shell. *)
189
190 (** {2 Shell} *)
191
192 val sh : ?tmpdir:bool -> ('a, unit, string, unit) format4 -> 'a
193   (** Run the command(s).
194
195       The command runs in a newly created temporary directory (which
196       is deleted after the command exits), {i unless} you use
197       [~tmpdir:false]. *)
198
199 val shout : ?tmpdir:bool -> ('a, unit, string, string) format4 -> 'a
200   (** Run the command(s).
201
202       Anything printed on stdout is returned as a string.
203       The trailing [\n] character, if any, is not returned.
204
205       The command runs in a newly created temporary directory (which
206       is deleted after the command exits), {i unless} you use
207       [~tmpdir:false]. *)
208
209 val shlines : ?tmpdir:bool -> ('a, unit, string, string list) format4 -> 'a
210   (** Run the command(s).
211
212       Any lines printed to stdout is returned as a list of strings.
213       Trailing [\n] characters are not returned.
214
215       The command runs in a newly created temporary directory (which
216       is deleted after the command exits), {i unless} you use
217       [~tmpdir:false]. *)
218
219 val shell : string ref
220   (** Set this variable to override the default shell ([/bin/sh]). *)
221
222 (** {2 String functions}
223
224     Most string functions are provided by the OCaml standard
225     library (see the module [String]).  For convenience some
226     extra functions are provided here. *)
227
228 (*
229 val replace_substring : string -> string -> string -> string
230   (** [replace_substring patt repl string] replaces all occurrences
231       of [patt] with [repl] in [string]. *)
232 *)
233
234 val change_file_extension : string -> string -> string
235   (** [change_file_extension ext filename] changes the file extension
236       of [filename] to [.ext].  For example
237       [change_file_extension "o" "main.c"] returns ["main.o"].
238       If the original filename has no extension, this function
239       adds the extension. *)
240
241 (*
242 val filter_file_extension : string -> string list -> string
243   (** [filter_file_extension ext filenames] returns only those
244       filenames in the list which have the given file extension.
245       For example [filter_file_extension "o" ["foo.c"; "bar.o"]]
246       would return [["bar.o"]] (a single element list). *)
247 *)
248
249 (** {2 Memory (persistent key/value storage)} *)
250
251 val memory_exists : string -> bool
252   (** [memory_exists key] checks that the named [key] exists in
253       the Memory.  It doesn't matter what value it has.
254
255       This is also available as a goal, so you can write
256       [require (memory_exists key)] *)
257
258 val memory_set : string -> string -> unit
259   (** Set [key] to [value] in the Memory. *)
260
261 val memory_get : string -> string option
262   (** Return the current value of [key] in the Memory.  Returns [None]
263       if the key has never been set or was deleted. *)
264
265 val memory_delete : string -> unit
266   (** Delete the [key].  If the key doesn't exist, has no effect. *)
267
268 (** {2 Publishing goals} *)
269
270 val publish : string -> (string list -> unit) -> unit
271   (** Publish the named goal.
272
273       Use this function as in this example:
274
275       {v
276       let goal compiled program sources =
277         ... stuff for building the program from sources ...
278
279       let () = publish "compiled" (
280         fun args ->
281           let program = List.hd args in
282           let sources = List.tl args in
283           require (compiled program sources)
284       )
285       v}
286
287       This could be used as follows:
288
289       {v ./script compiled program main.c utils.c v}
290
291       You will notice you have to write a bit of OCaml code to
292       map the string arguments from the command line on to the
293       goal arguments.  In the example it means taking the first
294       string argument as the program name, and the rest of the
295       string arguments as the source filenames.  This is also
296       the place to perform string to int conversion, checks, and
297       so on (remember that OCaml is strongly typed). *)
298
299 (** {2 Logging script output} *)
300
301 val log_program_output : unit -> string
302   (** [log_program_output] should be called at most once, usually at
303       the top-level of the script.  It creates a temporary file
304       and redirects stdout and stderr into this file (they are still
305       sent to the ordinary output, so it acts like [tee]).  The
306       filename of the temporary file is returned. *)
307
308 (** {2 Sending email} *)
309
310 val mailto : ?from:string -> subject:string -> ?attach:string list-> string -> unit
311   (** Send email.
312
313       Optional [?from] is the sender email address.
314
315       Required [~subject] is the subject line.
316
317       Optional [?attach] is a list of attachments (filenames).
318
319       The bare argument is the destination email address. *)
320
321 (**/**)
322
323 (* Goal versions of some common functions.  You are using these
324  * versions when you write something like:
325  *   require (file_exists "foo");
326  * They work the same way as the regular function, except they die
327  * if the predicate returns false.
328  *)
329 val goal_file_exists : string -> unit
330 val goal_directory_exists : string -> unit
331 val goal_file_newer_than : string -> string -> unit
332 val goal_more_recent : string list -> string list -> unit
333 val goal_url_exists : string -> unit
334 val goal_file_contains_string : string -> string -> unit
335 val goal_url_contains_string : string -> string -> unit
336 val goal_memory_exists : string -> unit
337
338 (* A single call to this function is added by the 'goaljobs' script.
339  * It is responsible for parsing the command line and so on.
340  *)
341 val init : unit -> unit
342
343 (* Export this so the macros can catch these exceptions. *)
344 type goal_result_t = Goal_OK | Goal_failed of string
345 exception Goal_result of goal_result_t