1 (**************************************************************************)
2 (* *)
3 (* OCaml *)
4 (* *)
5 (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
6 (* *)
7 (* Copyright 2001 Institut National de Recherche en Informatique et *)
8 (* en Automatique. *)
9 (* *)
10 (* All rights reserved. This file is distributed under the terms of *)
11 (* the GNU Lesser General Public License version 2.1, with the *)
12 (* special exception on linking described in the file LICENSE. *)
13 (* *)
14 (**************************************************************************)
15
16 open Printf
17 open Ocamlmklibconfig
18
19 let syslib x =
20 if Config.ccomp_type = "msvc" then x ^ ".lib" else "-l" ^ x
21
22 let mklib out files opts =
23 if Config.ccomp_type = "msvc"
24 then let machine =
25 if Config.architecture="amd64"
26 then "-machine:AMD64 "
27 else ""
28 in
29 Printf.sprintf "link -lib -nologo %s-out:%s %s %s"
30 machine out opts files
31 else Printf.sprintf "%s rcs %s %s %s && %s %s"
32 Config.ar out opts files Config.ranlib out
33
34 (* PR#4783: under Windows, don't use absolute paths because we do
35 not know where the binary distribution will be installed. *)
36 let compiler_path name =
37 if Sys.os_type = "Win32" then name else Filename.concat bindir name
38
39 let bytecode_objs = ref [] (* .cmo,.cma,.ml,.mli files to pass to ocamlc *)
40 and native_objs = ref [] (* .cmx,.ml,.mli files to pass to ocamlopt *)
41 and c_objs = ref [] (* .o, .a, .obj, .lib, .dll, .dylib, .so files to
42 pass to mksharedlib and ar *)
43 and caml_libs = ref [] (* -cclib to pass to ocamlc, ocamlopt *)
44 and caml_opts = ref [] (* -ccopt to pass to ocamlc, ocamlopt *)
45 and dynlink = ref supports_shared_libraries
46 and failsafe = ref false (* whether to fall back on static build only *)
47 and c_libs = ref [] (* libs to pass to mksharedlib and ocamlc -cclib *)
48 and c_Lopts = ref [] (* options to pass to mksharedlib and ocamlc -cclib *)
49 and c_opts = ref [] (* options to pass to mksharedlib and ocamlc -ccopt *)
50 and ld_opts = ref [] (* options to pass only to the linker *)
51 and ocamlc = ref (compiler_path "ocamlc")
52 and ocamlc_opts = ref [] (* options to pass only to ocamlc *)
53 and ocamlopt = ref (compiler_path "ocamlopt")
54 and ocamlopt_opts = ref [] (* options to pass only to ocamlc *)
55 and output = ref "a" (* Output name for OCaml part of library *)
56 and output_c = ref "" (* Output name for C part of library *)
57 and rpath = ref [] (* rpath options *)
58 and debug = ref false (* -g option *)
59 and verbose = ref false
60
61 let starts_with s pref =
62 String.length s >= String.length pref &&
63 String.sub s 0 (String.length pref) = pref
64 let ends_with = Filename.check_suffix
65 let chop_prefix s pref =
66 String.sub s (String.length pref) (String.length s - String.length pref)
67 let chop_suffix = Filename.chop_suffix
68
69 exception Bad_argument of string
70
71 let print_version () =
72 printf "ocamlmklib, version %s\n" Sys.ocaml_version;
73 exit 0;
74 ;;
75
76 let print_version_num () =
77 printf "%s\n" Sys.ocaml_version;
78 exit 0;
79 ;;
80
81 let parse_arguments argv =
82 let args = Stack.create () in
83 let push_args ~first arr =
84 for i = Array.length arr - 1 downto first do
85 Stack.push arr.(i) args
86 done
87 in
88 let next_arg s =
89 if Stack.is_empty args
90 then raise (Bad_argument("Option " ^ s ^ " expects one argument"));
91 Stack.pop args
92 in
93 push_args ~first:1 argv;
94 while not (Stack.is_empty args) do
95 let s = Stack.pop args in
96 if s = "-args" then
97 push_args ~first:0 (Arg.read_arg (next_arg s))
98 else if s = "-args0" then
99 push_args ~first:0 (Arg.read_arg0 (next_arg s))
100 else if ends_with s ".cmo" || ends_with s ".cma" then
101 bytecode_objs := s :: !bytecode_objs
102 else if ends_with s ".cmx" then
103 native_objs := s :: !native_objs
104 else if ends_with s ".ml" || ends_with s ".mli" then
105 (bytecode_objs := s :: !bytecode_objs;
106 native_objs := s :: !native_objs)
107 else if List.exists (ends_with s)
108 [".o"; ".a"; ".obj"; ".lib"; ".dll"; ".dylib"; ".so"]
109 then
110 c_objs := s :: !c_objs
111 else if s = "-cclib" then
112 caml_libs := next_arg s :: "-cclib" :: !caml_libs
113 else if s = "-ccopt" then
114 caml_opts := next_arg s :: "-ccopt" :: !caml_opts
115 else if s = "-custom" then
116 dynlink := false
117 else if s = "-I" then
118 caml_opts := next_arg s :: "-I" :: !caml_opts
119 else if s = "-failsafe" then
120 failsafe := true
121 else if s = "-g" then
122 debug := true
123 else if s = "-h" || s = "-help" || s = "--help" then
124 raise (Bad_argument "")
125 else if s = "-ldopt" then
126 ld_opts := next_arg s :: !ld_opts
127 else if s = "-linkall" then
128 caml_opts := s :: !caml_opts
129 else if starts_with s "-l" then
130 let s =
131 if Config.ccomp_type = "msvc" then
132 String.sub s 2 (String.length s - 2) ^ ".lib"
133 else
134 s
135 in
136 c_libs := s :: !c_libs
137 else if starts_with s "-L" then
138 (c_Lopts := s :: !c_Lopts;
139 let l = chop_prefix s "-L" in
140 if not (Filename.is_relative l) then rpath := l :: !rpath)
141 else if s = "-ocamlcflags" then
142 ocamlc_opts := next_arg s :: !ocamlc_opts
143 else if s = "-ocamlc" then
144 ocamlc := next_arg s
145 else if s = "-ocamlopt" then
146 ocamlopt := next_arg s
147 else if s = "-ocamloptflags" then
148 ocamlopt_opts := next_arg s :: !ocamlopt_opts
149 else if s = "-o" then
150 output := next_arg s
151 else if s = "-oc" then
152 output_c := next_arg s
153 else if s = "-dllpath" || s = "-R" || s = "-rpath" then
154 rpath := next_arg s :: !rpath
155 else if starts_with s "-R" then
156 rpath := chop_prefix s "-R" :: !rpath
157 else if s = "-Wl,-rpath" then
158 (let a = next_arg s in
159 if starts_with a "-Wl,"
160 then rpath := chop_prefix a "-Wl," :: !rpath
161 else raise (Bad_argument("Option -Wl,-rpath expects a -Wl, argument")))
162 else if starts_with s "-Wl,-rpath," then
163 rpath := chop_prefix s "-Wl,-rpath," :: !rpath
164 else if starts_with s "-Wl,-R" then
165 rpath := chop_prefix s "-Wl,-R" :: !rpath
166 else if s = "-v" || s = "-verbose" then
167 verbose := true
168 else if s = "-version" then
169 print_version ()
170 else if s = "-vnum" then
171 print_version_num ()
172 else if starts_with s "-F" then
173 c_opts := s :: !c_opts
174 else if s = "-framework" then
175 (let a = next_arg s in c_opts := a :: s :: !c_opts)
176 else if starts_with s "-" then
177 prerr_endline ("Unknown option " ^ s)
178 else
179 raise (Bad_argument("Don't know what to do with " ^ s))
180 done;
181 List.iter
182 (fun r -> r := List.rev !r)
183 [ bytecode_objs; native_objs; caml_libs; caml_opts;
184 c_libs; c_objs; c_opts; ld_opts; rpath ];
185 (* Put -L options in front of -l options in -cclib to mimic -ccopt behavior *)
186 c_libs := !c_Lopts @ !c_libs;
187
188 if !output_c = "" then output_c := !output
189
190 let usage = "\
191 Usage: ocamlmklib [options] <.cmo|.cma|.cmx|.ml|.mli|.o|.a|.obj|.lib|\
192 .dll|.dylib files>\
193 \nOptions are:\
194 \n -args <file> Read additional newline-terminated command line arguments\
195 \n from <file>\
196 \n -args0 <file> Read additional null character terminated command line\
197 \n arguments from <file>\
198 \n -cclib <lib> C library passed to ocamlc -a or ocamlopt -a only\
199 \n -ccopt <opt> C option passed to ocamlc -a or ocamlopt -a only\
200 \n -custom Disable dynamic loading\
201 \n -g Build with debug information\
202 \n -dllpath <dir> Add <dir> to the run-time search path for DLLs\
203 \n -F<dir> Specify a framework directory (MacOSX)\
204 \n -framework <name> Use framework <name> (MacOSX)\
205 \n -help Print this help message and exit\
206 \n --help Same as -help\
207 \n -h Same as -help\
208 \n -I <dir> Add <dir> to the path searched for OCaml object files\
209 \n -failsafe fall back to static linking if DLL construction failed\
210 \n -ldopt <opt> C option passed to the shared linker only\
211 \n -linkall Build OCaml archive with link-all behavior\
212 \n -l<lib> Specify a dependent C library\
213 \n -L<dir> Add <dir> to the path searched for C libraries\
214 \n -ocamlc <cmd> Use <cmd> in place of \"ocamlc\"\
215 \n -ocamlcflags <opt> Pass <opt> to ocamlc\
216 \n -ocamlopt <cmd> Use <cmd> in place of \"ocamlopt\"\
217 \n -ocamloptflags <opt> Pass <opt> to ocamlopt\
218 \n -o <name> Generated OCaml library is named <name>.cma or <name>.cmxa\
219 \n -oc <name> Generated C library is named dll<name>.so or lib<name>.a\
220 \n -rpath <dir> Same as -dllpath <dir>\
221 \n -R<dir> Same as -rpath\
222 \n -verbose Print commands before executing them\
223 \n -v same as -verbose\
224 \n -version Print version and exit\
225 \n -vnum Print version number and exit\
226 \n -Wl,-rpath,<dir> Same as -dllpath <dir>\
227 \n -Wl,-rpath -Wl,<dir> Same as -dllpath <dir>\
228 \n -Wl,-R<dir> Same as -dllpath <dir>\
229 \n"
230
231 let command cmd =
232 if !verbose then (print_string "+ "; print_string cmd; print_newline());
233 Sys.command cmd
234
235 let scommand cmd =
236 if command cmd <> 0 then exit 2
237
238 let safe_remove s =
239 try Sys.remove s with Sys_error _ -> ()
240
241 let make_set l =
242 let rec merge l = function
243 [] -> List.rev l
244 | p :: r -> if List.mem p l then merge l r else merge (p::l) r
245 in
246 merge [] l
247
248 let make_rpath flag =
249 if !rpath = [] || flag = ""
250 then ""
251 else flag ^ String.concat ":" (make_set !rpath)
252
253 let make_rpath_ccopt flag =
254 if !rpath = [] || flag = ""
255 then ""
256 else "-ccopt " ^ flag ^ String.concat ":" (make_set !rpath)
257
258 let prefix_list pref l =
259 List.map (fun s -> pref ^ s) l
260
261 let prepostfix pre name post =
262 let base = Filename.basename name in
263 let dir = Filename.dirname name in
264 Filename.concat dir (pre ^ base ^ post)
265 ;;
266
267 let transl_path s =
268 match Sys.os_type with
269 | "Win32" ->
270 let s = Bytes.of_string s in
271 let rec aux i =
272 if i = Bytes.length s || Bytes.get s i = ' ' then s
273 else begin
274 if Bytes.get s i = '/' then Bytes.set s i '\\';
275 aux (i + 1)
276 end
277 in Bytes.to_string (aux 0)
278 | _ -> s
279
280 let flexdll_dirs =
281 let dirs =
282 let expand = Misc.expand_directory Config.standard_library in
283 List.map expand Config.flexdll_dirs
284 in
285 let f dir =
286 let dir =
287 if String.contains dir ' ' then
288 "\"" ^ dir ^ "\""
289 else
290 dir
291 in
292 "-L" ^ dir
293 in
294 List.map f dirs
295
296 let build_libs () =
297 if !c_objs <> [] then begin
298 if !dynlink then begin
299 let retcode = command
300 (Printf.sprintf "%s %s -o %s %s %s %s %s %s %s"
301 Config.mkdll
302 (if !debug then "-g" else "")
303 (prepostfix "dll" !output_c Config.ext_dll)
304 (String.concat " " !c_objs)
305 (String.concat " " !c_opts)
306 (String.concat " " !ld_opts)
307 (make_rpath mksharedlibrpath)
308 (String.concat " " !c_libs)
309 (String.concat " " flexdll_dirs)
310 )
311 in
312 if retcode <> 0 then if !failsafe then dynlink := false else exit 2
313 end;
314 safe_remove (prepostfix "lib" !output_c Config.ext_lib);
315 scommand
316 (mklib (prepostfix "lib" !output_c Config.ext_lib)
317 (String.concat " " !c_objs) "");
318 end;
319 if !bytecode_objs <> [] then
320 scommand
321 (sprintf "%s -a %s %s %s -o %s.cma %s %s -dllib -l%s -cclib -l%s \
322 %s %s %s %s"
323 (transl_path !ocamlc)
324 (if !debug then "-g" else "")
325 (if !dynlink then "" else "-custom")
326 (String.concat " " !ocamlc_opts)
327 !output
328 (String.concat " " !caml_opts)
329 (String.concat " " !bytecode_objs)
330 (Filename.basename !output_c)
331 (Filename.basename !output_c)
332 (String.concat " " (prefix_list "-ccopt " !c_opts))
333 (make_rpath_ccopt default_rpath)
334 (String.concat " " (prefix_list "-cclib " !c_libs))
335 (String.concat " " !caml_libs));
336 if !native_objs <> [] then
337 scommand
338 (sprintf "%s -a %s %s -o %s.cmxa %s %s -cclib -l%s %s %s %s %s"
339 (transl_path !ocamlopt)
340 (if !debug then "-g" else "")
341 (String.concat " " !ocamlopt_opts)
342 !output
343 (String.concat " " !caml_opts)
344 (String.concat " " !native_objs)
345 (Filename.basename !output_c)
346 (String.concat " " (prefix_list "-ccopt " !c_opts))
347 (make_rpath_ccopt default_rpath)
348 (String.concat " " (prefix_list "-cclib " !c_libs))
349 (String.concat " " !caml_libs))
350
351 let _ =
352 try
353 parse_arguments Sys.argv;
354 build_libs()
355 with
356 | Bad_argument "" ->
357 prerr_string usage; exit 0
358 | Bad_argument s ->
359 prerr_endline s; prerr_string usage; exit 4
360 | Sys_error s ->
361 prerr_string "System error: "; prerr_endline s; exit 4
362 | x ->
363 raise x
364