X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=goaljobs.mli;h=43432f162a45c319a474574adc2256bcf02ce36e;hb=d6fddcd8789aa150d8c65d45a301df2a77a0686c;hp=3e1ca0c0d1aff08e15bb130cac990091c5a14895;hpb=e945605c6b1db3490c81a1434824151e8854dff6;p=goaljobs.git diff --git a/goaljobs.mli b/goaljobs.mli index 3e1ca0c..43432f1 100644 --- a/goaljobs.mli +++ b/goaljobs.mli @@ -16,45 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(** {1 Goaljobs library of useful helper functions.} *) +(** {1 Goaljobs library API} *) -(** {2 Target and require} - - These are used to write goals. - - Normally you write a goal with zero or one [target] and - zero or more [require]s, as the examples below should make - clear. - - In the first example, the target is that the [o_file] (object) exists - and is newer than the [c_file] (source). The goal - meets that target by running the C compiler ([cc]) which, if it - succeeds, will ensure that the object file exists and is newer - than the source file. - - {v - let goal compiled c_file = - let o_file = change_file_extension "o" c_file in - target (more_recent [o_file] [c_file]); - - sh "cd $builddir && cc -c %s -o %s" c_file o_file - v} - - In the second example, the goal requires that several files - have been compiled ([require (compiled ...)]) - before it can link the final program: - - {v - let goal built program source = - target (more_recent [program] [source]); - - require (compiled source); - - let object = change_file_extension "o" source in - sh "cd $builddir && cc %s -o %s" object program - v} - -*) +(** {2 Target and require} *) val target : bool -> unit (** [target] {i condition} defines the target condition that {b will} @@ -106,7 +70,7 @@ val target_exists : bool list -> unit (** [target_exists [t1; t2; ...]] is the same as writing [target (t1 || t2 || ...)] *) -val require : (unit -> unit) -> unit +val require : string -> (unit -> unit) -> unit (** [require] {i goal} defines the requirements of this goal, that is, other goals that have to be met before the rest of the goal is able to run. @@ -123,53 +87,7 @@ val require : (unit -> unit) -> unit placed anywhere within the goal, as long as you put them before they are needed. *) -(** {2 Periodic jobs} - - If you want to have a goal 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) - ) - v} - - 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. *) +(** {2 Periodic jobs} *) (* This is what lets you write '30 minutes' etc: *) type period_t = Seconds | Days | Months | Years @@ -269,49 +187,7 @@ val (//) : string -> string -> string val quote : string -> string (** Quote the string to make it safe to pass directly to the shell. *) -(** {2 Shell} - - Call out to the Unix shell. [/bin/sh] is used unless you set - {!shell} to some other value. Note that the environment variable - [SHELL] is {i not} used. - - {!sh}, {!shout}, {!shlines} work like [printf]. ie. You can - substitute variables using [%s], [%d] and so on. For example: - - {v - sh "rsync foo-%s.tar.gz example.com:/html/" version - v} - - 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. If you don't - want the temporary directory creation, use [~tmpdir:false]. - - The environment variable [$builddir] is exported to the script. - This is the current directory when the goaljobs program was started. - - Each invocation of {!sh} (etc) is a single shell (this is slightly - different from how [make] works). For example: - - {v - sh " - package=foo-%s - tarball=$package.tar.gz - cp $HOME/$tarball . - tar zxf $tarball - cd $package - ./configure - make - " version - v} - - The shell error mode is set such that if any single command - returns an error then the {!sh} function as a whole exits with - an error. Write: - {v command ||: v} - to ignore the result of a command. - -*) +(** {2 Shell} *) val sh : ?tmpdir:bool -> ('a, unit, string, unit) format4 -> 'a (** Run the command(s). @@ -370,34 +246,7 @@ val filter_file_extension : string -> string list -> string would return [["bar.o"]] (a single element list). *) *) -(** {2 Memory (persistent key/value storage)} - - "The Memory" is key/value storage which persists across goaljobs - sessions. It is stored in the file [$HOME/.goaljobs-memory] - (which is a binary file, but you can delete it if you want). - - The Memory is locked during accesses, so it is safe to read - or write it from multiple parallel goaljobs sessions. - - Keys and values are strings. The keys should be globally - unique, so it is suggested you use some application-specific - prefix. eg: "myapp-key" - - A common pattern is: - - {v - let goal tested version = - let key = "myapp-tested-" ^ version in - target (memory_exists key); - - ... some work to test version ... - - memory_set key "1" - v} - - Note in that example the value ["1"] is arbitrary. You just - want to store {i any} value so that a later call to {!memory_exists} - will succeed. *) +(** {2 Memory (persistent key/value storage)} *) val memory_exists : string -> bool (** [memory_exists key] checks that the named [key] exists in @@ -416,29 +265,7 @@ val memory_get : string -> string option val memory_delete : string -> unit (** Delete the [key]. If the key doesn't exist, has no effect. *) -(** {2 Publishing goals} - - To "publish" a goal means it's available on the command line - for users to use directly. - - Goals that have zero arguments are {b automatically published}. - So for example: - - {v - let goal clean () = sh "rm *~" - v} - - can be used on the command line: - - {v ./script clean v} - - The special goal called [all] (if it exists) is run implicitly - unless the user specifies another goal. Unlike [make], there is - nothing special about the first goal in the file. - - You can also publish goals, especially ones which take a non-zero - number of parameters, by calling {!publish}. -*) +(** {2 Publishing goals} *) val publish : string -> (string list -> unit) -> unit (** Publish the named goal. @@ -469,6 +296,28 @@ val publish : string -> (string list -> unit) -> unit the place to perform string to int conversion, checks, and so on (remember that OCaml is strongly typed). *) +(** {2 Logging script output} *) + +val log_program_output : unit -> string + (** [log_program_output] should be called at most once, usually at + the top-level of the script. It creates a temporary file + and redirects stdout and stderr into this file (they are still + sent to the ordinary output, so it acts like [tee]). The + filename of the temporary file is returned. *) + +(** {2 Sending email} *) + +val mailto : ?from:string -> subject:string -> ?attach:string list-> string -> unit + (** Send email. + + Optional [?from] is the sender email address. + + Required [~subject] is the subject line. + + Optional [?attach] is a list of attachments (filenames). + + The bare argument is the destination email address. *) + (**/**) (* Goal versions of some common functions. You are using these @@ -494,3 +343,7 @@ val init : unit -> unit (* Export this so the macros can catch these exceptions. *) type goal_result_t = Goal_OK | Goal_failed of string exception Goal_result of goal_result_t + +(* Called to print debug message when we enter or leave a goal. *) +val _enter_goal : string -> unit +val _leave_goal : string -> unit