2 * Copyright (C) 2013 Red Hat Inc.
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.
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.
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.
19 (** {1 Goaljobs library API} *)
21 (** {2 Target and require} *)
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.
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.
30 Goaljobs is much more flexible than [make]. In [make] only a
31 single type of target is possible. The following are roughly
40 let goal compiled () =
41 target (more_recent ["foo.o"] ["foo.c"]);
42 require (file_exists "foo.c");
46 Targets in goaljobs can be any arbitrary expression. For
47 example, it can access network resources or test URLs.
49 Almost every goal should have one target, which should
50 accurately state the outcome once the goal has been run.
52 It is possible to have no target. This means the goal
53 always runs (like using "force" in make).
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.
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. *)
65 val target_all : bool list -> unit
66 (** [target_all [t1; t2; ...]] is the same as writing
67 [target (t1 && t2 && ...)] *)
69 val target_exists : bool list -> unit
70 (** [target_exists [t1; t2; ...]] is the same as writing
71 [target (t1 || t2 || ...)] *)
73 val require : string -> (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
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".
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.
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. *)
90 (** {2 Periodic jobs} *)
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
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.
117 The optional [~name] parameter can be used to name the job
120 (** {2 File and URL testing}
122 Various functions to test the existence of files, URLs.
125 val file_exists : string -> bool
126 (** Return true if the named file exists.
128 This function also exists as a goal. Writing:
129 {v require (file_exists "somefile"); v}
130 will die unless ["somefile"] exists. *)
132 val directory_exists : string -> bool
133 (** Return true if the named directory exists.
135 There is also a goal version of this function. *)
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.
142 There is also a goal version of this function. *)
144 val more_recent : string list -> string list -> bool
145 (** [more_recent objects sources] expresses the [make] relationship:
147 {v object(s) ...: source(s) ... v}
152 let goal built objects sources =
153 target (more_recent objects sources);
154 ... code to rebuild ...
157 It is roughly equivalent to checking that all the object files
158 exist and are newer than all of the source files.
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.
165 There is also a goal version of this function. *)
167 val url_exists : string -> bool
168 (** The URL is tested to see if it exists.
170 There is also a goal version of this function. *)
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].
176 There is also a goal version of this function. *)
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].
182 There is also a goal version of this function. *)
184 val (//) : string -> string -> string
185 (** Concatenate two paths. *)
187 val quote : string -> string
188 (** Quote the string to make it safe to pass directly to the shell. *)
192 val sh : ?tmpdir:bool -> ('a, unit, string, unit) format4 -> 'a
193 (** Run the command(s).
195 The command runs in a newly created temporary directory (which
196 is deleted after the command exits), {i unless} you use
199 val shout : ?tmpdir:bool -> ('a, unit, string, string) format4 -> 'a
200 (** Run the command(s).
202 Anything printed on stdout is returned as a string.
203 The trailing [\n] character, if any, is not returned.
205 The command runs in a newly created temporary directory (which
206 is deleted after the command exits), {i unless} you use
209 val shlines : ?tmpdir:bool -> ('a, unit, string, string list) format4 -> 'a
210 (** Run the command(s).
212 Any lines printed to stdout is returned as a list of strings.
213 Trailing [\n] characters are not returned.
215 The command runs in a newly created temporary directory (which
216 is deleted after the command exits), {i unless} you use
219 val shell : string ref
220 (** Set this variable to override the default shell ([/bin/sh]). *)
222 (** {2 String functions}
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. *)
229 val replace_substring : string -> string -> string -> string
230 (** [replace_substring patt repl string] replaces all occurrences
231 of [patt] with [repl] in [string]. *)
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. *)
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). *)
249 (** {2 Memory (persistent key/value storage)} *)
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.
255 This is also available as a goal, so you can write
256 [require (memory_exists key)] *)
258 val memory_set : string -> string -> unit
259 (** Set [key] to [value] in the Memory. *)
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. *)
265 val memory_delete : string -> unit
266 (** Delete the [key]. If the key doesn't exist, has no effect. *)
268 val memory_list : unit -> (string * string) list
269 (** Return all [(key, value)] pairs in the memory. *)
271 (** {2 Publishing goals} *)
273 val publish : string -> (string list -> unit) -> unit
274 (** Publish the named goal.
276 Use this function as in this example:
279 let goal compiled program sources =
280 ... stuff for building the program from sources ...
282 let () = publish "compiled" (
284 let program = List.hd args in
285 let sources = List.tl args in
286 require (compiled program sources)
290 This could be used as follows:
292 {v ./script compiled program main.c utils.c v}
294 You will notice you have to write a bit of OCaml code to
295 map the string arguments from the command line on to the
296 goal arguments. In the example it means taking the first
297 string argument as the program name, and the rest of the
298 string arguments as the source filenames. This is also
299 the place to perform string to int conversion, checks, and
300 so on (remember that OCaml is strongly typed). *)
302 (** {2 Logging script output} *)
304 val log_program_output : unit -> string
305 (** [log_program_output] should be called at most once, usually at
306 the top-level of the script. It creates a temporary file
307 and redirects stdout and stderr into this file (they are still
308 sent to the ordinary output, so it acts like [tee]). The
309 filename of the temporary file is returned. *)
311 (** {2 Sending email} *)
313 val mailto : ?from:string -> subject:string -> ?attach:string list-> string -> unit
316 Optional [?from] is the sender email address.
318 Required [~subject] is the subject line.
320 Optional [?attach] is a list of attachments (filenames).
322 The bare argument is the destination email address. *)
326 (* Goal versions of some common functions. You are using these
327 * versions when you write something like:
328 * require (file_exists "foo");
329 * They work the same way as the regular function, except they die
330 * if the predicate returns false.
332 val goal_file_exists : string -> unit
333 val goal_directory_exists : string -> unit
334 val goal_file_newer_than : string -> string -> unit
335 val goal_more_recent : string list -> string list -> unit
336 val goal_url_exists : string -> unit
337 val goal_file_contains_string : string -> string -> unit
338 val goal_url_contains_string : string -> string -> unit
339 val goal_memory_exists : string -> unit
341 (* A single call to this function is added by the 'goaljobs' script.
342 * It is responsible for parsing the command line and so on.
344 val init : unit -> unit
346 (* Export this so the macros can catch these exceptions. *)
347 type goal_result_t = Goal_OK | Goal_failed of string
348 exception Goal_result of goal_result_t
350 (* Called to print debug message when we enter or leave a goal. *)
351 val _enter_goal : string -> unit
352 val _leave_goal : string -> unit