]> matita.cs.unibo.it Git - helm.git/blob - helm/software/matita/dist/static_link/static_link.ml
Bugs (mostly from Oliboni)
[helm.git] / helm / software / matita / dist / static_link / static_link.ml
1
2 open Printf
3
4 exception Found of string list
5
6 let ocamlobjinfo = "ocamlobjinfo"
7 let noautolink = "-noautolink"
8 let dummy_opt_cmd = "dummy_ocamlopt"
9 let opt_cmd = "ocamlopt"
10 let libdirs = [ "/lib"; "/usr/lib"; "/usr/lib/gcc/i486-linux-gnu/4.0.2" ]
11 let exceptions = [ "threads.cma", [ "-lthreads", "-lthreadsnat" ] ]
12
13 let blanks_RE = Str.regexp "[ \t\r\n]+"
14 let cmxa_RE = Str.regexp "\\.cmxa$"
15 let extra_cfiles_RE = Str.regexp "^.*Extra +C +object +files:\\(.*\\)$"
16 let extra_copts_RE = Str.regexp "^.*Extra +C +options:\\(.*\\)$"
17 let lib_RE = Str.regexp "^lib"
18 let l_RE = Str.regexp "^-l"
19 let opt_line_RE = Str.regexp (sprintf "^\\+ +%s +\\(.*\\)$" dummy_opt_cmd)
20 let trailing_cmxa_RE = Str.regexp ".*\\.cmxa$"
21
22 let message s = prerr_endline ("STATIC_LINK: " ^ s)
23 let warning s = message ("WARNING: " ^ s)
24
25 let handle_exceptions ~cma cflag =
26   try
27     let cma_exns = List.assoc (Filename.basename cma) exceptions in
28     let cflag' = List.assoc cflag cma_exns in
29     message (sprintf "using %s exception %s -> %s" cma cflag cflag');
30     cflag'
31   with Not_found -> cflag
32
33 let parse_cmdline () =
34   let mine, rest = ref [], ref [] in
35   let is_mine = ref true in
36   Array.iter
37     (function
38       | "--" -> is_mine := false
39       | s when !is_mine ->
40           if Str.string_match lib_RE s 0 then
41             warning (sprintf
42               ("libraries to be statically linked must be specified "
43               ^^ "without heading \"lib\", \"%s\" argument may be wrong") s);
44           mine := s :: !mine
45       | s -> rest := s :: !rest)
46     Sys.argv;
47   if !rest = [] then begin
48     prerr_endline "Usage:   static_link [ CLIB .. ] -- COMMAND [ ARG .. ]";
49     prerr_endline ("Example: static_link pcre expat --"
50       ^ " ocamlfind opt -package pcre,expat -linkpkg -o foo foo.ml");
51     exit 0
52   end;
53   List.tl (List.rev !mine), List.rev !rest
54
55 let extract_opt_flags cmd =
56   let ic = Unix.open_process_in cmd in
57   (try
58     while true do
59       let l = input_line ic in
60       if Str.string_match opt_line_RE l 0 then begin
61         message ("got ocamlopt line: " ^ l);
62         raise (Found (Str.split blanks_RE (Str.matched_group 1 l)));
63       end
64     done;
65     []  (* dummy value *)
66   with
67   | End_of_file -> failwith "compiler command not found"
68   | Found flags ->
69       close_in ic;
70       flags)
71
72 let cma_of_cmxa = Str.replace_first cmxa_RE ".cma"
73
74 let find_clib libname =
75   let rec aux =
76     function
77     | [] -> raise Not_found
78     | libdir :: tl ->
79         let fname = sprintf "%s/lib%s.a" libdir libname in
80         if Sys.file_exists fname then fname else aux tl
81   in
82   aux libdirs
83
84 let a_of_cflag cflag =  (* "-lfoo" -> "/usr/lib/libfoo.a" *)
85   let libname = Str.replace_first l_RE "" cflag in
86   find_clib libname
87
88 let cflags_of_cma fname =
89   let ic = Unix.open_process_in (sprintf "%s %s" ocamlobjinfo fname) in
90   let extra_copts = ref "" in
91   let extra_cfiles = ref "" in
92   (try
93     while true do
94       match input_line ic with
95       | s when Str.string_match extra_copts_RE s 0 ->
96           extra_copts := Str.matched_group 1 s
97       | s when Str.string_match extra_cfiles_RE s 0 ->
98           extra_cfiles := Str.matched_group 1 s
99       | _ -> ()
100     done
101   with End_of_file -> ());
102   close_in ic;
103   let extra_cfiles = List.rev (Str.split blanks_RE !extra_cfiles) in
104   let extra_copts = Str.split blanks_RE !extra_copts in
105   extra_copts @ extra_cfiles
106
107 let staticize static_libs flags =
108   let static_flags = List.map ((^) "-l") static_libs in
109   let aux ~add_cclib ~cma cflag =
110     let cflag =
111       if List.mem cflag static_flags
112       then
113         (try
114           let a = a_of_cflag cflag in
115           message (sprintf "using static %s instead of shared %s" a cflag);
116           a
117         with Not_found -> warning ("can't find lib for " ^ cflag); cflag)
118       else (handle_exceptions ~cma cflag)
119     in
120     if add_cclib then [ "-cclib"; cflag ] else [ cflag ]
121   in
122   List.fold_right
123     (fun flag acc ->
124       let cma = cma_of_cmxa flag in
125       if Str.string_match trailing_cmxa_RE flag 0 then begin
126         message ("processing native archive: " ^ flag);
127         let cflags = cflags_of_cma cma in
128         let cflags' =
129           List.fold_right
130             (fun cflag acc -> (aux ~add_cclib:true ~cma cflag) @ acc)
131             cflags []
132         in
133         flag :: (cflags' @ acc)
134       end else
135         (aux ~add_cclib:false ~cma flag) @ acc)
136     flags []
137
138 let quote_if_needed s =
139   try
140     ignore (Str.search_forward blanks_RE s 0);
141     "\"" ^ s ^ "\""
142   with Not_found -> s
143
144 let main () =
145   let static_libs, args = parse_cmdline () in
146   printf "C libraries to be linked-in: %s\n" (String.concat " " static_libs);
147   flush stdout;
148   let verbose_cmd =
149     sprintf "OCAMLFIND_COMMANDS='ocamlopt=%s' %s -verbose 2>&1" dummy_opt_cmd
150       (String.concat " " (List.map quote_if_needed args))
151   in
152   let orig_opt_flags = extract_opt_flags verbose_cmd in
153   message ("original ocamlopt flags: " ^ String.concat " " orig_opt_flags);
154   let opt_flags = staticize static_libs orig_opt_flags in
155   message ("new ocamlopt flags: " ^ String.concat " " opt_flags);
156   let flags = noautolink :: opt_flags in
157   let cmd = String.concat " " (opt_cmd :: flags) in
158   message ("executing command: " ^ cmd);
159   exit (Sys.command cmd)
160
161 let _ = main ()
162