|
|
@ -9,30 +9,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
open! Utils
|
|
|
|
open! Utils
|
|
|
|
|
|
|
|
|
|
|
|
(** Prints information about a unix error *)
|
|
|
|
module Pid = Core.Std.Pid
|
|
|
|
let print_unix_error cmd e =
|
|
|
|
|
|
|
|
match e with
|
|
|
|
module L = Logging
|
|
|
|
| Unix.Unix_error(err, _, _) ->
|
|
|
|
module F = Format
|
|
|
|
Logging.err "Cannot execute %s : %s\n%!"
|
|
|
|
|
|
|
|
cmd (Unix.error_message err)
|
|
|
|
|
|
|
|
| _ -> ()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(** Prints an error message to a log file, prints a message saying that the error can be
|
|
|
|
(** Prints an error message to a log file, prints a message saying that the error can be
|
|
|
|
found in that file, and exits, with default code 1 or a given code. *)
|
|
|
|
found in that file, and exits, with default code 1 or a given code. *)
|
|
|
|
let print_error_and_exit ?(exit_code=1) fmt =
|
|
|
|
let print_error_and_exit ?(exit_code=1) fmt =
|
|
|
|
Format.kfprintf (fun _ ->
|
|
|
|
F.kfprintf (fun _ ->
|
|
|
|
Logging.do_err "%s" (Format.flush_str_formatter ());
|
|
|
|
L.do_err "%s" (F.flush_str_formatter ());
|
|
|
|
let log_file = snd (Logging.log_file_names ()) in
|
|
|
|
let log_file = snd (L.log_file_names ()) in
|
|
|
|
Logging.stderr "@\nAn error occured. Please find details in %s@\n@\n%!" log_file;
|
|
|
|
L.stderr "@\nAn error occured. Please find details in %s@\n@\n%!" log_file;
|
|
|
|
exit exit_code
|
|
|
|
exit exit_code
|
|
|
|
)
|
|
|
|
)
|
|
|
|
Format.str_formatter fmt
|
|
|
|
F.str_formatter fmt
|
|
|
|
|
|
|
|
|
|
|
|
(** Executes a command and catches a potential exception and prints it. *)
|
|
|
|
|
|
|
|
let exec_command ~prog ~args env =
|
|
|
|
|
|
|
|
try Unix.execve prog (Array.of_list (prog :: args)) env
|
|
|
|
|
|
|
|
with (Unix.Unix_error _ as e) ->
|
|
|
|
|
|
|
|
print_unix_error (String.concat ~sep:" " (prog :: args)) e
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(** Given a command to be executed, create a process to execute this command, and wait for it to
|
|
|
|
(** Given a command to be executed, create a process to execute this command, and wait for it to
|
|
|
|
terminate. The standard out and error are not redirected. If the command fails to execute,
|
|
|
|
terminate. The standard out and error are not redirected. If the command fails to execute,
|
|
|
@ -51,40 +42,35 @@ let create_process_and_wait ~prog ~args =
|
|
|
|
(** Given a process id and a function that describes the command that the process id
|
|
|
|
(** Given a process id and a function that describes the command that the process id
|
|
|
|
represents, prints a message explaining the command and its status, if in debug or stats mode.
|
|
|
|
represents, prints a message explaining the command and its status, if in debug or stats mode.
|
|
|
|
It also prints a dot to show progress of jobs being finished. *)
|
|
|
|
It also prints a dot to show progress of jobs being finished. *)
|
|
|
|
let print_status f pid (status : Unix. process_status) =
|
|
|
|
let print_status f pid status =
|
|
|
|
if Config.debug_mode || Config.stats_mode then
|
|
|
|
let open Core.Std in
|
|
|
|
(let program = f pid in
|
|
|
|
L.err "%a%s@."
|
|
|
|
match status with
|
|
|
|
(fun fmt pid -> F.pp_print_string fmt (f pid)) pid
|
|
|
|
| WEXITED status ->
|
|
|
|
(Unix.Exit_or_signal.to_string_hum status) ;
|
|
|
|
if status = 0 then
|
|
|
|
L.stdout ".%!"
|
|
|
|
Logging.out "%s OK \n%!" program
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
Logging.err "%s exited with code %d\n%!" program status
|
|
|
|
|
|
|
|
| WSIGNALED signal ->
|
|
|
|
|
|
|
|
Logging.err "%s killed by signal %d\n%!" program signal
|
|
|
|
|
|
|
|
| WSTOPPED _ ->
|
|
|
|
|
|
|
|
Logging.err "%s stopped \n%!" program);
|
|
|
|
|
|
|
|
Logging.stdout ".%!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let start_current_jobs_count () = ref 0
|
|
|
|
let start_current_jobs_count () = ref 0
|
|
|
|
|
|
|
|
|
|
|
|
let waited_for_jobs = ref 0
|
|
|
|
let waited_for_jobs = ref 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module PidMap = Map.Make (Pid)
|
|
|
|
|
|
|
|
|
|
|
|
(** [wait_for_son pid_child f jobs_count] wait for pid_child
|
|
|
|
(** [wait_for_son pid_child f jobs_count] wait for pid_child
|
|
|
|
and all the other children and update the current jobs count.
|
|
|
|
and all the other children and update the current jobs count.
|
|
|
|
Use f to print the job status *)
|
|
|
|
Use f to print the job status *)
|
|
|
|
let rec wait_for_child f current_jobs_count jobs_map =
|
|
|
|
let rec wait_for_child f current_jobs_count jobs_map =
|
|
|
|
let pid, status = Unix.wait () in
|
|
|
|
let open! Core.Std in
|
|
|
|
|
|
|
|
let pid, status = Unix.wait `Any in
|
|
|
|
Pervasives.decr current_jobs_count;
|
|
|
|
Pervasives.decr current_jobs_count;
|
|
|
|
Pervasives.incr waited_for_jobs;
|
|
|
|
Pervasives.incr waited_for_jobs;
|
|
|
|
print_status f pid status;
|
|
|
|
print_status f pid status;
|
|
|
|
jobs_map := IntMap.remove pid !jobs_map;
|
|
|
|
jobs_map := PidMap.remove pid !jobs_map;
|
|
|
|
if not (IntMap.is_empty !jobs_map) then
|
|
|
|
if not (PidMap.is_empty !jobs_map) then
|
|
|
|
wait_for_child f current_jobs_count jobs_map
|
|
|
|
wait_for_child f current_jobs_count jobs_map
|
|
|
|
|
|
|
|
|
|
|
|
let pid_to_program jobsMap pid =
|
|
|
|
let pid_to_program jobsMap pid =
|
|
|
|
try
|
|
|
|
try
|
|
|
|
IntMap.find pid jobsMap
|
|
|
|
PidMap.find pid jobsMap
|
|
|
|
with Not_found -> ""
|
|
|
|
with Not_found -> ""
|
|
|
|
|
|
|
|
|
|
|
|
(** [run_jobs_in_parallel jobs_stack gen_prog prog_to_string] runs the jobs in the given stack, by
|
|
|
|
(** [run_jobs_in_parallel jobs_stack gen_prog prog_to_string] runs the jobs in the given stack, by
|
|
|
@ -93,27 +79,28 @@ let pid_to_program jobsMap pid =
|
|
|
|
env)] where [dir_opt] is an optional directory to chdir to before executing [command] with
|
|
|
|
env)] where [dir_opt] is an optional directory to chdir to before executing [command] with
|
|
|
|
[args] in [env]. [prog_to_string] is used for printing information about the job's status. *)
|
|
|
|
[args] in [env]. [prog_to_string] is used for printing information about the job's status. *)
|
|
|
|
let run_jobs_in_parallel jobs_stack gen_prog prog_to_string =
|
|
|
|
let run_jobs_in_parallel jobs_stack gen_prog prog_to_string =
|
|
|
|
|
|
|
|
let open! Core.Std in
|
|
|
|
let run_job () =
|
|
|
|
let run_job () =
|
|
|
|
let jobs_map = ref IntMap.empty in
|
|
|
|
let jobs_map = ref PidMap.empty in
|
|
|
|
let current_jobs_count = start_current_jobs_count () in
|
|
|
|
let current_jobs_count = start_current_jobs_count () in
|
|
|
|
while not (Stack.is_empty jobs_stack) do
|
|
|
|
while not (Caml.Stack.is_empty jobs_stack) do
|
|
|
|
let job_prog = Stack.pop jobs_stack in
|
|
|
|
let job_prog = Caml.Stack.pop jobs_stack in
|
|
|
|
let (dir_opt, prog, args, env) = gen_prog job_prog in
|
|
|
|
let (dir_opt, prog, args, env) = gen_prog job_prog in
|
|
|
|
Pervasives.incr current_jobs_count;
|
|
|
|
Pervasives.incr current_jobs_count;
|
|
|
|
match Unix.fork () with
|
|
|
|
match Unix.fork () with
|
|
|
|
| 0 ->
|
|
|
|
| `In_the_child ->
|
|
|
|
(match dir_opt with
|
|
|
|
Core.Std.Option.iter dir_opt ~f:Unix.chdir ;
|
|
|
|
| Some dir -> Unix.chdir dir
|
|
|
|
Unix.exec ~prog ~args:(prog :: args) ~env ~use_path:false
|
|
|
|
| None -> ());
|
|
|
|
|> Unix.handle_unix_error
|
|
|
|
exec_command ~prog ~args env
|
|
|
|
|> never_returns
|
|
|
|
| pid_child ->
|
|
|
|
| `In_the_parent pid_child ->
|
|
|
|
jobs_map := IntMap.add pid_child (prog_to_string job_prog) !jobs_map;
|
|
|
|
jobs_map := PidMap.add pid_child (prog_to_string job_prog) !jobs_map;
|
|
|
|
if Stack.length jobs_stack = 0 || !current_jobs_count >= Config.jobs then
|
|
|
|
if Caml.Stack.length jobs_stack = 0 || !current_jobs_count >= Config.jobs then
|
|
|
|
wait_for_child (pid_to_program !jobs_map) current_jobs_count jobs_map
|
|
|
|
wait_for_child (pid_to_program !jobs_map) current_jobs_count jobs_map
|
|
|
|
done in
|
|
|
|
done in
|
|
|
|
run_job ();
|
|
|
|
run_job ();
|
|
|
|
Logging.stdout ".\n%!";
|
|
|
|
L.stdout ".@.";
|
|
|
|
Logging.out "Waited for %d jobs" !waited_for_jobs
|
|
|
|
L.out "Waited for %d jobs" !waited_for_jobs
|
|
|
|
|
|
|
|
|
|
|
|
let pipeline ~producer_prog ~producer_args ~consumer_prog ~consumer_args =
|
|
|
|
let pipeline ~producer_prog ~producer_args ~consumer_prog ~consumer_args =
|
|
|
|
let open Core.Std in
|
|
|
|
let open Core.Std in
|
|
|
|