diff options
Diffstat (limited to 'ocaml-binary-annot/ocamlbuild/resource.ml')
| -rw-r--r-- | ocaml-binary-annot/ocamlbuild/resource.ml | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/ocaml-binary-annot/ocamlbuild/resource.ml b/ocaml-binary-annot/ocamlbuild/resource.ml new file mode 100644 index 0000000000..ed90d1cff6 --- /dev/null +++ b/ocaml-binary-annot/ocamlbuild/resource.ml @@ -0,0 +1,396 @@ +(***********************************************************************) +(* 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 My_std +open Format +open Log +open Pathname.Operators + +module Resources = Set.Make(Pathname) + +let print = Pathname.print + +let equal = (=) +let compare = compare + +let in_source_dir p = + if Pathname.is_implicit p then Pathname.pwd/p else invalid_arg (Printf.sprintf "in_source_dir: %S" p) + +let in_build_dir p = + if Pathname.is_relative p then p + else invalid_arg (Printf.sprintf "in_build_dir: %S" p) + +let clean_up_links entry = + if not !Options.make_links then entry else + Slurp.filter begin fun path name _ -> + let pathname = in_source_dir (path/name) in + if Pathname.link_to_dir pathname !Options.build_dir then + let z = Pathname.readlink pathname in + (* Here is one exception where one can use Sys.file_exists directly *) + (if not (Sys.file_exists z) then + Shell.rm pathname; false) + else true + end entry + +let clean_up_link_to_build () = + Options.entry := Some(clean_up_links (the !Options.entry)) + +let source_dir_path_set_without_links_to_build = + lazy begin + clean_up_link_to_build (); + Slurp.fold (fun path name _ -> StringSet.add (path/name)) + (the !Options.entry) StringSet.empty + end + +let clean_links () = + if !*My_unix.is_degraded then + () + else + ignore (clean_up_link_to_build ()) + +let exists_in_source_dir p = + if !*My_unix.is_degraded then sys_file_exists (in_source_dir p) + else StringSet.mem p !*source_dir_path_set_without_links_to_build + +let clean p = Shell.rm_f p + +module Cache = struct + + let clean () = Shell.chdir Pathname.pwd; Shell.rm_rf !Options.build_dir + + type knowledge = + | Yes + | No + | Unknown + + type suspension = (Command.t * (unit -> unit)) + + type build_status = + | Bbuilt + | Bcannot_be_built + | Bnot_built_yet + | Bsuspension of suspension + + type cache_entry = + { mutable built : build_status; + mutable changed : knowledge; + mutable dependencies : Resources.t } + + let empty () = + { built = Bnot_built_yet; + changed = Unknown; + dependencies = Resources.empty } + + let print_knowledge f = + function + | Yes -> pp_print_string f "Yes" + | No -> pp_print_string f "No" + | Unknown -> pp_print_string f "Unknown" + + let print_build_status f = + function + | Bbuilt -> pp_print_string f "Bbuilt" + | Bnot_built_yet -> pp_print_string f "Bnot_built_yet" + | Bcannot_be_built -> pp_print_string f "Bcannot_be_built" + | Bsuspension(cmd, _) -> + fprintf f "@[<2>Bsuspension(%a,@ (<fun> : unit -> unit))@]" Command.print cmd + + let print_cache_entry f e = + fprintf f "@[<2>{ @[<2>built =@ %a@];@ @[<2>changed =@ %a@];@ @[<2>dependencies =@ %a@]@ }@]" + print_build_status e.built print_knowledge e.changed Resources.print e.dependencies + + let cache = Hashtbl.create 103 + + let get r = + try Hashtbl.find cache r + with Not_found -> + let cache_entry = empty () in + Hashtbl.add cache r cache_entry; cache_entry + + let fold_cache f x = Hashtbl.fold f cache x + + let print_cache f () = + fprintf f "@[<hv0>@[<hv2>{:"; + fold_cache begin fun k v () -> + fprintf f "@ @[<2>%a =>@ %a@];" print k print_cache_entry v + end (); + fprintf f "@]:}@]" + + let print_graph f () = + fprintf f "@[<hv0>@[<hv2>{:"; + fold_cache begin fun k v () -> + if not (Resources.is_empty v.dependencies) then + fprintf f "@ @[<2>%a =>@ %a@];" print k Resources.print v.dependencies + end (); + fprintf f "@]@ :}@]" + + let resource_changed r = + dprintf 10 "resource_changed:@ %a" print r; + (get r).changed <- Yes + + let external_is_up_to_date absolute_path = + let key = "Resource: " ^ absolute_path in + let digest = Digest.file absolute_path in + let is_up_to_date = + try + let digest' = Digest_cache.get key in + digest = digest' + with Not_found -> + false + in + is_up_to_date || (Digest_cache.put key digest; false) + + let source_is_up_to_date r_in_source_dir r_in_build_dir = + let key = "Resource: " ^ r_in_source_dir in + let digest = Digest.file r_in_source_dir in + let r_is_up_to_date = + Pathname.exists r_in_build_dir && + try + let digest' = Digest_cache.get key in + digest = digest' + with Not_found -> + false + in + r_is_up_to_date || (Digest_cache.put key digest; false) + + let prod_is_up_to_date p = + let x = in_build_dir p in + not (exists_in_source_dir p) || Pathname.exists x && Pathname.same_contents x (in_source_dir p) + + let rec resource_has_changed r = + let cache_entry = get r in + match cache_entry.changed with + | Yes -> true + | No -> false + | Unknown -> + let res = + match cache_entry.built with + | Bbuilt -> false + | Bsuspension _ -> assert false + | Bcannot_be_built -> false + | Bnot_built_yet -> not (prod_is_up_to_date r) in + let () = cache_entry.changed <- if res then Yes else No in res + + let resource_state r = (get r).built + + let resource_built r = (get r).built <- Bbuilt + + let resource_failed r = (get r).built <- Bcannot_be_built + + let import_in_build_dir r = + let cache_entry = get r in + let r_in_build_dir = in_build_dir r in + let r_in_source_dir = in_source_dir r in + if source_is_up_to_date r_in_source_dir r_in_build_dir then begin + dprintf 5 "%a exists and up to date" print r; + end else begin + dprintf 5 "%a exists in source dir -> import it" print r; + Shell.mkdir_p (Pathname.dirname r); + Pathname.copy r_in_source_dir r_in_build_dir; + cache_entry.changed <- Yes; + end; + cache_entry.built <- Bbuilt + + let suspend_resource r cmd kont prods = + let cache_entry = get r in + match cache_entry.built with + | Bsuspension _ -> () + | Bbuilt -> () + | Bcannot_be_built -> assert false + | Bnot_built_yet -> + let kont = begin fun () -> + kont (); + List.iter begin fun prod -> + (get prod).built <- Bbuilt + end prods + end in cache_entry.built <- Bsuspension(cmd, kont) + + let resume_suspension (cmd, kont) = + Command.execute cmd; + kont () + + let resume_resource r = + let cache_entry = get r in + match cache_entry.built with + | Bsuspension(s) -> resume_suspension s + | Bbuilt -> () + | Bcannot_be_built -> () + | Bnot_built_yet -> () + + let get_optional_resource_suspension r = + match (get r).built with + | Bsuspension cmd_kont -> Some cmd_kont + | Bbuilt | Bcannot_be_built | Bnot_built_yet -> None + + let clear_resource_failed r = (get r).built <- Bnot_built_yet + + let dependencies r = (get r).dependencies + + let fold_dependencies f = + fold_cache (fun k v -> Resources.fold (f k) v.dependencies) + + let add_dependency r s = + let cache_entry = get r in + cache_entry.dependencies <- Resources.add s cache_entry.dependencies + + let print_dependencies = print_graph + +end + +let digest p = + let f = Pathname.to_string (in_build_dir p) in + let buf = Buffer.create 1024 in + Buffer.add_string buf f; + (if sys_file_exists f then Buffer.add_string buf (Digest.file f)); + Digest.string (Buffer.contents buf) + +let exists_in_build_dir p = Pathname.exists (in_build_dir p) + +(* +type env = string + +let split_percent s = + try + let pos = String.index s '%' in + Some (String.before s pos, String.after s (pos + 1)) + with Not_found -> None + +let extract prefix suffix s = + let lprefix = String.length prefix in + let lsuffix = String.length suffix in + let ls = String.length s in + if lprefix + lsuffix > ls then None else + let s' = String.sub s lprefix (ls - lsuffix - lprefix) in + if equal (prefix ^ s' ^ suffix) s then Some s' else None + +let matchit r1 r2 = + match split_percent r1 with + | Some (x, y) -> extract x y r2 + | _ -> if equal r1 r2 then Some "" else None + +let rec subst percent r = + match split_percent r with + | Some (x, y) -> x ^ percent ^ y + | _ -> r + +let print_env = pp_print_string +*) + +(* Should normalize *) +let import x = Pathname.normalize x + +module MetaPath : sig + + type t + type env + + val mk : (bool * string) -> t + val matchit : t -> string -> env option + val subst : env -> t -> string + val print_env : Format.formatter -> env -> unit + +end = struct + open Glob_ast + + type atoms = A of string | V of string * Glob.globber + type t = atoms list + type env = (string * string) list + + exception No_solution + + let mk (pattern_allowed, s) = List.map begin function + | `Var(var_name, globber) -> V(var_name, globber) + | `Word s -> A s + end (Lexers.path_scheme pattern_allowed (Lexing.from_string s)) + + let mk = memo mk + + let match_prefix s pos prefix = + match String.contains_string s pos prefix with + | Some(pos') -> if pos = pos' then pos' + String.length prefix else raise No_solution + | None -> raise No_solution + + let matchit p s = + let sl = String.length s in + let rec loop xs pos acc delta = + match xs with + | [] -> if pos = sl then acc else raise No_solution + | A prefix :: xs -> loop xs (match_prefix s pos prefix) acc 0 + | V(var, patt) :: A s2 :: xs' -> + begin match String.contains_string s (pos + delta) s2 with + | Some(pos') -> + let matched = String.sub s pos (pos' - pos) in + if Glob.eval patt matched + then + try loop xs' (pos' + String.length s2) ((var, matched) :: acc) 0 + with No_solution -> loop xs pos acc (pos' - pos + 1) + else loop xs pos acc (pos' - pos + 1) + | None -> raise No_solution + end + | [V(var, patt)] -> + let matched = String.sub s pos (sl - pos) in + if Glob.eval patt matched then (var, matched) :: acc else raise No_solution + | V _ :: _ -> assert false + in + try Some (loop p 0 [] 0) + with No_solution -> None + + let pp_opt pp_elt f = + function + | None -> pp_print_string f "None" + | Some x -> Format.fprintf f "Some(%a)" pp_elt x + + let print_env f env = + List.iter begin fun (k, v) -> + if k = "" then Format.fprintf f "%%=%s " v + else Format.fprintf f "%%(%s)=%s " k v + end env + + (* let matchit p s = + let res = matchit p s in + Format.eprintf "matchit %S %S = %a@." p s (pp_opt print_env) res; + res + + let _ = begin + assert (matchit "%(path)lib%(libname).a" "libfoo.a" <> None); + assert (matchit "%(path)lib%(libname).a" "path/libfoo.a" <> None); + assert (matchit "libfoo.a" "libfoo.a" <> None); + assert (matchit "lib%(libname).a" "libfoo.a" <> None); + assert (matchit "%(path)libfoo.a" "path/libfoo.a" <> None); + assert (matchit "foo%" "foobar" <> None); + exit 42 + end;; *) + + let subst env s = + String.concat "" begin + List.map begin fun x -> + match x with + | A atom -> atom + | V(var, _) -> List.assoc var env + end s + end +end + +type env = MetaPath.env +type resource_pattern = (Pathname.t * MetaPath.t) + +let print_pattern f (x, _) = Pathname.print f x + +let import_pattern x = x, MetaPath.mk (true, x) +let matchit (_, p) x = MetaPath.matchit p x + +let subst env s = MetaPath.subst env (MetaPath.mk (false, s)) +let subst_any env s = MetaPath.subst env (MetaPath.mk (true, s)) +let subst_pattern env (_, p) = MetaPath.subst env p + +let print_env = MetaPath.print_env |
