--- /dev/null
+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