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