]> matita.cs.unibo.it Git - helm.git/blob - components/library/librarian.ml
beginning to see the light
[helm.git] / components / library / librarian.ml
1 let debug = false;;
2
3 exception NoRootFor of string
4
5 let absolutize path =
6  let path = 
7    if String.length path > 0 && path.[0] <> '/' then
8      Sys.getcwd () ^ "/" ^ path
9    else 
10      path
11  in
12    HExtlib.normalize_path path
13 ;;
14
15
16 let find_root path =
17   let path = absolutize path in
18   let paths = List.rev (Str.split (Str.regexp "/") path) in
19   let rec build = function
20     | he::tl as l -> ("/" ^ String.concat "/" (List.rev l) ^ "/") :: build tl
21     | [] -> ["/"]
22   in
23   let paths = List.map HExtlib.normalize_path (build paths) in
24   try HExtlib.find_in paths "root"
25   with Failure "find_in" -> 
26     raise (NoRootFor (path ^ " (" ^ String.concat ", " paths ^ ")"))
27 ;;
28   
29 let ensure_trailing_slash s = 
30   if s = "" then "/" else
31   if s.[String.length s-1] <> '/' then s^"/" else s
32 ;;
33
34 let remove_trailing_slash s = 
35   if s = "" then "" else
36   let len = String.length s in
37   if s.[len-1] = '/' then String.sub s 0 (len-1) else s
38 ;;
39
40 let load_root_file rootpath =
41   let data = HExtlib.input_file rootpath in
42   let lines = Str.split (Str.regexp "\n") data in
43   let clean s = Pcre.replace ~pat:"^ *" (Pcre.replace ~pat:" *$" s) in
44   List.map 
45     (fun l -> 
46       match Str.split (Str.regexp "=") l with
47       | [k;v] -> clean k, Http_getter_misc.strip_trailing_slash (clean v)
48       | _ -> raise (Failure ("Malformed root file: " ^ rootpath)))
49     lines
50 ;;
51
52 let find_root_for ~include_paths file = 
53  let include_paths = "" :: Sys.getcwd () :: include_paths in
54  try 
55    let path = HExtlib.find_in include_paths file in
56    let path = absolutize path in
57 (*     HLog.debug ("file "^file^" resolved as "^path);  *)
58    let rootpath, root, buri = 
59      try
60        let mburi = Helm_registry.get "matita.baseuri" in
61        match Str.split (Str.regexp " ") mburi with
62        | [root; buri] when HExtlib.is_prefix_of root path -> 
63            ":registry:", root, buri
64        | _ -> raise (Helm_registry.Key_not_found "matita.baseuri")
65      with Helm_registry.Key_not_found "matita.baseuri" -> 
66        let rootpath = find_root path in
67        let buri = List.assoc "baseuri" (load_root_file rootpath) in
68        rootpath, Filename.dirname rootpath, buri
69    in
70 (*     HLog.debug ("file "^file^" rooted by "^rootpath^"");  *)
71    let uri = Http_getter_misc.strip_trailing_slash buri in
72    if String.length uri < 5 || String.sub uri 0 5 <> "cic:/" then
73      HLog.error (rootpath ^ " sets an incorrect baseuri: " ^ buri);
74    ensure_trailing_slash root, remove_trailing_slash uri, path
75  with Failure "find_in" -> 
76    HLog.error ("We are in: " ^ Sys.getcwd ());
77    HLog.error ("Unable to find: "^file^"\nPaths explored:");
78    List.iter (fun x -> HLog.error (" - "^x)) include_paths;
79    raise (NoRootFor file)
80 ;;
81
82 let baseuri_of_script ~include_paths file = 
83   let root, buri, path = find_root_for ~include_paths file in
84   let path = HExtlib.normalize_path path in
85   let root = HExtlib.normalize_path root in
86   let lpath = Str.split (Str.regexp "/") path in
87   let lroot = Str.split (Str.regexp "/") root in
88   let rec substract l1 l2 =
89     match l1, l2 with
90     | h1::tl1,h2::tl2 when h1 = h2 -> substract tl1 tl2
91     | l,[] -> l
92     | _ -> raise (NoRootFor (file ^" "^path^" "^root))
93   in
94   let extra_buri = substract lpath lroot in
95   let chop name = 
96     assert(Filename.check_suffix name ".ma" ||
97       Filename.check_suffix name ".mma");
98     try Filename.chop_extension name
99     with Invalid_argument "Filename.chop_extension" -> name
100   in
101   let extra = String.concat "/" extra_buri in
102    root,
103    remove_trailing_slash (HExtlib.normalize_path 
104     (buri ^ "/" ^ chop extra)),
105    path,
106    extra
107 ;;
108
109 let find_roots_in_dir dir =
110   HExtlib.find ~test:(fun f ->
111     Filename.basename f = "root" &&
112     try (Unix.stat f).Unix.st_kind = Unix.S_REG 
113     with Unix.Unix_error _ -> false)
114     dir
115 ;;
116
117 (* make *)
118 let load_deps_file f = 
119   let deps = ref [] in
120   let ic = open_in f in
121   try
122     while true do
123       begin
124         let l = input_line ic in
125         match Str.split (Str.regexp " ") l with
126         | [] -> 
127             HLog.error ("Malformed deps file: " ^ f); 
128             raise (Failure ("Malformed deps file: " ^ f)) 
129         | he::tl -> deps := (he,tl) :: !deps
130       end
131     done; !deps
132     with End_of_file -> !deps
133 ;;
134
135 type options = (string * string) list
136
137 module type Format =
138   sig
139     type source_object
140     type target_object
141     val load_deps_file: string -> (source_object * source_object list) list
142     val target_of: options -> source_object -> target_object
143     val string_of_source_object: source_object -> string
144     val string_of_target_object: target_object -> string
145     val build: options -> source_object -> bool
146     val root_of: options -> source_object -> string option
147     val mtime_of_source_object: source_object -> float option
148     val mtime_of_target_object: target_object -> float option
149   end
150
151 module Make = functor (F:Format) -> struct
152
153   let prerr_endline s = if debug then prerr_endline ("make: "^s);; 
154
155   let younger_s_t a b = 
156     match F.mtime_of_source_object a, F.mtime_of_target_object b with
157     | Some a, Some b -> a < b
158     | _ -> false (* XXX check if correct *)
159   ;;
160   let younger_t_t a b = 
161     match F.mtime_of_target_object a, F.mtime_of_target_object b with
162     | Some a, Some b -> a < b
163     | _ -> false (* XXX check if correct *)
164   ;;
165
166   let is_built opts t = younger_s_t t (F.target_of opts t);;
167
168   let rec needs_build opts deps compiled (t,dependencies) =
169     prerr_endline ("Checking if "^F.string_of_source_object t^" needs to be built");
170     if List.mem t compiled then
171       (prerr_endline "already compiled";
172       false)
173     else
174     if not (is_built opts t) then
175       (prerr_endline (F.string_of_source_object t^
176        " is not built, thus needs to be built");
177       true)
178     else
179     try
180       let unsat =
181         List.find
182           (needs_build opts deps compiled) 
183           (List.map (fun x -> x, List.assoc x deps) dependencies)
184       in
185         prerr_endline 
186          (F.string_of_source_object t^" depends on "^F.string_of_source_object (fst unsat)^
187          " that needs to be built, thus needs to be built");
188         true
189     with Not_found ->
190       try 
191         let unsat = 
192           List.find (younger_t_t (F.target_of opts t)) 
193            (List.map (F.target_of opts) dependencies)
194         in
195           prerr_endline 
196            (F.string_of_source_object t^" depends on "^F.string_of_target_object
197            unsat^" and "^F.string_of_source_object t^".o is younger than "^
198            F.string_of_target_object unsat^", thus needs to be built");
199           true
200       with Not_found -> false
201   ;;
202
203   let is_buildable opts compiled deps (t,dependencies) =
204     prerr_endline ("Checking if "^F.string_of_source_object t^" is buildable");
205     let b = needs_build opts deps compiled (t,dependencies) in
206     if not b then
207       (prerr_endline (F.string_of_source_object t^
208        " does not need to be built, thus it not buildable");
209       false)
210     else
211     try  
212       let unsat =
213         List.find (needs_build opts deps compiled) 
214           (List.map (fun x -> x, List.assoc x deps) dependencies)
215       in
216         prerr_endline 
217           (F.string_of_source_object t^" depends on "^
218           F.string_of_source_object (fst unsat)^
219           " that needs build, thus is not buildable");
220         false
221     with Not_found -> 
222       prerr_endline 
223         ("None of "^F.string_of_source_object t^
224         " dependencies needs to be built, thus it is buildable");
225       true
226   ;;
227
228   let rec purge_unwanted_roots wanted deps =
229     let roots, rest = 
230        List.partition 
231          (fun (t,d) ->
232            not (List.exists (fun (_,d1) -> List.mem t d1) deps))
233          deps
234     in
235     let newroots = List.filter (fun (t,_) -> List.mem t wanted) roots in
236     if newroots = roots then
237       deps
238     else
239       purge_unwanted_roots wanted (newroots @ rest)
240   ;;
241
242
243   let rec make_aux root local_options compiled failed deps = 
244     let todo = List.filter (is_buildable local_options compiled deps) deps in
245     let todo = List.filter (fun (f,_) -> not (List.mem f failed)) todo in
246     if todo <> [] then
247       let compiled, failed = 
248         let todo =
249           let local, remote =
250             List.partition
251               (fun (file,d) -> 
252                 d<>[] || F.root_of local_options file = Some root)
253               todo
254           in
255           remote @ local
256         in
257         List.fold_left 
258           (fun (c,f) (file,_) ->
259             let froot = F.root_of local_options file in
260             let rc = 
261               match froot with
262               | Some froot when froot = root ->
263                   F.build local_options file 
264               | Some froot ->
265                   make froot [file]
266               | None -> 
267                   HLog.error ("No root for: "^F.string_of_source_object file);
268                   false
269             in
270             if rc then (file::c,f)
271             else (c,file::f))
272           (compiled,failed) todo
273       in
274         make_aux root local_options compiled failed deps
275     else
276       compiled, failed
277
278   and  make root targets = 
279     let deps = F.load_deps_file (root^"/depends") in
280     let local_options = load_root_file (root^"/root") in
281     HLog.debug ("Entering directory '"^root^"'");
282     let old_root = Sys.getcwd () in
283     Sys.chdir root;
284     let _compiled, failed =
285       if targets = [] then 
286         make_aux root local_options [] [] deps
287       else
288         make_aux root local_options [] [] (purge_unwanted_roots targets deps)
289     in
290     HLog.debug ("Leaving directory '"^root^"'");
291     Sys.chdir old_root;
292     failed = []
293   ;;
294
295 end
296   
297 let write_deps_file root deps =
298   let oc = open_out (root ^ "/depends") in
299   List.iter 
300     (fun (t,d) -> output_string oc (t^" "^String.concat " " d^"\n")) 
301     deps;
302   close_out oc;
303   HLog.message ("Generated: " ^ root ^ "/depends")
304 ;;
305