From: Richard W.M. Jones Date: Tue, 13 Mar 2012 18:56:59 +0000 (+0000) Subject: Add support for multiple jobs files. X-Git-Tag: 0.7.0~1 X-Git-Url: http://git.annexia.org/?p=whenjobs.git;a=commitdiff_plain;h=77707acb12b5424488757569f376e1d9b58e5a22 Add support for multiple jobs files. --- diff --git a/daemon/daemon.ml b/daemon/daemon.ml index e18574d..c04bfcb 100644 --- a/daemon/daemon.ml +++ b/daemon/daemon.ml @@ -99,7 +99,7 @@ let rec init j d = and proc_reload_file () = if !debug then Syslog.notice "remote call: reload_file"; - try reload_file (); `ok + try reload_files (); `ok with Failure err -> `error err and proc_set_variable (name, value) = @@ -290,9 +290,18 @@ and proc_whisper_variables vars = with Failure msg -> `error msg -(* Reload the jobs file. *) -and reload_file () = - let file = sprintf "%s/jobs.cmo" !jobsdir in +(* Reload the jobs file(s). *) +and reload_files () = + (* Get dir/*.cmo *) + let dir = !jobsdir in + let files = Array.to_list (Sys.readdir dir) in + let files = List.filter ( + fun file -> + let n = String.length file in + n >= 5 && String.sub file (n-4) 4 = ".cmo" + ) files in + let files = List.map (fun file -> dir // file) files in + let files = List.sort compare files in (* As we are reloading the file, we want to create a new state * that has no jobs, but has all the variables from the previous @@ -303,9 +312,10 @@ and reload_file () = let s = try - Dynlink.loadfile file; + List.iter Dynlink.loadfile files; let s = Whenfile.get_state () in - Syslog.notice "loaded %d job(s) from %s" (Whenstate.nr_jobs s) file; + Syslog.notice "loaded %d job(s) from %d file(s)" + (Whenstate.nr_jobs s) (List.length files); s with | Dynlink.Error err -> diff --git a/daemon/daemon.mli b/daemon/daemon.mli index d860714..b7f7d57 100644 --- a/daemon/daemon.mli +++ b/daemon/daemon.mli @@ -24,8 +24,8 @@ val init : string -> bool -> unit The parameters are [jobsdir] and [debug]. *) -val reload_file : unit -> unit - (** (Re-)load the file [$jobsdir/jobs.cmo]. +val reload_files : unit -> unit + (** (Re-)load the file(s) [$jobsdir/*.cmo]. This can raise [Failure] if the operation fails. *) diff --git a/daemon/whenjobsd.ml b/daemon/whenjobsd.ml index 1efde6c..647c7f2 100644 --- a/daemon/whenjobsd.ml +++ b/daemon/whenjobsd.ml @@ -130,7 +130,7 @@ Options: let () = let file = sprintf "%s/jobs.cmo" jobsdir in if Sys.file_exists file then - try Daemon.reload_file () with Failure _ -> () in + try Daemon.reload_files () with Failure _ -> () in (* Go into main loop. *) Daemon.main_loop () diff --git a/daemon/whenjobsd.pod b/daemon/whenjobsd.pod index 182a62d..7139cb3 100644 --- a/daemon/whenjobsd.pod +++ b/daemon/whenjobsd.pod @@ -80,11 +80,15 @@ This contains the process ID of the daemon. The daemon also holds an advisory (L-style) exclusive lock on this file while it is running. -=item C<$HOME/.whenjobs/jobs.cmo> +=item C<$HOME/.whenjobs/*.cmo> -This is the compiled jobs specification which the daemon loads on +The compiled jobs specification file(s) which the daemon loads on start up, or reloads when instructed to by the L tool. +Normally you only have one, called C, corresponding to the +source file C which is edited by C. It is +possible to have multiple files, see L. + =item C<$HOME/.whenjobs/socket> The daemon creates this socket and listens for incoming connections diff --git a/tools/whenjobs.ml b/tools/whenjobs.ml index 1334aec..9449abc 100644 --- a/tools/whenjobs.ml +++ b/tools/whenjobs.ml @@ -307,26 +307,39 @@ and list_file () = close_in chan and upload_file () = - (* Recompile the jobs file. *) - let file = get_jobs_filename () in - let cmo_file = sprintf "%s/jobs.cmo" jobsdir in - let cmd = sprintf "ocamlfind ocamlc -I +camlp4 -I %s -package unix,camlp4.lib -pp 'camlp4o %s/pa_when.cmo' -c %s -o %s" - !libdir !libdir file cmo_file in - if Sys.command cmd <> 0 then ( - eprintf "whenjobs: could not compile jobs script, see earlier error messages\n"; - eprintf "compile command was:\n%s\n" cmd; - exit 1 - ); + (* Recompile the jobs file(s). *) + let files = get_multijobs_filenames () in + List.iter ( + fun file -> + let cmd = sprintf "ocamlfind ocamlc -I +camlp4 -I %s -package unix,camlp4.lib -pp 'camlp4o %s/pa_when.cmo' -c %s" + !libdir !libdir file in + if Sys.command cmd <> 0 then ( + eprintf "whenjobs: %s: could not compile jobs script, see earlier errors\n" + file; + eprintf "compile command was:\n%s\n" cmd; + exit 1 + ) + ) files; + + let cmo_files = List.map ( + fun file -> + let n = String.length file in + if n < 4 then assert false; + String.sub file 0 (n-3) ^ ".cmo" + ) files in - (* Test-load the jobs file to ensure it makes sense. *) + (* Test-load the jobs files to ensure they make sense. *) Whenfile.init Whenstate.empty; (try - Dynlink.loadfile cmo_file + List.iter Dynlink.loadfile cmo_files with Dynlink.Error err -> eprintf "whenjobs: %s\n" (Dynlink.error_message err); - (* Since it failed, unlink it. *) - (try unlink cmo_file with Unix_error _ -> ()); + (* Since it failed, unlink the cmo files. *) + List.iter ( + fun cmo_file -> + (try unlink cmo_file with Unix_error _ -> ()) + ) cmo_files; exit 1 ); @@ -555,6 +568,17 @@ and suggest_check_server_logs () = and get_jobs_filename () = sprintf "%s/jobs.ml" jobsdir +and get_multijobs_filenames () = + (* Get dir/*.ml *) + let files = Array.to_list (Sys.readdir jobsdir) in + let files = List.filter ( + fun file -> + let n = String.length file in + n >= 4 && String.sub file (n-3) 3 = ".ml" + ) files in + let files = List.map (fun file -> jobsdir // file) files in + List.sort compare files + and create_tutorial file = let chan = open_out file in output_string chan Tutorial.tutorial; diff --git a/tools/whenjobs.pod b/tools/whenjobs.pod index 6d2a554..1f38d29 100644 --- a/tools/whenjobs.pod +++ b/tools/whenjobs.pod @@ -252,11 +252,13 @@ The output is a list of job names that would run. =item B<--upload> -Compile the jobs script and upload it to the daemon, without editing. +Compile the jobs file(s) and upload it to the daemon, without editing. Note that the I<--edit> option does this automatically. Furthermore, when the daemon is started it checks for a jobs script and loads it if found. +See also L below. + =item B<--variables> Display all the variables and their values, in the format C. @@ -860,6 +862,57 @@ fields: =back +=head1 MULTIPLE JOBS FILES + +The whenjobs I<-e> and I<-l> options edit and list a file called +C<$HOME/.whenjobs/jobs.ml>. This is an OCaml source file which is +compiled behind the scenes into a bytecode file called +C<$HOME/.whenjobs/jobs.cmo>. C is what the daemon normally +loads. + +You can also edit C<$HOME/.whenjobs/jobs.ml> by other means (eg. your +own editor). After editing, to recompile and upload it, use: + + whenjobs --upload + +When you have lots of jobs, it is convenient to split the jobs across +multiple files. Any C<*.ml> files located in C<$HOME/.whenjobs> can +be used (with some restrictions on filenames -- see below). These are +compiled to the corresponding C<*.cmo> files and loaded into the +daemon using the I<--upload> command. + +To create multiple jobs files, you cannot use the I<-e> or I<-l> +options. Instead you have to create them yourself in +C<$HOME/.whenjobs>, and when you have finished creating or editing +them, upload them. + +=head2 FILENAME RESTRICTIONS ON JOBS FILES + +In OCaml, a file called C corresponds to an OCaml module +called C (note the capitalization). OCaml module names can only +contain ASCII alphanumeric characters, underscore, and C<'> (single +quote), and they must begin with an alphabetic character. The same +rules apply to jobs files. + +Furthermore, various OCaml module names are reserved (eg. C, +C). It is therefore better to prefix any names with something +specific to your application. + +Examples of legal filenames are: + + foo.ml + app_foo.ml + app_123.ml + jobs.ml + +Examples of illegal filenames are: + + ann.txt # must end with .ml + 123.ml # must begin with alphabetic + app!.ml # must contain alphanumeric or underscore + app-foo.ml # must contain alphanumeric or underscore + map.ml # reserved module name + =head1 FILES