let debug = false;; exception NoRootFor of string let absolutize path = let path = if String.length path > 0 && path.[0] <> '/' then Sys.getcwd () ^ "/" ^ path else path in HExtlib.normalize_path path ;; let find_root path = let path = absolutize path in let paths = List.rev (Str.split (Str.regexp "/") path) in let rec build = function | he::tl as l -> ("/" ^ String.concat "/" (List.rev l) ^ "/") :: build tl | [] -> ["/"] in let paths = List.map HExtlib.normalize_path (build paths) in try HExtlib.find_in paths "root" with Failure "find_in" -> raise (NoRootFor (path ^ " (" ^ String.concat ", " paths ^ ")")) ;; let ensure_trailing_slash s = if s = "" then "/" else if s.[String.length s-1] <> '/' then s^"/" else s ;; let remove_trailing_slash s = if s = "" then "" else let len = String.length s in if s.[len-1] = '/' then String.sub s 0 (len-1) else s ;; let load_root_file rootpath = let data = HExtlib.input_file rootpath in let lines = Str.split (Str.regexp "\n") data in let clean s = Pcre.replace ~pat:"[ \t]+" ~templ:" " (Pcre.replace ~pat:"^ *" (Pcre.replace ~pat:" *$" s)) in List.map (fun l -> match Str.split (Str.regexp "=") l with | [k;v] -> clean k, Http_getter_misc.strip_trailing_slash (clean v) | _ -> raise (Failure ("Malformed root file: " ^ rootpath))) lines ;; let find_root_for ~include_paths file = let include_paths = "" :: Sys.getcwd () :: include_paths in try let path = HExtlib.find_in include_paths file in let path = absolutize path in (* HLog.debug ("file "^file^" resolved as "^path); *) let rootpath, root, buri = try let mburi = Helm_registry.get "matita.baseuri" in match Str.split (Str.regexp " ") mburi with | [root; buri] when HExtlib.is_prefix_of root path -> ":registry:", root, buri | _ -> raise (Helm_registry.Key_not_found "matita.baseuri") with Helm_registry.Key_not_found "matita.baseuri" -> let rootpath = find_root path in let buri = List.assoc "baseuri" (load_root_file rootpath) in rootpath, Filename.dirname rootpath, buri in (* HLog.debug ("file "^file^" rooted by "^rootpath^""); *) let uri = Http_getter_misc.strip_trailing_slash buri in if String.length uri < 5 || String.sub uri 0 5 <> "cic:/" then HLog.error (rootpath ^ " sets an incorrect baseuri: " ^ buri); ensure_trailing_slash root, remove_trailing_slash uri, path with Failure "find_in" -> HLog.error ("We are in: " ^ Sys.getcwd ()); HLog.error ("Unable to find: "^file^"\nPaths explored:"); List.iter (fun x -> HLog.error (" - "^x)) include_paths; raise (NoRootFor file) ;; let mk_baseuri root extra = let chop name = assert(Filename.check_suffix name ".ma" || Filename.check_suffix name ".mma"); try Filename.chop_extension name with Invalid_argument "Filename.chop_extension" -> name in remove_trailing_slash (HExtlib.normalize_path (root ^ "/" ^ chop extra)) ;; let baseuri_of_script ~include_paths file = let root, buri, path = find_root_for ~include_paths file in let path = HExtlib.normalize_path path in let root = HExtlib.normalize_path root in let lpath = Str.split (Str.regexp "/") path in let lroot = Str.split (Str.regexp "/") root in let rec substract l1 l2 = match l1, l2 with | h1::tl1,h2::tl2 when h1 = h2 -> substract tl1 tl2 | l,[] -> l | _ -> raise (NoRootFor (file ^" "^path^" "^root)) in let extra_buri = substract lpath lroot in let extra = String.concat "/" extra_buri in root, mk_baseuri buri extra, path, extra ;; let find_roots_in_dir dir = HExtlib.find ~test:(fun f -> Filename.basename f = "root" && try (Unix.stat f).Unix.st_kind = Unix.S_REG with Unix.Unix_error _ -> false) dir ;; (* make *) let load_deps_file f = let deps = ref [] in let ic = open_in f in try while true do begin let l = input_line ic in match Str.split (Str.regexp " ") l with | [] -> HLog.error ("Malformed deps file: " ^ f); raise (Failure ("Malformed deps file: " ^ f)) | he::tl -> deps := (he,tl) :: !deps end done; !deps with End_of_file -> !deps ;; type options = (string * string) list module type Format = sig type source_object type target_object val load_deps_file: string -> (source_object * source_object list) list val string_of_source_object: source_object -> string val string_of_target_object: target_object -> string val build: options -> source_object -> bool val root_and_target_of: options -> source_object -> string option * target_object val mtime_of_source_object: source_object -> float option val mtime_of_target_object: target_object -> float option val is_readonly_buri_of: options -> source_object -> bool end module Make = functor (F:Format) -> struct let say s = if debug then prerr_endline ("make: "^s);; let unopt_or_call x f y = match x with Some _ -> x | None -> f y;; let younger_s_t (_,cs,ct) a b = let a = try Hashtbl.find cs a with Not_found -> assert false in let b = try match Hashtbl.find ct b with | Some _ as x -> x | None -> match F.mtime_of_target_object b with | Some t as x -> Hashtbl.remove ct b; Hashtbl.add ct b x; x | x -> x with Not_found -> assert false in match a, b with | Some a, Some b -> a < b | _ -> false ;; let younger_t_t (_,_,ct) a b = let a = try match Hashtbl.find ct a with | Some _ as x -> x | None -> match F.mtime_of_target_object a with | Some t as x -> Hashtbl.remove ct b; Hashtbl.add ct a x; x | x -> x with Not_found -> assert false in let b = try match Hashtbl.find ct b with | Some _ as x -> x | None -> match F.mtime_of_target_object b with | Some t as x -> Hashtbl.remove ct b; Hashtbl.add ct b x; x | x -> x with Not_found -> assert false in match a, b with | Some a, Some b -> a < b | _ -> false ;; let is_built opts t tgt = younger_s_t opts t tgt ;; let assoc4 l k = List.find (fun (k1,_,_,_) -> k1 = k) l;; let fst4 = function (x,_,_,_) -> x;; let rec needs_build opts deps compiled (t,dependencies,root,tgt) = say ("Checking if "^F.string_of_source_object t^ " needs to be built"); if List.mem t compiled then (say "already compiled"; false) else if not (is_built opts t tgt) then (say(F.string_of_source_object t^" is not built, thus needs to be built"); true) else try let unsat = List.find (needs_build opts deps compiled) (List.map (assoc4 deps) dependencies) in say (F.string_of_source_object t^" depends on "^ F.string_of_source_object (fst4 unsat)^ " that needs to be built, thus needs to be built"); true with Not_found -> try let _,_,_,unsat = List.find (fun (_,_,_,tgt1) -> younger_t_t opts tgt tgt1) (List.map (assoc4 deps) dependencies) in say (F.string_of_source_object t^" depends on "^F.string_of_target_object unsat^" and "^F.string_of_source_object t^".o is younger than "^ F.string_of_target_object unsat^", thus needs to be built"); true with Not_found -> false ;; let is_buildable opts compiled deps (t,dependencies,root,tgt as what) = say ("Checking if "^F.string_of_source_object t^" is buildable"); let b = needs_build opts deps compiled what in if not b then (say (F.string_of_source_object t^ " does not need to be built, thus it not buildable"); false) else try let unsat,_,_,_ = List.find (needs_build opts deps compiled) (List.map (assoc4 deps) dependencies) in say (F.string_of_source_object t^" depends on "^ F.string_of_source_object unsat^ " that needs build, thus is not buildable"); false with Not_found -> say ("None of "^F.string_of_source_object t^ " dependencies needs to be built, thus it is buildable"); true ;; let rec purge_unwanted_roots wanted deps = let roots, rest = List.partition (fun (t,d,_,_) -> not (List.exists (fun (_,d1,_,_) -> List.mem t d1) deps)) deps in let newroots = List.filter (fun (t,_,_,_) -> List.mem t wanted) roots in if newroots = roots then deps else purge_unwanted_roots wanted (newroots @ rest) ;; let is_not_ro (opts,_,_) (f,_,r,_) = match r with | Some root -> not (F.is_readonly_buri_of opts f) | None -> assert false ;; let rec make_aux root (lo,_,ct as opts) compiled failed deps = let todo = List.filter (is_buildable opts compiled deps) deps in let todo = List.filter (fun (f,_,_,_)->not (List.mem f failed)) todo in let todo = let local, remote = List.partition (fun (_,_,froot,_) -> froot = Some root) todo in let local, skipped = List.partition (is_not_ro opts) local in List.iter (fun x -> HLog.warn("Read only baseuri for: "^F.string_of_source_object(fst4 x))) skipped; remote @ local in if todo <> [] then let compiled, failed = List.fold_left (fun (c,f) (file,_,froot,tgt) -> let rc = match froot with | Some froot when froot = root -> Hashtbl.remove ct tgt; Hashtbl.add ct tgt None; F.build lo file | Some froot -> make froot [file] | None -> HLog.error ("No root for: "^F.string_of_source_object file); false in if rc then (file::c,f) else (c,file::f)) (compiled,failed) todo in make_aux root opts compiled failed deps else compiled, failed and make root targets = HLog.debug ("Entering directory '"^root^"'"); let old_root = Sys.getcwd () in Sys.chdir root; let deps = F.load_deps_file (root^"/depends") in let local_options = load_root_file (root^"/root") in let caches,cachet = Hashtbl.create 73, Hashtbl.create 73 in (* deps are enriched with these informations to sped up things later *) let deps = List.map (fun (file,d) -> let r,tgt = F.root_and_target_of local_options file in Hashtbl.add caches file (F.mtime_of_source_object file); Hashtbl.add cachet tgt (F.mtime_of_target_object tgt); file, d, r, tgt) deps in let opts = local_options, caches, cachet in let _compiled, failed = if targets = [] then make_aux root opts [] [] deps else make_aux root opts [] [] (purge_unwanted_roots targets deps) in HLog.debug ("Leaving directory '"^root^"'"); Sys.chdir old_root; failed = [] ;; end let write_deps_file where deps = match where with | Some root -> let oc = open_out (root ^ "/depends") in let map (t, d) = output_string oc (t^" "^String.concat " " d^"\n") in List.iter map deps; close_out oc; HLog.message ("Generated: " ^ root ^ "/depends") | None -> print_endline (String.concat " " (List.flatten (List.map snd deps))) (* FG ***********************************************************************) (* scheme uri part as defined in URI Generic Syntax (RFC 3986) *) let uri_scheme_rex = Pcre.regexp "^[[:alpha:]][[:alnum:]\-+.]*:" let is_uri str = Pcre.pmatch ~rex:uri_scheme_rex str