--- /dev/null
+
+open Printf
+
+exception Found of string list
+
+let ocamlobjinfo = "ocamlobjinfo"
+let noautolink = "-noautolink"
+let dummy_opt_cmd = "dummy_ocamlopt"
+let opt_cmd = "ocamlopt"
+let libdirs = [ "/lib"; "/usr/lib" ]
+let exceptions = [ "threads.cma", [ "-lthreads", "-lthreadsnat" ] ]
+
+let blanks_RE = Str.regexp "[ \t\r\n]+"
+let cmxa_RE = Str.regexp "\\.cmxa$"
+let extra_cfiles_RE = Str.regexp "^.*Extra +C +object +files:\\(.*\\)$"
+let extra_copts_RE = Str.regexp "^.*Extra +C +options:\\(.*\\)$"
+let lib_RE = Str.regexp "^lib"
+let l_RE = Str.regexp "^-l"
+let opt_line_RE = Str.regexp (sprintf "^\\+ +%s +\\(.*\\)$" dummy_opt_cmd)
+let trailing_cmxa_RE = Str.regexp ".*\\.cmxa$"
+
+let message s = prerr_endline ("STATIC_LINK: " ^ s)
+let warning s = message ("WARNING: " ^ s)
+
+let handle_exceptions ~cma cflag =
+ try
+ let cma_exns = List.assoc (Filename.basename cma) exceptions in
+ let cflag' = List.assoc cflag cma_exns in
+ message (sprintf "using %s exception %s -> %s" cma cflag cflag');
+ cflag'
+ with Not_found -> cflag
+
+let parse_cmdline () =
+ let mine, rest = ref [], ref [] in
+ let is_mine = ref true in
+ Array.iter
+ (function
+ | "--" -> is_mine := false
+ | s when !is_mine ->
+ if Str.string_match lib_RE s 0 then
+ warning (sprintf
+ ("libraries to be statically linked must be specified "
+ ^^ "without heading \"lib\", \"%s\" argument may be wrong") s);
+ mine := s :: !mine
+ | s -> rest := s :: !rest)
+ Sys.argv;
+ if !rest = [] then begin
+ prerr_endline "Usage: static_link [ CLIB .. ] -- COMMAND [ ARG .. ]";
+ prerr_endline ("Example: static_link pcre expat --"
+ ^ " ocamlfind opt -package pcre,expat -linkpkg -o foo foo.ml");
+ exit 0
+ end;
+ List.tl (List.rev !mine), List.rev !rest
+
+let extract_opt_flags cmd =
+ let ic = Unix.open_process_in cmd in
+ (try
+ while true do
+ let l = input_line ic in
+ if Str.string_match opt_line_RE l 0 then begin
+ message ("got ocamlopt line: " ^ l);
+ raise (Found (Str.split blanks_RE (Str.matched_group 1 l)));
+ end
+ done;
+ [] (* dummy value *)
+ with
+ | End_of_file -> failwith "compiler command not found"
+ | Found flags ->
+ close_in ic;
+ flags)
+
+let cma_of_cmxa = Str.replace_first cmxa_RE ".cma"
+
+let find_clib libname =
+ let rec aux =
+ function
+ | [] -> raise Not_found
+ | libdir :: tl ->
+ let fname = sprintf "%s/lib%s.a" libdir libname in
+ if Sys.file_exists fname then fname else aux tl
+ in
+ aux libdirs
+
+let a_of_cflag cflag = (* "-lfoo" -> "/usr/lib/libfoo.a" *)
+ let libname = Str.replace_first l_RE "" cflag in
+ find_clib libname
+
+let cflags_of_cma fname =
+ let ic = Unix.open_process_in (sprintf "%s %s" ocamlobjinfo fname) in
+ let extra_copts = ref "" in
+ let extra_cfiles = ref "" in
+ (try
+ while true do
+ match input_line ic with
+ | s when Str.string_match extra_copts_RE s 0 ->
+ extra_copts := Str.matched_group 1 s
+ | s when Str.string_match extra_cfiles_RE s 0 ->
+ extra_cfiles := Str.matched_group 1 s
+ | _ -> ()
+ done
+ with End_of_file -> ());
+ close_in ic;
+ let extra_cfiles = List.rev (Str.split blanks_RE !extra_cfiles) in
+ let extra_copts = Str.split blanks_RE !extra_copts in
+ extra_copts @ extra_cfiles
+
+let staticize static_libs flags =
+ let static_flags = List.map ((^) "-l") static_libs in
+ let aux ~add_cclib ~cma cflag =
+ let cflag =
+ if List.mem cflag static_flags
+ then
+ (try
+ let a = a_of_cflag cflag in
+ message (sprintf "using static %s instead of shared %s" a cflag);
+ a
+ with Not_found -> warning ("can't find lib for " ^ cflag); cflag)
+ else (handle_exceptions ~cma cflag)
+ in
+ if add_cclib then [ "-cclib"; cflag ] else [ cflag ]
+ in
+ List.fold_right
+ (fun flag acc ->
+ let cma = cma_of_cmxa flag in
+ if Str.string_match trailing_cmxa_RE flag 0 then begin
+ message ("processing native archive: " ^ flag);
+ let cflags = cflags_of_cma cma in
+ let cflags' =
+ List.fold_right
+ (fun cflag acc -> (aux ~add_cclib:true ~cma cflag) @ acc)
+ cflags []
+ in
+ flag :: (cflags' @ acc)
+ end else
+ (aux ~add_cclib:false ~cma flag) @ acc)
+ flags []
+
+let quote_if_needed s =
+ try
+ ignore (Str.search_forward blanks_RE s 0);
+ "\"" ^ s ^ "\""
+ with Not_found -> s
+
+let main () =
+ let static_libs, args = parse_cmdline () in
+ printf "C libraries to be linked-in: %s\n" (String.concat " " static_libs);
+ flush stdout;
+ let verbose_cmd =
+ sprintf "OCAMLFIND_COMMANDS='ocamlopt=%s' %s -verbose 2>&1" dummy_opt_cmd
+ (String.concat " " (List.map quote_if_needed args))
+ in
+ let orig_opt_flags = extract_opt_flags verbose_cmd in
+ message ("original ocamlopt flags: " ^ String.concat " " orig_opt_flags);
+ let opt_flags = staticize static_libs orig_opt_flags in
+ message ("new ocamlopt flags: " ^ String.concat " " opt_flags);
+ let flags = noautolink :: opt_flags in
+ let cmd = String.concat " " (opt_cmd :: flags) in
+ message ("executing command: " ^ cmd);
+ exit (Sys.command cmd)
+
+let _ = main ()
+