391 lines
10 KiB
OCaml
391 lines
10 KiB
OCaml
(***********************************************************************)
|
|
(* 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. *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
|
|
|
|
(* Original author: Nicolas Pouillard *)
|
|
open Format
|
|
|
|
exception Exit_OK
|
|
exception Exit_usage of string
|
|
exception Exit_system_error of string
|
|
exception Exit_with_code of int
|
|
exception Exit_silently_with_code of int
|
|
|
|
module Outcome = struct
|
|
type ('a,'b) t =
|
|
| Good of 'a
|
|
| Bad of 'b
|
|
|
|
let ignore_good =
|
|
function
|
|
| Good _ -> ()
|
|
| Bad e -> raise e
|
|
|
|
let good =
|
|
function
|
|
| Good x -> x
|
|
| Bad exn -> raise exn
|
|
|
|
let wrap f x =
|
|
try Good (f x) with e -> Bad e
|
|
|
|
end
|
|
|
|
let opt_print elt ppf =
|
|
function
|
|
| Some x -> fprintf ppf "@[<2>Some@ %a@]" elt x
|
|
| None -> pp_print_string ppf "None"
|
|
|
|
open Format
|
|
let ksbprintf g fmt =
|
|
let buff = Buffer.create 42 in
|
|
let f = formatter_of_buffer buff in
|
|
kfprintf (fun f -> (pp_print_flush f (); g (Buffer.contents buff))) f fmt
|
|
let sbprintf fmt = ksbprintf (fun x -> x) fmt
|
|
|
|
(** Some extensions of the standard library *)
|
|
module Set = struct
|
|
|
|
module type OrderedTypePrintable = sig
|
|
include Set.OrderedType
|
|
val print : formatter -> t -> unit
|
|
end
|
|
|
|
module type S = sig
|
|
include Set.S
|
|
val find : (elt -> bool) -> t -> elt
|
|
val map : (elt -> elt) -> t -> t
|
|
val of_list : elt list -> t
|
|
val print : formatter -> t -> unit
|
|
end
|
|
|
|
module Make (M : OrderedTypePrintable) : S with type elt = M.t = struct
|
|
include Set.Make(M)
|
|
exception Found of elt
|
|
let find p set =
|
|
try
|
|
iter begin fun elt ->
|
|
if p elt then raise (Found elt)
|
|
end set; raise Not_found
|
|
with Found elt -> elt
|
|
let map f set = fold (fun x -> add (f x)) set empty
|
|
let of_list l = List.fold_right add l empty
|
|
let print f s =
|
|
let () = fprintf f "@[<hv0>@[<hv2>{.@ " in
|
|
let _ =
|
|
fold begin fun elt first ->
|
|
if not first then fprintf f ",@ ";
|
|
M.print f elt;
|
|
false
|
|
end s true in
|
|
fprintf f "@]@ .}@]"
|
|
end
|
|
end
|
|
|
|
module List = struct
|
|
include List
|
|
let print pp_elt f ls =
|
|
fprintf f "@[<2>[@ ";
|
|
let _ =
|
|
fold_left begin fun first elt ->
|
|
if not first then fprintf f ";@ ";
|
|
pp_elt f elt;
|
|
false
|
|
end true ls in
|
|
fprintf f "@ ]@]"
|
|
|
|
let filter_opt f xs =
|
|
List.fold_right begin fun x acc ->
|
|
match f x with
|
|
| Some x -> x :: acc
|
|
| None -> acc
|
|
end xs []
|
|
|
|
let rec rev_append_uniq acc =
|
|
function
|
|
| [] -> acc
|
|
| x :: xs ->
|
|
if mem x acc then rev_append_uniq acc xs
|
|
else rev_append_uniq (x :: acc) xs
|
|
|
|
let union a b =
|
|
rev (rev_append_uniq (rev_append_uniq [] a) b)
|
|
|
|
end
|
|
|
|
module String = struct
|
|
include String
|
|
|
|
let print f s = fprintf f "%S" s
|
|
|
|
let chomp s =
|
|
let is_nl_char = function '\n' | '\r' -> true | _ -> false in
|
|
let rec cut n =
|
|
if n = 0 then 0 else if is_nl_char s.[n-1] then cut (n-1) else n
|
|
in
|
|
let ls = length s in
|
|
let n = cut ls in
|
|
if n = ls then s else sub s 0 n
|
|
|
|
let before s pos = sub s 0 pos
|
|
let after s pos = sub s pos (length s - pos)
|
|
let first_chars s n = sub s 0 n
|
|
let last_chars s n = sub s (length s - n) n
|
|
|
|
let rec eq_sub_strings s1 p1 s2 p2 len =
|
|
if len > 0 then s1.[p1] = s2.[p2] && eq_sub_strings s1 (p1+1) s2 (p2+1) (len-1)
|
|
else true
|
|
|
|
let rec contains_string s1 p1 s2 =
|
|
let ls1 = length s1 in
|
|
let ls2 = length s2 in
|
|
try let pos = index_from s1 p1 s2.[0] in
|
|
if ls1 - pos < ls2 then None
|
|
else if eq_sub_strings s1 pos s2 0 ls2 then
|
|
Some pos else contains_string s1 (pos + 1) s2
|
|
with Not_found -> None
|
|
|
|
let subst patt repl s =
|
|
let lpatt = length patt in
|
|
let lrepl = length repl in
|
|
let rec loop s from =
|
|
match contains_string s from patt with
|
|
| Some pos ->
|
|
loop (before s pos ^ repl ^ after s (pos + lpatt)) (pos + lrepl)
|
|
| None -> s
|
|
in loop s 0
|
|
|
|
let tr patt subst text =
|
|
let len = length text in
|
|
let text = copy text in
|
|
let rec loop pos =
|
|
if pos < len then begin
|
|
(if text.[pos] = patt then text.[pos] <- subst);
|
|
loop (pos + 1)
|
|
end
|
|
in loop 0; text
|
|
|
|
(*** is_prefix : is u a prefix of v ? *)
|
|
let is_prefix u v =
|
|
let m = String.length u
|
|
and n = String.length v
|
|
in
|
|
m <= n &&
|
|
let rec loop i = i = m or u.[i] = v.[i] && loop (i + 1) in
|
|
loop 0
|
|
(* ***)
|
|
|
|
(*** is_suffix : is v a suffix of u ? *)
|
|
let is_suffix u v =
|
|
let m = String.length u
|
|
and n = String.length v
|
|
in
|
|
n <= m &&
|
|
let rec loop i = i = n or u.[m - 1 - i] = v.[n - 1 - i] && loop (i + 1) in
|
|
loop 0
|
|
(* ***)
|
|
|
|
let rev s =
|
|
let sl = String.length s in
|
|
let s' = String.create sl in
|
|
for i = 0 to sl - 1 do
|
|
s'.[i] <- s.[sl - i - 1]
|
|
done;
|
|
s';;
|
|
|
|
let implode l =
|
|
match l with
|
|
| [] -> ""
|
|
| cs ->
|
|
let r = create (List.length cs) in
|
|
let pos = ref 0 in
|
|
List.iter begin fun c ->
|
|
unsafe_set r !pos c;
|
|
incr pos
|
|
end cs;
|
|
r
|
|
|
|
let explode s =
|
|
let sl = String.length s in
|
|
let rec go pos =
|
|
if pos >= sl then [] else unsafe_get s pos :: go (pos + 1)
|
|
in go 0
|
|
end
|
|
|
|
module StringSet = Set.Make(String)
|
|
|
|
let sys_readdir, reset_readdir_cache, reset_readdir_cache_for =
|
|
let cache = Hashtbl.create 103 in
|
|
let sys_readdir dir =
|
|
try Hashtbl.find cache dir with Not_found ->
|
|
let res = Outcome.wrap Sys.readdir dir in
|
|
(Hashtbl.add cache dir res; res)
|
|
and reset_readdir_cache () =
|
|
Hashtbl.clear cache
|
|
and reset_readdir_cache_for dir =
|
|
Hashtbl.remove cache dir in
|
|
(sys_readdir, reset_readdir_cache, reset_readdir_cache_for)
|
|
|
|
let sys_file_exists x =
|
|
let dirname = Filename.dirname x in
|
|
let basename = Filename.basename x in
|
|
match sys_readdir dirname with
|
|
| Outcome.Bad _ -> false
|
|
| Outcome.Good a ->
|
|
if basename = Filename.current_dir_name then true else
|
|
try Array.iter (fun x -> if x = basename then raise Exit) a; false
|
|
with Exit -> true
|
|
|
|
let sys_command =
|
|
match Sys.os_type with
|
|
| "Win32" -> fun cmd ->
|
|
if cmd = "" then 0 else
|
|
let cmd = "bash -c "^Filename.quote cmd in
|
|
(* FIXME fix Filename.quote for windows *)
|
|
let cmd = String.subst "\"&\"\"&\"" "&&" cmd in
|
|
Sys.command cmd
|
|
| _ -> fun cmd -> if cmd = "" then 0 else Sys.command cmd
|
|
|
|
(* FIXME warning fix and use Filename.concat *)
|
|
let filename_concat x y =
|
|
if x = Filename.current_dir_name || x = "" then y else
|
|
if x.[String.length x - 1] = '/' then
|
|
if y = "" then x
|
|
else x ^ y
|
|
else x ^ "/" ^ y
|
|
|
|
(* let reslash =
|
|
match Sys.os_type with
|
|
| "Win32" -> tr '\\' '/'
|
|
| _ -> (fun x -> x) *)
|
|
|
|
open Format
|
|
|
|
let invalid_arg' fmt = ksbprintf invalid_arg fmt
|
|
|
|
let the = function Some x -> x | None -> invalid_arg "the: expect Some not None"
|
|
|
|
let getenv ?default var =
|
|
try Sys.getenv var
|
|
with Not_found ->
|
|
match default with
|
|
| Some x -> x
|
|
| None -> failwith (sprintf "This command must have %S in his environment" var);;
|
|
|
|
let with_input_file ?(bin=false) x f =
|
|
let ic = (if bin then open_in_bin else open_in) x in
|
|
try let res = f ic in close_in ic; res with e -> (close_in ic; raise e)
|
|
|
|
let with_output_file ?(bin=false) x f =
|
|
reset_readdir_cache_for (Filename.dirname x);
|
|
let oc = (if bin then open_out_bin else open_out) x in
|
|
try let res = f oc in close_out oc; res with e -> (close_out oc; raise e)
|
|
|
|
let read_file x =
|
|
with_input_file ~bin:true x begin fun ic ->
|
|
let len = in_channel_length ic in
|
|
let buf = String.create len in
|
|
let () = really_input ic buf 0 len in
|
|
buf
|
|
end
|
|
|
|
let copy_chan ic oc =
|
|
let m = in_channel_length ic in
|
|
let m = (m lsr 12) lsl 12 in
|
|
let m = max 16384 (min Sys.max_string_length m) in
|
|
let buf = String.create m in
|
|
let rec loop () =
|
|
let len = input ic buf 0 m in
|
|
if len > 0 then begin
|
|
output oc buf 0 len;
|
|
loop ()
|
|
end
|
|
in loop ()
|
|
|
|
let copy_file src dest =
|
|
reset_readdir_cache_for (Filename.dirname dest);
|
|
with_input_file ~bin:true src begin fun ic ->
|
|
with_output_file ~bin:true dest begin fun oc ->
|
|
copy_chan ic oc
|
|
end
|
|
end
|
|
|
|
let ( !* ) = Lazy.force
|
|
|
|
let ( @:= ) ref list = ref := !ref @ list
|
|
|
|
let ( & ) f x = f x
|
|
|
|
let ( |> ) x f = f x
|
|
|
|
let print_string_list = List.print String.print
|
|
|
|
module Digest = struct
|
|
include Digest
|
|
(* USEFUL FOR DIGEST DEBUGING
|
|
let digest_log_hash = Hashtbl.create 103;;
|
|
let digest_log = "digest.log";;
|
|
let digest_log_oc = open_out_gen [Open_append;Open_wronly;Open_text;Open_creat] 0o644 digest_log;;
|
|
let my_to_hex x = to_hex x ^ ";";;
|
|
if sys_file_exists digest_log then
|
|
with_input_file digest_log begin fun ic ->
|
|
try while true do
|
|
let l = input_line ic in
|
|
Scanf.sscanf l "%S: %S" (Hashtbl.replace digest_log_hash)
|
|
done with End_of_file -> ()
|
|
end;;
|
|
let string s =
|
|
let res = my_to_hex (string s) in
|
|
if try let x = Hashtbl.find digest_log_hash res in s <> x with Not_found -> true then begin
|
|
Hashtbl.replace digest_log_hash res s;
|
|
Printf.fprintf digest_log_oc "%S: %S\n%!" res s
|
|
end;
|
|
res
|
|
let file f = my_to_hex (file f)
|
|
let to_hex x = x
|
|
*)
|
|
|
|
let digest_cache = Hashtbl.create 103
|
|
let reset_digest_cache () = Hashtbl.clear digest_cache
|
|
let reset_digest_cache_for file = Hashtbl.remove digest_cache file
|
|
let file f =
|
|
try Hashtbl.find digest_cache f
|
|
with Not_found ->
|
|
let res = file f in
|
|
(Hashtbl.add digest_cache f res; res)
|
|
end
|
|
|
|
let reset_filesys_cache () =
|
|
Digest.reset_digest_cache ();
|
|
reset_readdir_cache ()
|
|
|
|
let reset_filesys_cache_for_file file =
|
|
Digest.reset_digest_cache_for file;
|
|
reset_readdir_cache_for (Filename.dirname file)
|
|
|
|
let sys_remove x =
|
|
reset_filesys_cache_for_file x;
|
|
Sys.remove x
|
|
|
|
let with_temp_file pre suf fct =
|
|
let tmp = Filename.temp_file pre suf in
|
|
(* Sys.remove is used instead of sys_remove since we know that the tempfile is not that important *)
|
|
try let res = fct tmp in Sys.remove tmp; res
|
|
with e -> (Sys.remove tmp; raise e)
|
|
|
|
let memo f =
|
|
let cache = Hashtbl.create 103 in
|
|
fun x ->
|
|
try Hashtbl.find cache x
|
|
with Not_found ->
|
|
let res = f x in
|
|
(Hashtbl.add cache x res; res)
|