1 (* Note: this module passes paths to other commands, but does not take
2 * quoting into account. Thus, if these paths contain spaces, it's likely
3 * that things go wrong.
8 exception CompileFailure
of string
9 exception LinkFailure
of string
11 let ext = if Config.dynlink_is_native
then ".cmxs" else ".cma"
14 let sysdircmd = !Flag.ocamlfind ^
" printconf stdlib" in
15 match Common.cmd_to_list
sysdircmd with
17 | _
-> raise
(CompileFailure
(sysdircmd ^
" has failed"))
20 let (_
,stat
) = Common.cmd_to_list_and_status cmd
in
22 Unix.WEXITED
0 -> true
25 (* this function does not work when the executable has an extension like .exe *)
27 let n = String.length cmd
in
28 if n > 4 && String.compare
(String.sub cmd
(n-4) 4) ".opt" == 0
32 let check_runtime () =
33 let has_opt = check_cmd (to_opt (!Flag.ocamlc
) ^
" -version 2>&1 > /dev/null") in
34 let has_c = check_cmd (!Flag.ocamlc ^
" -version 2>&1 > /dev/null") in
37 Flag.ocamlc
:= to_opt (!Flag.ocamlc
);
38 Flag.ocamlopt
:= to_opt (!Flag.ocamlopt
);
39 Flag.ocamldep
:= to_opt (!Flag.ocamldep
);
40 Common.pr2
"Using native version of ocamlc/ocamlopt/ocamldep"
44 Common.pr2
"Using bytecode version of ocamlc/ocamlopt/ocamldep"
46 if Config.dynlink_is_native
then
48 "No OCaml compiler found! Install either ocamlopt or ocamlopt.opt"
51 "No OCaml compiler found! Install either ocamlc or ocamlc.opt"
53 let init_ocamlcocci _
=
56 let print_match ctr nm kind
=
57 let endlet = "| _ -> failwith \"bad value\" in\n" in
61 "let %s = match List.nth args %d with Coccilib.%s x -> x %s"
64 let string_rep_binding ctr
= function
65 (Some nm
,Ast.MetaPosDecl _
) -> print_match ctr nm
"Pos"
66 | (Some nm
,Ast.MetaListlenDecl _
) -> print_match ctr nm
"Int"
67 | (Some nm
,_
) (* strings for everything else *) ->
68 print_match ctr nm
"Str"
71 let ast_rep_binding ctr
= function
72 (Some nm
,Ast.MetaPosDecl _
) ->
74 (Printf.sprintf
"%s: No AST representation for position variables" nm
)
75 | (Some nm
,Ast.MetaMetaDecl _
) ->
78 "%s: No AST representation for metavariables declared as \"%s\""
80 | (Some nm
,Ast.MetaIdDecl _
) -> print_match ctr nm
"Str"
81 | (Some nm
,Ast.MetaFreshIdDecl _
) -> print_match ctr nm
"Str"
82 | (Some nm
,Ast.MetaTypeDecl _
) -> print_match ctr nm
"Type"
83 | (Some nm
,Ast.MetaInitDecl _
) -> print_match ctr nm
"Init"
84 | (Some nm
,Ast.MetaInitListDecl _
) -> print_match ctr nm
"InitList"
85 | (Some nm
,Ast.MetaListlenDecl _
) ->
87 (Printf.sprintf
"%s: No AST representation for listlen variables" nm
)
88 | (Some nm
,Ast.MetaParamDecl _
) -> print_match ctr nm
"Param"
89 | (Some nm
,Ast.MetaParamListDecl _
) -> print_match ctr nm
"ParamList"
90 | (Some nm
,Ast.MetaConstDecl _
) -> print_match ctr nm
"Expr"
91 | (Some nm
,Ast.MetaErrDecl _
) -> failwith
("not supported: "^nm
)
92 | (Some nm
,Ast.MetaExpDecl _
) -> print_match ctr nm
"Expr"
93 | (Some nm
,Ast.MetaIdExpDecl _
) -> print_match ctr nm
"Expr"
94 | (Some nm
,Ast.MetaLocalIdExpDecl _
) -> print_match ctr nm
"Expr"
95 | (Some nm
,Ast.MetaExpListDecl _
) -> print_match ctr nm
"ExprList"
96 | (Some nm
,Ast.MetaDeclDecl _
) -> print_match ctr nm
"Decl"
97 | (Some nm
,Ast.MetaFieldDecl _
) -> print_match ctr nm
"Field"
98 | (Some nm
,Ast.MetaFieldListDecl _
) -> print_match ctr nm
"FieldList"
99 | (Some nm
,Ast.MetaStmDecl _
) -> print_match ctr nm
"Stmt"
100 | (Some nm
,Ast.MetaStmListDecl _
) -> failwith
("not supported: "^nm
)
101 | (Some nm
,Ast.MetaFuncDecl _
) -> print_match ctr nm
"Str"
102 | (Some nm
,Ast.MetaLocalFuncDecl _
) -> print_match ctr nm
"Str"
103 | (Some nm
,Ast.MetaDeclarerDecl _
) -> print_match ctr nm
"Str"
104 | (Some nm
,Ast.MetaIteratorDecl _
) -> print_match ctr nm
"Str"
107 let manage_script_vars script_vars
=
108 let rec loop n = function
111 (Printf.sprintf
"let %s = List.nth script_args %d in\n" x
n) ^
115 (* ---------------------------------------------------------------------- *)
116 (* Iteration management *)
118 let print_iteration_code o
=
120 String.concat
"\n | "
122 (function x
-> Printf.sprintf
"%s -> \"%s\""
123 (String.capitalize x
) x
)
125 let add_virt_rules_method =
126 match !Iteration.parsed_virtual_rules
with
130 method add_virtual_rule r =
131 let r = match r with %s in
132 virtual_rules <- Common.union_set [r] virtual_rules\n"
134 let add_virt_ids_method =
135 match !Iteration.parsed_virtual_identifiers
with
139 method add_virtual_identifier i v =
140 let i = match i with %s in
142 let v1 = List.assoc i virtual_identifiers in
144 then failwith (\"multiple values specified for \"^i)
146 virtual_identifiers <- (i,v) :: virtual_identifiers"
151 val mutable files = None
152 val mutable files_changed = false
153 val mutable virtual_rules = ([] : string list)
154 val mutable virtual_identifiers = ([] : (string * string) list)
155 method set_files f = files <- Some f
158 Iteration.add_pending_instance (files,virtual_rules,virtual_identifiers)
159 end\n\n" add_virt_rules_method add_virt_ids_method
161 (* ---------------------------------------------------------------------- *)
163 let prepare_rule (name
, metavars
, script_vars
, code
) =
164 let fname = String.concat
"_" (Str.split
(Str.regexp
" ") name
) in
165 (* function header *)
166 let function_header body
=
167 Printf.sprintf
"let %s args script_args =\n %s" fname body
in
169 let build_parameter_list body
=
176 function ((str_nm
,ast_nm
),_
,mv
) ->
177 (* order important; ctr is incremented *)
178 let string_rep = string_rep_binding ctr (str_nm
,mv
) in
179 let ast_rep = ast_rep_binding ctr (ast_nm
,mv
) in
180 ast_rep :: string_rep :: prev
)
182 lets ^
(manage_script_vars script_vars
) ^ body
in
183 (* add to hash table *)
186 "%s\nlet _ = Hashtbl.add Coccilib.fcts \"%s\" %s\n" body name
fname in
187 hash_add (function_header (build_parameter_list code
))
189 let prepare coccifile code
=
194 Ast_cocci.InitialScriptRule
(name
,"ocaml",deps
,code
) ->
198 let init_rules = List.rev
init_rules in
203 Ast_cocci.ScriptRule
(name
,"ocaml",deps
,mv
,script_vars
,code
) ->
204 (name
,mv
,script_vars
,code
) :: prev
205 | Ast_cocci.InitialScriptRule
(name
,"ocaml",deps
,code
) -> prev
206 | Ast_cocci.FinalScriptRule
(name
,"ocaml",deps
,code
) ->
207 (name
,[],[],code
) :: prev
210 let other_rules = List.rev
other_rules in
211 if init_rules = [] && other_rules = []
215 let basefile = Filename.basename
(Filename.chop_extension coccifile
) in
217 String.concat
"_" (Str.split
(Str.regexp
"-") basefile) in
218 let (file
,o
) = Filename.open_temp_file
basefile ".ml" in
219 (* Global initialization *)
220 Printf.fprintf o
"%s\n" (init_ocamlcocci());
221 (* virtual rules and identifiers *)
222 (if !Iteration.parsed_virtual_rules
!= []
224 Printf.fprintf o
"type __virtual_rules__ = %s\n\n"
226 (List.map
String.capitalize
!Iteration.parsed_virtual_rules
)));
227 (if !Iteration.parsed_virtual_identifiers
!= []
229 Printf.fprintf o
"type __virtual_identifiers__ = %s\n\n"
232 (function x
-> Printf.sprintf
"%s" x
)
233 (List.map
String.capitalize
234 !Iteration.parsed_virtual_identifiers
))));
235 print_iteration_code o
;
236 (* Semantic patch specific initialization *)
237 Printf.fprintf o
"%s" (String.concat
"\n\n" init_rules);
238 (* Semantic patch rules and finalizer *)
239 let rule_code = List.map
prepare_rule other_rules in
240 Printf.fprintf o
"%s" (String.concat
"\n\n" rule_code);
246 (* give a path to the coccilib cmi file *)
247 let find_cmifile name
=
248 let path1 = Printf.sprintf
"%s/ocaml/%s.cmi" Config.path name
in
249 if Sys.file_exists
path1 then path1 else
250 let path2 = Printf.sprintf
"%s/ocaml/coccilib/%s.cmi" Config.path name
in
251 if Sys.file_exists
path2 then path2 else
252 raise
(CompileFailure
("No coccilib.cmi in " ^
path1 ^
" or " ^
path2))
254 (* extract upper case identifiers from the cmi file. This will be an
255 * approximation of the modules referenced by the coccilib, which are
256 * thus present in the application and do not need to be loaded by
257 * the dynamic linker.
260 module ModuleSet
= Set.Make
(String
)
262 let approx_coccilib_deps cmi
=
263 let chan = open_in_bin cmi
in
264 let tbl = Hashtbl.create
1024 in
265 let buf = Buffer.create
140 in
269 let c = input_char
chan in
270 let has_ident = Buffer.length
buf > 0 in
273 if (c >= 'a'
&& c <= 'z'
) ||
274 (c >= 'A'
&& c <= 'Z'
) ||
275 (c >= '
0'
&& c <= '
9'
) ||
276 c == '
_'
|| c == '
\''
277 then Buffer.add_char
buf c
279 if Buffer.length
buf >= 3
281 let key = Buffer.contents
buf in
282 if Hashtbl.mem
tbl key
284 else Hashtbl.add
tbl (Buffer.contents
buf) ()
290 if c >= 'A'
&& c <= 'Z'
291 then (* perhaps the begin of a captialized identifier *)
292 Buffer.add_char
buf c
296 with End_of_file
-> ()
301 let filter_dep existing_deps
(accld
, accinc
) dep
=
302 if Hashtbl.mem existing_deps dep
303 then (accld
, accinc
) (* skip an existing dep *)
305 (* Built-in and OCaml defaults are filtered out *)
306 "Arg" | "Arith_status" | "Array" | "ArrayLabels" | "Big_int" | "Bigarray"
307 | "Buffer" | "Callback" | "CamlinternalLazy" | "CamlinternalMod"
309 | "Char" | "Complex" | "Condition" | "Digest" | "Dynlink" | "Event"
311 | "Format" | "Gc" | "Genlex" | "GraphicsX11" | "Hashtbl" | "Int32"
313 | "Lazy" | "Lexing" | "List" | "ListLabels" | "Map" | "Marshal"
314 | "MoreLabels" | "Mutex"
315 | "Nativeint" | "Num" | "Obj" | "Oo" | "Parsing" | "Pervasives"
316 | "Printexc" | "Printf"
317 | "Queue" | "Random" | "Scanf" | "Set" | "Sort" | "Stack" | "StdLabels"
319 | "String" | "StringLabels" | "Sys" | "ThreadUnix" | "Unix" | "UnixLabels"
320 | "Weak" -> (accld
, accinc
)
321 | "Dbm" -> ("dbm"::accld
, accinc
)
322 | "Graphics" -> ("graphics"::accld
, accinc
)
323 | "Thread" -> ("thread"::accld
, accinc
)
324 | "Tk" -> ("tk"::accld
, accinc
)
327 let l = Char.lowercase
(String.get dep
0)in
332 let inclcmd = !Flag.ocamlfind ^
" query "^p
in
333 let dir = List.hd
(Common.cmd_to_list
inclcmd) in
336 let parse_dep cmifile mlfile depout
=
337 let empty_deps = ([], "") in
338 let existing_deps = approx_coccilib_deps cmifile
in
339 let re_colon = Str.regexp_string
":" in
340 match Str.split
re_colon depout
with
342 let deplist = Str.split
(Str.regexp_string
" ") dep
in
343 let (libs
, orderdep
) = List.fold_left
(filter_dep existing_deps) ([],[]) deplist in
344 if libs
<> [] || orderdep
<> [] then
346 if check_cmd (!Flag.ocamlfind ^
" printconf 2>&1 > /dev/null")
348 let packages = List.rev orderdep
in
349 let inclflags = List.map
get_dir packages in
350 let intlib = List.map
get_dir libs
in
351 let alllibs = List.rev_append
intlib inclflags in
353 List.fold_left
(fun acc
(_,p
) -> acc ^
" "^p
) "" alllibs in
355 String.concat
" " (List.map
(fun (d
,_) -> "-I "^d
) inclflags) in
356 if flags <> "" || libs
<> []
359 ("Extra OCaml packages used in the semantic patch:"^
plist);
363 Common.pr2
("Warning: ocamlfind did not find "^
364 (if (List.length libs
+ List.length orderdep
) = 1
366 else "one of these packages:")^
plist);
370 Common.pr2
("Warning: ocamlfind not found but "^mlfile^
" uses "^dep
);
378 (CompileFailure
("Wrong dependencies for "^mlfile^
" (Got "^depout^
")"))
380 let dep_flag cmifile mlfile
=
381 let depcmd = !Flag.ocamldep ^
" -modules "^mlfile
in
382 match Common.cmd_to_list
depcmd with
383 [dep
] -> parse_dep cmifile mlfile dep
385 List.iter
(function x
-> Common.pr2
(x^
"\n")) err
;
386 raise
(CompileFailure
("Failed ocamldep for "^mlfile
))
388 let compile_bytecode_cmd flags mlfile
=
389 let obj = (Filename.chop_extension mlfile
) ^
".cmo" in
390 (obj, Printf.sprintf
"%s -c %s %s %s" !Flag.ocamlc
obj flags mlfile
)
392 let compile_native_cmd flags mlfile
=
393 let obj = (Filename.chop_extension mlfile
) ^
".cmxs" in
395 Printf.sprintf
"%s -shared -o %s %s %s" !Flag.ocamlopt
obj flags mlfile
)
397 let compile mlfile cmd
=
399 match Sys.command cmd
with
401 | _ -> raise
(CompileFailure mlfile
)
404 Dynlink.allow_unsafe_modules
true;
405 try Dynlink.loadfile
obj
406 with Dynlink.Error e
->
407 Common.pr2
(Dynlink.error_message e
);
408 raise
(LinkFailure
obj)
410 let load_lib (dir, name
) =
411 let obj = dir ^
"/" ^name ^
ext in
412 Common.pr2
("Loading "^
obj ^
"...");
416 List.iter
load_lib libs
418 let load_file mlfile
=
419 let cmifile = find_cmifile "coccilib" in
420 let (ldlibs
, inc
) = dep_flag cmifile mlfile
in
421 (* add ocaml and ocaml/coccilib as search directories for the ocaml scripting *)
425 (sysdir ()) inc
(Filename.dirname
cmifile) in
427 if Config.dynlink_is_native
428 then compile_native_cmd flags mlfile
429 else compile_bytecode_cmd flags mlfile
in
431 Common.pr2
"Compilation OK!";
433 Common.pr2
"Loading ML code of the SP...";
436 let clean_file mlfile
=
437 let basefile = Filename.chop_extension mlfile
in
439 if Config.dynlink_is_native
then
448 if not
!Flag_parsing_cocci.keep_ml_script
then Sys.remove mlfile
;
449 Sys.remove
(basefile^
".cmi");
450 List.iter
(fun f
-> try Sys.remove f
with _ -> ()) files
453 This function is used in testing.ml.
454 Once the ML file is compiled and loaded,
455 newly available functions are reported here.
460 Common.pr2
("Fct registered: \""^
key^
"\"")