summaryrefslogtreecommitdiffstats
path: root/ocamlbuild/command.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocamlbuild/command.ml')
-rw-r--r--ocamlbuild/command.ml295
1 files changed, 295 insertions, 0 deletions
diff --git a/ocamlbuild/command.ml b/ocamlbuild/command.ml
new file mode 100644
index 000000000..593b1f8d0
--- /dev/null
+++ b/ocamlbuild/command.ml
@@ -0,0 +1,295 @@
+(***********************************************************************)
+(* ocamlbuild *)
+(* *)
+(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *)
+(* *)
+(* Copyright 2007 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the Q Public License version 1.0. *)
+(* *)
+(***********************************************************************)
+
+(* $Id$ *)
+(* Original author: Nicolas Pouillard *)
+(* Command *)
+
+open My_std
+open Log
+
+type tags = Tags.t
+
+let jobs = ref 1
+
+type t =
+| Seq of t list
+| Cmd of spec
+| Nop
+and spec =
+| N (* nop or nil *)
+| S of spec list
+| A of string
+| P of string (* Pathname.t *)
+| Px of string (* Pathname.t *)
+| Sh of string
+| T of Tags.t
+| V of string
+| Quote of spec
+
+(*type v = [ `Seq of v list | `Cmd of vspec | `Nop ]
+and vspec =
+ [ `N
+ | `S of vspec list
+ | `A of string
+ | `P of string (* Pathname.t *)
+ | `Px of string (* Pathname.t *)
+ | `Sh of string
+ | `Quote of vspec ]
+
+let rec spec_of_vspec =
+ function
+ | `N -> N
+ | `S vspecs -> S (List.map spec_of_vspec vspecs)
+ | `A s -> A s
+ | `P s -> P s
+ | `Px s -> Px s
+ | `Sh s -> Sh s
+ | `Quote vspec -> Quote (spec_of_vspec vspec)
+
+let rec vspec_of_spec =
+ function
+ | N -> `N
+ | S specs -> `S (List.map vspec_of_spec specs)
+ | A s -> `A s
+ | P s -> `P s
+ | Px s -> `Px s
+ | Sh s -> `Sh s
+ | T _ -> invalid_arg "vspec_of_spec: T not supported"
+ | Quote spec -> `Quote (vspec_of_spec spec)
+
+let rec t_of_v =
+ function
+ | `Nop -> Nop
+ | `Cmd vspec -> Cmd (spec_of_vspec vspec)
+ | `Seq cmds -> Seq (List.map t_of_v cmds)
+
+let rec v_of_t =
+ function
+ | Nop -> `Nop
+ | Cmd spec -> `Cmd (vspec_of_spec spec)
+ | Seq cmds -> `Seq (List.map v_of_t cmds)*)
+
+let no_tag_handler _ = failwith "no_tag_handler"
+
+let tag_handler = ref no_tag_handler
+
+(*** atomize *)
+let atomize l = S(List.map (fun x -> A x) l)
+let atomize_paths l = S(List.map (fun x -> P x) l)
+(* ***)
+
+let env_path = lazy begin
+ let path_var = Sys.getenv "PATH" in
+ Lexers.colon_sep_strings (Lexing.from_string path_var)
+end
+
+let virtual_solvers = Hashtbl.create 32
+let setup_virtual_command_solver virtual_command solver =
+ Hashtbl.replace virtual_solvers virtual_command solver
+let virtual_solver virtual_command =
+ let solver =
+ try
+ Hashtbl.find virtual_solvers virtual_command
+ with Not_found ->
+ failwith (sbprintf "no solver for the virtual command %S \
+ (setup one with Command.setup_virtual_command_solver)"
+ virtual_command)
+ in
+ try solver ()
+ with Not_found ->
+ failwith (Printf.sprintf "the solver for the virtual command %S \
+ has failed finding a valid command" virtual_command)
+
+
+(* FIXME windows *)
+let search_in_path cmd =
+ if Filename.is_implicit cmd then
+ let path = List.find begin fun path ->
+ if path = Filename.current_dir_name then sys_file_exists cmd
+ else sys_file_exists (filename_concat path cmd)
+ end !*env_path in
+ filename_concat path cmd
+ else cmd
+
+(*** string_of_command_spec{,_with_calls *)
+let rec string_of_command_spec_with_calls call_with_tags call_with_target resolve_virtuals spec =
+ let self = string_of_command_spec_with_calls call_with_tags call_with_target resolve_virtuals in
+ let b = Buffer.create 256 in
+ let first = ref true in
+ let put_space () =
+ if !first then
+ first := false
+ else
+ Buffer.add_char b ' '
+ in
+ let put_filename p =
+ Buffer.add_string b (Shell.quote_filename_if_needed p)
+ in
+ let rec do_spec = function
+ | N -> ()
+ | A u -> put_space (); put_filename u
+ | Sh u -> put_space (); Buffer.add_string b u
+ | P p -> put_space (); put_filename p
+ | Px u -> put_space (); put_filename u; call_with_target u
+ | V v -> if resolve_virtuals then do_spec (virtual_solver v)
+ else (put_space (); Printf.bprintf b "<virtual %s>" (Shell.quote_filename_if_needed v))
+ | S l -> List.iter do_spec l
+ | T tags -> call_with_tags tags; do_spec (!tag_handler tags)
+ | Quote s -> put_space (); put_filename (self s)
+ in
+ do_spec spec;
+ Buffer.contents b
+
+let string_of_command_spec x = string_of_command_spec_with_calls ignore ignore false x
+
+let string_print_of_command_spec spec =
+ let rtags = ref Tags.empty in
+ let rtarget = ref "" in
+ let s = string_of_command_spec_with_calls ((:=) rtags) ((:=) rtarget) true spec in
+ let target = if !rtarget = "" then s else !rtarget in
+ (s, (fun quiet pretend () -> if not quiet then Log.event ~pretend s target !rtags))
+(* ***)
+
+let rec print f =
+ function
+ | Cmd spec -> Format.pp_print_string f (string_of_command_spec spec)
+ | Seq seq -> List.print print f seq
+ | Nop -> Format.pp_print_string f "nop"
+
+let to_string x = sbprintf "%a" print x
+
+let rec list_rev_iter f =
+ function
+ | [] -> ()
+ | x :: xs -> list_rev_iter f xs; f x
+
+let spec_list_of_cmd cmd =
+ let rec loop acc =
+ function
+ | [] -> acc
+ | Nop :: xs -> loop acc xs
+ | Cmd spec :: xs -> loop (string_print_of_command_spec spec :: acc) xs
+ | Seq l :: xs -> loop (loop acc l) xs
+ in List.rev (loop [] [cmd])
+
+let execute_many ?(quiet=false) ?(pretend=false) cmds =
+ let degraded = !*My_unix.is_degraded || Sys.os_type = "Win32" in
+ let jobs = !jobs in
+ if jobs < 0 then invalid_arg "jobs < 0";
+ let max_jobs = if jobs = 0 then None else Some jobs in
+
+ let ticker = Log.update in
+ let display = Log.display in
+
+ if cmds = [] then
+ None
+ else
+ begin
+ let konts =
+ List.map
+ begin fun cmd ->
+ let specs = spec_list_of_cmd cmd in
+ List.map
+ begin fun (cmd, print) ->
+ (cmd, (print quiet pretend))
+ end
+ specs
+ end
+ cmds
+ in
+ if pretend then
+ begin
+ List.iter
+ begin fun l ->
+ List.iter
+ begin fun (_, f) -> f () end
+ l
+ end
+ konts;
+ None
+ end
+ else
+ begin
+ reset_filesys_cache ();
+ if degraded then
+ let res, opt_exn =
+ List.fold_left begin fun (acc_res, acc_exn) cmds ->
+ match acc_exn with
+ | None ->
+ begin try
+ List.iter begin fun (cmd, print) ->
+ print ();
+ let rc = sys_command cmd in
+ if rc <> 0 then begin
+ if not quiet then
+ eprintf "Exit code %d while executing this \
+ command:@\n%s" rc cmd;
+ raise (Exit_with_code rc)
+ end
+ end cmds;
+ true :: acc_res, None
+ with e -> false :: acc_res, Some e
+ end
+ | Some _ -> false :: acc_res, acc_exn
+ end ([], None) konts
+ in match opt_exn with
+ | Some(exn) -> Some(res, exn)
+ | None -> None
+ else
+ My_unix.execute_many ~ticker ?max_jobs ~display konts
+ end
+ end
+;;
+
+let execute ?quiet ?pretend cmd =
+ match execute_many ?quiet ?pretend [cmd] with
+ | Some(_, exn) -> raise exn
+ | _ -> ()
+
+let rec reduce x =
+ let rec self x acc =
+ match x with
+ | N -> acc
+ | A _ | Sh _ | P _ | Px _ | V _ -> x :: acc
+ | S l -> List.fold_right self l acc
+ | T tags -> self (!tag_handler tags) acc
+ | Quote s -> Quote (reduce s) :: acc in
+ match self x [] with
+ | [] -> N
+ | [x] -> x
+ | xs -> S xs
+
+let to_string_for_digest = to_string
+(*
+let to_string_for_digest x =
+ let rec cmd_of_spec =
+ function
+ | [] -> None
+ | N :: xs -> cmd_of_spec xs
+ | (A x | P x | P x) :: _ -> Some x
+ | Sh x :: _ ->
+ if Shell.is_simple_filename x then Some x
+ else None (* Sh"ocamlfind ocamlc" for example will not be digested. *)
+ | S specs1 :: specs2 -> cmd_of_spec (specs1 @ specs2)
+ | (T _ | Quote _) :: _ -> assert false in
+ let rec cmd_of_cmds =
+ function
+ | Nop | Seq [] -> None
+ | Cmd spec -> cmd_of_spec [spec]
+ | Seq (cmd :: _) -> cmd_of_cmds cmd in
+ let s = to_string x in
+ match cmd_of_cmds x with
+ | Some x ->
+ if sys_file_exists x then sprintf "(%S,%S)" s (Digest.file x)
+ else s
+ | None -> s
+*)