(**************************************************************************) |
(* Copyright 2014, 2015: *)
(* Sebastien Mondet <seb@mondet.org>, *)
(* Leonid Rozenberg <leonidr@gmail.com>, *)
(* Arun Ahuja <aahuja11@gmail.com>, *)
(* Jeff Hammerbacher <jeff.hammerbacher@gmail.com> *)
(* *)
(* Licensed under the Apache License, Version 2.0 (the "License"); *)
(* you may not use this file except in compliance with the License. *)
(* You may obtain a copy of the License at *)
(* *)
(* http://www.apache.org/licenses/LICENSE-2.0 *)
(* *)
(* Unless required by applicable law or agreed to in writing, software *)
(* distributed under the License is distributed on an "AS IS" BASIS, *)
(* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *)
(* implied. See the License for the specific language governing *)
(* permissions and limitations under the License. *)
(**************************************************************************) |
(** Manage external processes. *) |
open Ketrew_pervasives
open Ketrew_unix_io
module Exit_code = struct
type t = [
| `Exited of int
| `Signaled of int
| `Stopped of int
]
let to_string = function
| `Exited n -> fmt "exited:%d" n
| `Signaled n -> fmt "signaled:%d" n
| `Stopped n -> fmt "stopped:%d" n
let to_log exit_status = Log.s (to_string exit_status)
end
let exec ?(bin="") argl =
let command = (bin, Array.of_list argl) in
let process = Lwt_process.open_process_full command in
wrap_deferred ~on_exn:(fun e ->
Log.(s "Tarminating process: " % parens (
quote bin % s ", " % OCaml.list quote argl) @ verbose);
process#terminate;
Lwt.ignore_result process#close;
`Process (`Exec (bin, argl), `Exn e))
Lwt.(fun () ->
Lwt_list.map_p Lwt_io.read
[process#stdout; process#stderr; ]
>>= fun output_2list ->
process#close >>= fun status ->
return (status, output_2list))
>>= fun (ret, output_2list) ->
let code =
match ret with
| Lwt_unix.WEXITED n -> (`Exited n)
| Lwt_unix.WSIGNALED n -> (`Signaled n)
| Lwt_unix.WSTOPPED n -> (`Stopped n)
in
begin match output_2list with
| [out; err] -> return (out, err, code)
| _ -> assert false
end
let succeed ?(bin="") argl =
exec ~bin argl
>>= fun (out, err, status) ->
let failure fmt =
Printf.ksprintf (fun s -> fail (`Process (`Exec (bin, argl), `Non_zero s)))
fmt in
begin match status with
| `Exited 0 -> return (out, err)
| code -> failure "%s" (Exit_code.to_string code)
end
let error_to_string = function
| `Process (`Exec (bin, cmd), how) ->
fmt "Executing %S[%s]: %s"
bin (String.concat ~sep:", " (List.map cmd ~f:(fmt "%S")))
(match how with
| `Exn e -> fmt "Exn %s" @@ Printexc.to_string e
| `Non_zero s -> fmt "Non-zero: %s" s)