Coccinelle release 1.0.0-rc12
[bpt/coccinelle.git] / ocaml / yes_prepare_ocamlcocci.ml
CommitLineData
174d1640
C
1module Ast = Ast_cocci
2
3exception CompileFailure of string
4exception LinkFailure of string
5
feec80c3 6let ext = if Config.dynlink_is_native then ".cmxs" else ".cma"
174d1640 7
8babbc8f 8let sysdir () =
1eddfd50
C
9 let sysdircmd = !Flag.ocamlfind ^ " printconf stdlib" in
10 match Common.cmd_to_list sysdircmd with
11 [sysdir] -> sysdir
12 | _ -> raise (CompileFailure (sysdircmd ^" has failed"))
13
174d1640 14let check_cmd cmd =
c491d8ee
C
15 let (_,stat) = Common.cmd_to_list_and_status cmd in
16 match stat with
17 Unix.WEXITED 0 -> true
174d1640
C
18 | _ -> false
19
feec80c3
C
20(* this function does not work when the executable has an extension like .exe *)
21let to_opt cmd =
22 let n = String.length cmd in
23 if n > 4 && String.compare (String.sub cmd (n-4) 4) ".opt" == 0
24 then cmd
25 else cmd ^ ".opt"
26
174d1640 27let check_runtime () =
feec80c3
C
28 let has_opt = check_cmd (to_opt (!Flag.ocamlc) ^ " -version 2>&1 > /dev/null") in
29 let has_c = check_cmd (to_opt (!Flag.ocamlc) ^ " -version 2>&1 > /dev/null") in
174d1640
C
30 if has_opt then
31 begin
feec80c3
C
32 Flag.ocamlc := to_opt (!Flag.ocamlc);
33 Flag.ocamlopt := to_opt (!Flag.ocamlopt);
34 Flag.ocamldep := to_opt (!Flag.ocamldep);
174d1640
C
35 Common.pr2 "Using native version of ocamlc/ocamlopt/ocamldep"
36 end
37 else
38 if has_c then
39 Common.pr2 "Using bytecode version of ocamlc/ocamlopt/ocamldep"
40 else
feec80c3 41 if Config.dynlink_is_native then
174d1640
C
42 failwith
43 "No OCaml compiler found! Install either ocamlopt or ocamlopt.opt"
44 else
45 failwith
46 "No OCaml compiler found! Install either ocamlc or ocamlc.opt"
47
48let init_ocamlcocci _ =
49 "open Coccilib\n"
50
aba5c457
C
51let print_match ctr nm kind =
52 let endlet = "| _ -> failwith \"bad value\" in\n" in
53 let index = !ctr in
54 ctr := !ctr + 1;
55 Printf.sprintf
56 "let %s = match List.nth args %d with Coccilib.%s x -> x %s"
57 nm index kind endlet
58
59let string_rep_binding ctr = function
60 (Some nm,Ast.MetaPosDecl _) -> print_match ctr nm "Pos"
61 | (Some nm,Ast.MetaListlenDecl _) -> print_match ctr nm "Int"
62 | (Some nm,_) (* strings for everything else *) ->
63 print_match ctr nm "Str"
64 | (None,_) -> ""
65
66let ast_rep_binding ctr = function
67 (Some nm,Ast.MetaPosDecl _) ->
68 failwith
69 (Printf.sprintf "%s: No AST representation for position variables" nm)
b23ff9c7
C
70 | (Some nm,Ast.MetaMetaDecl _) ->
71 failwith
72 (Printf.sprintf
73 "%s: No AST representation for metavariables declared as \"%s\""
74 "metavariable" nm)
aba5c457
C
75 | (Some nm,Ast.MetaIdDecl _) -> print_match ctr nm "Str"
76 | (Some nm,Ast.MetaFreshIdDecl _) -> print_match ctr nm "Str"
77 | (Some nm,Ast.MetaTypeDecl _) -> print_match ctr nm "Type"
78 | (Some nm,Ast.MetaInitDecl _) -> print_match ctr nm "Init"
8f657093 79 | (Some nm,Ast.MetaInitListDecl _) -> print_match ctr nm "InitList"
aba5c457
C
80 | (Some nm,Ast.MetaListlenDecl _) ->
81 failwith
82 (Printf.sprintf "%s: No AST representation for listlen variables" nm)
83 | (Some nm,Ast.MetaParamDecl _) -> print_match ctr nm "Param"
84 | (Some nm,Ast.MetaParamListDecl _) -> print_match ctr nm "ParamList"
85 | (Some nm,Ast.MetaConstDecl _) -> print_match ctr nm "Expr"
86 | (Some nm,Ast.MetaErrDecl _) -> failwith ("not supported: "^nm)
87 | (Some nm,Ast.MetaExpDecl _) -> print_match ctr nm "Expr"
88 | (Some nm,Ast.MetaIdExpDecl _) -> print_match ctr nm "Expr"
89 | (Some nm,Ast.MetaLocalIdExpDecl _) -> print_match ctr nm "Expr"
90 | (Some nm,Ast.MetaExpListDecl _) -> print_match ctr nm "ExprList"
413ffc02
C
91 | (Some nm,Ast.MetaDeclDecl _) -> print_match ctr nm "Decl"
92 | (Some nm,Ast.MetaFieldDecl _) -> print_match ctr nm "Field"
190f1acf 93 | (Some nm,Ast.MetaFieldListDecl _) -> print_match ctr nm "FieldList"
aba5c457
C
94 | (Some nm,Ast.MetaStmDecl _) -> print_match ctr nm "Stmt"
95 | (Some nm,Ast.MetaStmListDecl _) -> failwith ("not supported: "^nm)
96 | (Some nm,Ast.MetaFuncDecl _) -> print_match ctr nm "Str"
97 | (Some nm,Ast.MetaLocalFuncDecl _) -> print_match ctr nm "Str"
98 | (Some nm,Ast.MetaDeclarerDecl _) -> print_match ctr nm "Str"
99 | (Some nm,Ast.MetaIteratorDecl _) -> print_match ctr nm "Str"
100 | (None,_) -> ""
101
413ffc02
C
102let manage_script_vars script_vars =
103 let rec loop n = function
104 [] -> ""
105 | (_,x)::xs ->
106 (Printf.sprintf "let %s = List.nth script_args %d in\n" x n) ^
107 (loop (n+1) xs) in
108 loop 0 script_vars
109
3a314143
C
110(* ---------------------------------------------------------------------- *)
111(* Iteration management *)
112
113let print_iteration_code o =
114 let translator l =
115 String.concat "\n | "
116 (List.map
117 (function x -> Printf.sprintf "%s -> \"%s\""
118 (String.capitalize x) x)
119 l) in
120 let add_virt_rules_method =
121 match !Iteration.parsed_virtual_rules with
122 [] -> ""
123 | l ->
124 Printf.sprintf "
125 method add_virtual_rule r =
126 let r = match r with %s in
127 virtual_rules <- Common.union_set [r] virtual_rules\n"
128 (translator l) in
129 let add_virt_ids_method =
130 match !Iteration.parsed_virtual_identifiers with
131 [] -> ""
132 | l ->
133 Printf.sprintf "
134 method add_virtual_identifier i v =
135 let i = match i with %s in
136 try
137 let v1 = List.assoc i virtual_identifiers in
138 if not (v = v1)
139 then failwith (\"multiple values specified for \"^i)
140 with Not_found ->
141 virtual_identifiers <- (i,v) :: virtual_identifiers"
142 (translator l) in
143 Printf.fprintf o "
144class iteration () =
145 object
146 val mutable files = None
147 val mutable files_changed = false
148 val mutable virtual_rules = ([] : string list)
149 val mutable virtual_identifiers = ([] : (string * string) list)
150 method set_files f = files <- Some f
151 %s%s
152 method register () =
153 Iteration.add_pending_instance (files,virtual_rules,virtual_identifiers)
154 end\n\n" add_virt_rules_method add_virt_ids_method
155
156(* ---------------------------------------------------------------------- *)
157
413ffc02 158let prepare_rule (name, metavars, script_vars, code) =
174d1640
C
159 let fname = String.concat "_" (Str.split (Str.regexp " ") name) in
160 (* function header *)
161 let function_header body =
413ffc02 162 Printf.sprintf "let %s args script_args =\n %s" fname body in
174d1640
C
163 (* parameter list *)
164 let build_parameter_list body =
165 let ctr = ref 0 in
aba5c457
C
166 let lets =
167 String.concat ""
168 (List.rev
169 (List.fold_left
170 (function prev ->
171 function ((str_nm,ast_nm),_,mv) ->
172 (* order important; ctr is incremented *)
173 let string_rep = string_rep_binding ctr (str_nm,mv) in
174 let ast_rep = ast_rep_binding ctr (ast_nm,mv) in
175 ast_rep :: string_rep :: prev)
176 [] metavars)) in
413ffc02 177 lets ^ (manage_script_vars script_vars) ^ body in
174d1640
C
178 (* add to hash table *)
179 let hash_add body =
180 Printf.sprintf
181 "%s\nlet _ = Hashtbl.add Coccilib.fcts \"%s\" %s\n" body name fname in
182 hash_add (function_header (build_parameter_list code))
183
184let prepare coccifile code =
185 let init_rules =
186 List.fold_left
187 (function prev ->
188 function
189 Ast_cocci.InitialScriptRule (name,"ocaml",deps,code) ->
190 code :: prev
191 | _ -> prev)
192 [] code in
193 let init_rules = List.rev init_rules in
194 let other_rules =
195 List.fold_left
196 (function prev ->
197 function
413ffc02
C
198 Ast_cocci.ScriptRule (name,"ocaml",deps,mv,script_vars,code) ->
199 (name,mv,script_vars,code) :: prev
174d1640
C
200 | Ast_cocci.InitialScriptRule (name,"ocaml",deps,code) -> prev
201 | Ast_cocci.FinalScriptRule (name,"ocaml",deps,code) ->
413ffc02 202 (name,[],[],code) :: prev
174d1640
C
203 | _ -> prev)
204 [] code in
205 let other_rules = List.rev other_rules in
206 if init_rules = [] && other_rules = []
207 then None
208 else
3a314143
C
209 begin
210 let basefile = Filename.basename (Filename.chop_extension coccifile) in
211 let basefile =
212 String.concat "_" (Str.split (Str.regexp "-") basefile) in
213 let (file,o) = Filename.open_temp_file basefile ".ml" in
174d1640 214 (* Global initialization *)
3a314143
C
215 Printf.fprintf o "%s\n" (init_ocamlcocci());
216 (* virtual rules and identifiers *)
217 (if !Iteration.parsed_virtual_rules != []
218 then
219 Printf.fprintf o "type __virtual_rules__ = %s\n\n"
220 (String.concat " | "
221 (List.map String.capitalize !Iteration.parsed_virtual_rules)));
222 (if !Iteration.parsed_virtual_identifiers != []
223 then
224 Printf.fprintf o "type __virtual_identifiers__ = %s\n\n"
225 (String.concat " | "
226 (List.map
227 (function x -> Printf.sprintf "%s" x)
228 (List.map String.capitalize
229 !Iteration.parsed_virtual_identifiers))));
230 print_iteration_code o;
174d1640
C
231 (* Semantic patch specific initialization *)
232 Printf.fprintf o "%s" (String.concat "\n\n" init_rules);
233 (* Semantic patch rules and finalizer *)
234 let rule_code = List.map prepare_rule other_rules in
3a314143
C
235 Printf.fprintf o "%s" (String.concat "\n\n" rule_code);
236 close_out o;
237 check_runtime ();
238 Some file
239 end
174d1640 240
aba5c457 241let filter_dep (accld, accinc) dep =
174d1640
C
242 match dep with
243 (* Built-in and OCaml defaults are filtered out *)
244 "Arg" | "Arith_status" | "Array" | "ArrayLabels" | "Big_int" | "Bigarray"
aba5c457
C
245 | "Buffer" | "Callback" | "CamlinternalLazy" | "CamlinternalMod"
246 | "CamlinternalOO"
247 | "Char" | "Complex" | "Condition" | "Digest" | "Dynlink" | "Event"
248 | "Filename"
249 | "Format" | "Gc" | "Genlex" | "GraphicsX11" | "Hashtbl" | "Int32"
250 | "Int64"
251 | "Lazy" | "Lexing" | "List" | "ListLabels" | "Map" | "Marshal"
252 | "MoreLabels" | "Mutex"
253 | "Nativeint" | "Num" | "Obj" | "Oo" | "Parsing" | "Pervasives"
254 | "Printexc" | "Printf"
255 | "Queue" | "Random" | "Scanf" | "Set" | "Sort" | "Stack" | "StdLabels"
256 | "Str" | "Stream"
257 | "String" | "StringLabels" | "Sys" | "ThreadUnix" | "Unix" | "UnixLabels"
174d1640
C
258 | "Weak"
259
260 (* Coccilib is filtered out too *)
3a314143 261 | "Coccilib" | "Common" | "Ast_c" | "Visitor_c" | "Lib_parsing_c"
97111a47 262 | "Iteration" | "Flag" ->
aba5c457
C
263 (accld, accinc)
264
265 | "Dbm" -> ("dbm"::accld, accinc)
266 | "Graphics" -> ("graphics"::accld, accinc)
267 | "Thread" -> ("thread"::accld, accinc)
268 | "Tk" -> ("tk"::accld, accinc)
174d1640
C
269
270 | _ ->
271 let l = Char.lowercase (String.get dep 0)in
272 String.set dep 0 l;
aba5c457
C
273 (accld, dep::accinc)
274
275let get_dir p =
276 let inclcmd = !Flag.ocamlfind ^" query "^p in
277 let dir = List.hd (Common.cmd_to_list inclcmd) in
278 (dir, p)
174d1640
C
279
280let parse_dep mlfile depout =
281 let re_colon = Str.regexp_string ":" in
282 match Str.split re_colon depout with
aba5c457
C
283 _::[dep] ->
284 let deplist = Str.split (Str.regexp_string " ") dep in
285 let (libs, orderdep) = List.fold_left filter_dep ([],[]) deplist in
286 if libs <> [] || orderdep <> [] then
ca417fcf
C
287 begin
288 if check_cmd (!Flag.ocamlfind ^ " printconf 2>&1 > /dev/null")
aba5c457 289 then
ca417fcf
C
290 let packages = List.rev orderdep in
291 let inclflags = List.map get_dir packages in
292 let intlib = List.map get_dir libs in
293 let alllibs = List.rev_append intlib inclflags in
294 let plist =
295 List.fold_left (fun acc (_,p) -> acc ^" "^p) "" alllibs in
296 let flags =
297 String.concat " " (List.map (fun (d,_) -> "-I "^d) inclflags) in
298 if flags <> "" || libs <> []
299 then
300 begin
301 Common.pr2
302 ("Extra OCaml packages used in the semantic patch:"^ plist);
303 (alllibs (* , inclflags *), flags)
304 end
305 else
306 raise
307 (CompileFailure
308 ("ocamlfind did not find "^
309 (if (List.length libs + List.length orderdep) = 1
310 then "this package:"
311 else "one of these packages:")^ plist))
174d1640 312 else
aba5c457 313 raise
ca417fcf
C
314 (CompileFailure ("ocamlfind not found but "^mlfile^" uses "^dep))
315 end
aba5c457
C
316 else
317 ([] (* , [] *), "")
318 | _ ->
319 raise
320 (CompileFailure ("Wrong dependencies for "^mlfile^" (Got "^depout^")"))
413ffc02 321
174d1640
C
322let dep_flag mlfile =
323 let depcmd = !Flag.ocamldep ^" -modules "^mlfile in
aba5c457
C
324 match Common.cmd_to_list depcmd with
325 [dep] -> parse_dep mlfile dep
1eddfd50
C
326 | err ->
327 List.iter (function x -> Common.pr2 (x^"\n")) err;
328 raise (CompileFailure ("Failed ocamldep for "^mlfile))
413ffc02 329
174d1640
C
330let compile_bytecode_cmd flags mlfile =
331 let obj = (Filename.chop_extension mlfile) ^ ".cmo" in
aba5c457 332 (obj, Printf.sprintf "%s -c %s %s %s" !Flag.ocamlc obj flags mlfile)
413ffc02 333
174d1640
C
334let compile_native_cmd flags mlfile =
335 let obj = (Filename.chop_extension mlfile) ^ ".cmxs" in
aba5c457
C
336 (obj,
337 Printf.sprintf "%s -shared -o %s %s %s" !Flag.ocamlopt obj flags mlfile)
174d1640
C
338
339let compile mlfile cmd =
340 Common.pr2 cmd;
341 match Sys.command cmd with
342 0 -> ()
343 | _ -> raise (CompileFailure mlfile)
344
aba5c457
C
345let rec load_obj obj =
346 try
347 Dynlink.loadfile obj
348 with Dynlink.Error e ->
349 match e with
350 Dynlink.Unsafe_file ->
351 Dynlink.allow_unsafe_modules true;
352 load_obj obj
353 | _ ->
354 Common.pr2 (Dynlink.error_message e);
355 raise (LinkFailure obj)
356
357(*
358let link_lib (dir, name) = name ^ ext
359
360let link_libs libs =
361 String.concat " " (List.map link_lib libs)
362*)
363
364let load_lib (dir, name) =
365 let obj = dir ^ "/" ^name ^ ext in
366 Common.pr2 ("Loading "^ obj ^"...");
367 load_obj obj
368
369let load_libs libs =
370 List.iter load_lib libs
371
174d1640 372let load_file mlfile =
aba5c457
C
373 let (ldlibs (* , lklibs *), inc) = dep_flag mlfile in
374(* let linklibs = link_libs lklibs in *)
375 let flags =
376 Printf.sprintf
3a314143 377 "-thread -g -dtypes -I %s %s -I %s/globals -I %s/ocaml -I %s/parsing_c -I %s/commons "
8babbc8f 378 (sysdir ()) inc Config.path Config.path Config.path Config.path in
174d1640 379 let (obj, cmd) =
feec80c3 380 if Config.dynlink_is_native
174d1640 381 then compile_native_cmd flags mlfile
aba5c457
C
382 else compile_bytecode_cmd flags mlfile in
383 compile mlfile cmd;
384 Common.pr2 "Compilation OK!";
385 load_libs ldlibs;
386 Common.pr2 "Loading ML code of the SP...";
387 try Dynlink.loadfile obj
388 with Dynlink.Error e ->
389 Common.pr2 (Dynlink.error_message e);
390 raise (LinkFailure obj)
174d1640
C
391
392let clean_file mlfile =
393 let basefile = Filename.chop_extension mlfile in
394 let files =
feec80c3 395 if Config.dynlink_is_native then
174d1640
C
396 [basefile ^ ".cmxs";
397 basefile ^ ".cmx";
aba5c457
C
398 basefile ^ ".o";
399 basefile ^ ".annot"]
174d1640 400 else
aba5c457
C
401 [basefile ^ ".cmo";
402 basefile ^ ".annot"]
174d1640 403 in
993936c0 404 if not !Flag_parsing_cocci.keep_ml_script then Sys.remove mlfile;
174d1640 405 Sys.remove (basefile^".cmi");
aba5c457 406 List.iter (fun f -> try Sys.remove f with _ -> ()) files
174d1640
C
407
408(*
409 This function is used in testing.ml.
410 Once the ML file is compiled and loaded,
411 newly available functions are reported here.
412*)
413let test () =
414 Hashtbl.iter
415 (fun key fct ->
416 Common.pr2 ("Fct registered: \""^key^"\"")
417 ) Coccilib.fcts