2 let (<|) = fun f g x -> f (g x)
3 and (|>) = fun f g x -> g (f x)
13 | ' '|'\n'|'\r'|'\t' -> true
23 ~(next:string -> int -> int -> (int * int))
24 ~(sub:acc:'acc -> skip: int -> string -> int -> int -> 'acc)
27 and siz = String.length string
30 while !bgn < siz || !last_skip <> 0 do
32 let keep, skip = next string pos (siz - pos) in
33 let inc = keep + skip in
34 assert (inc <> 0 || (inc = 0 && !last_skip <> 0));
35 acc := sub ~acc:!acc ~skip string pos keep;
43 ~sub: (fun ~acc ~skip string pos siz ->
44 let pos = String.length string - pos - siz in
45 String.sub string pos siz::acc)
46 ~next: (fun string pos siz ->
48 and pos = String.length string - pos - 1 in
49 while let keep = !keep
51 && not (char (String.unsafe_get string (pos - keep)))
53 (!keep, if !keep = siz then 0 else 1))
54 let char_with_separators_glued
57 ~sub: (fun ~acc ~skip string pos siz ->
58 let pos = String.length string - pos - siz in
59 let acc = String.sub string pos siz::acc in
61 then String.sub string (pos - skip) skip::acc
63 ~next: (fun string pos siz ->
66 and pos = String.length string - pos - 1 in
67 while let keep = !keep
69 && not (char (String.unsafe_get string (pos - keep)))
73 and siz = siz - keep in
74 while let skip = !skip
76 && char (String.unsafe_get string (pos - skip))
83 let len = String.length string in
88 match String.unsafe_get string (pred curr) with
89 | ' '|'\n'|'\r'|'\t' -> loop (pred curr)
90 | _ when curr = len -> string
91 | _ -> String.sub string 0 curr
96 let string = copy string in
97 for pos = 0 to pred (length string)
98 do match unsafe_get string pos with
99 | 'a'..'z' | 'A'..'Z' | '_' -> ()
100 | '0'..'9' when pos > 0 -> ()
101 | _ -> unsafe_set string pos '_'
108 let (//) = Filename.concat
116 Pervasives.prerr_endline
117 ("File.rm: " ^ Printexc.to_string exn);
120 = fun ?(prefix="tmp") ?(suffix="tmp") fct ->
121 let (file, chan) = Filename.open_temp_file prefix suffix in
126 try let t = fct ~file ~chan in clean (); t
127 with exn -> clean (); raise exn
132 { close : unit -> unit
133 ; format : 'a. ('a, Pervasives.out_channel, unit) format -> 'a
138 { close = Pervasives.ignore
139 ; format = (fun format -> Printf.ifprintf Pervasives.stderr format)
142 let chan = Pervasives.open_out file in
143 { close = (fun _ -> Pervasives.close_out chan)
144 ; format = (fun format -> Printf.fprintf chan format)
152 match Sys.os_type with
156 = String.Split.char char
159 try split (Sys.getenv name)
164 try Some (Sys.getenv name)
165 with Not_found -> None
173 let self = Sys.argv.(0)
174 let c = ref (self^".h")
175 and caml = ref (self^".ml")
176 and camlpp = ref (self^".mlp")
177 and make = ref (self^".make")
178 and log = ref (self^".log")
190 and caml = !Param.caml
191 and camlpp = !Param.camlpp
192 and make = !Param.make
193 and log = !Param.log in
196 and caml = Out.t caml
197 and camlpp = Out.t camlpp
198 and make = Out.t make
199 and c_def = String.macrofy c in
206 if Sys.file_exists log
208 let t = fct {c=c; caml=caml; camlpp=camlpp; make=make; log=log} in
228 = fun ~glob:{Glob.c=c; caml=caml; camlpp=camlpp; make=make} name bool ->
234 c .Out.format "#define %s\n" name;
235 caml .Out.format "let _%s = true\n" name;
236 camlpp .Out.format "DEFINE %s\n" name;
237 make .Out.format "%s := true\n" name
239 c .Out.format "#undef %s\n" name;
240 caml .Out.format "let _%s = false\n" name;
241 camlpp .Out.format "(* DEFINE %s *)\n" name;
242 make .Out.format "%s := \n" name
244 = fun ~glob:{Glob.c=c; caml=caml; camlpp=camlpp; make=make} name string ->
248 c .Out.format "#define %s = %S\n" name string;
249 caml .Out.format "let _%s = %S\n" name string;
250 camlpp .Out.format "DEFINE %s = %S\n" name string;
251 make .Out.format "%s := \"%s\"\n" name string
261 = fun ~glob:{Glob.log=log} string ->
265 let log = Pervasives.open_out_gen
266 [Open_wronly; Open_append; Open_creat; Open_text] 0o660 log in
267 Pervasives.output_string log string;
268 Pervasives.close_out log;
276 Log.t ~glob (Printf.sprintf "$ %s\n" string);
279 if 0 = Sys.command string
283 match glob.Glob.log with
288 string (Filename.quote log))
289 let buffer_size = ref 2048
290 exception Exit of int * string
295 Log.t ~glob (Printf.sprintf "$ %s\n" string);
298 let size = !buffer_size in
299 let chan = Unix.open_process_in cmd
300 and buf = Buffer.create size
301 and string = String.create size
304 read := Pervasives.input chan string 0 size;
305 Buffer.add_substring buf string 0 !read
307 let string = Buffer.contents buf in
309 match Unix.close_process_in chan with
310 | Unix.WEXITED 0 -> string
311 | Unix.WEXITED int -> raise (Exit (int, string))
313 | Unix.WSTOPPED int ->
316 "command received signal %i: %s"
318 match glob.Glob.log with
320 | log -> Printf.ksprintf sh "{ %s ; } 2>&1" string)
326 let ocamlc = ref "ocamlfind ocamlc"
327 and ext_obj = ref ".o"
328 and ccomp_type = ref "cc"
339 (fun dir -> {inc=dir//"include"; lib=dir//"lib"})
355 [ List.map (fun dir -> {inc=dir; lib=dir//".."//"lib"})
356 (Env.list "C_INCLUDE_PATH")
357 ; List.map (fun dir -> {inc=dir//".." //"include"; lib=dir})
358 (Env.list "LIBRARY_PATH")
363 Log.t ~glob (Printf.sprintf "# searching for %s .. " h);
367 Log.t ~glob (Printf.sprintf "found NOTHING; \
368 you may use environment variables: \
369 C_INCLUDE_PATH and LIBRARY_PATH\n");
371 | {inc=inc; lib=lib}::list ->
372 match Sys.file_exists (inc//h) with
375 Log.t ~glob (Printf.sprintf "found include:%s lib:%s\n" inc lib);
376 Some {inc=inc; lib=lib}
384 = fun ~glob ~name ~default ->
387 ( Env.list ("CFLAGS_" ^ name)
388 , Env.list ("LIBS_" ^ name)
390 | (_::_ as opt), (_::_ as lib) -> (opt, lib)
392 let opt, lib = default () in
394 | (_::_ as opt), [] -> (opt, lib)
395 | [] , (_::_ as lib) -> (opt, lib)
396 | [] , [] -> (opt, lib)
397 | _ -> assert false in
398 let {Glob.make} = glob in
399 make.Out.format "CFLAGS_%s := %s\n" name (String.concat " " inc);
400 make.Out.format "LIBS_%s := %s\n" name (String.concat " " lib);
407 File.tmp ~prefix: "configure_ml_" ~suffix: ".ml"
409 Pervasives.output_string chan string;
410 Pervasives.close_out chan;
413 File.rm (Filename.chop_extension file ^ ".cmi");
414 File.rm (Filename.chop_extension file ^ ".cmo");
415 File.rm (Filename.chop_extension file ^ ".byte");
417 try let t = fct ~ml:file in clean (); t
418 with exn -> clean (); raise exn
425 File.tmp ~prefix: "configure_c_" ~suffix: ".c"
427 Pervasives.output_string chan string;
428 Pervasives.close_out chan;
431 File.rm (Filename.chop_extension file ^ !Param.ext_obj);
433 try let t = fct ~c:file in clean (); t
434 with exn -> clean (); raise exn
438 = fun ~glob (opt, lib) c ->
440 external t : unit -> unit = \"t\"\n\
444 (fun ~c -> Shell.t ~glob
445 "cd %s && %s -o %s -verbose -custom %s %s %s %s"
446 (Filename.quote (Filename.dirname c))
448 (Filename.quote ((Filename.chop_extension ml) ^ ".byte"))
449 (String.concat " " (List.map (fun t -> Printf.sprintf "-ccopt %s" (Filename.quote t)) opt))
450 (String.concat " " (List.map (fun t -> Printf.sprintf "-cclib %s" (Filename.quote t)) lib))
452 (Filename.quote ml)))
466 try Int (Pervasives.int_of_string string)
467 with Failure "int_of_string" -> String string)
468 (String.Split.char_with_separators_glued
469 ((not) <| Char.decimal) string)
475 | Int int -> Pervasives.string_of_int int
476 | String string -> string) t)
479 let string = String.t
494 | NOT (t) -> not (bool t)
495 | AND (t, tt) -> bool t && bool tt
496 | OR (t, tt) -> bool t || bool tt
497 | EQ (v, vv) -> v = vv
498 | LT (v, vv) -> v < vv
499 | LE (v, vv) -> v <= vv
500 | GT (v, vv) -> v > vv
501 | GE (v, vv) -> v >= vv
504 | NOT (t) -> Printf.sprintf "(not %s)" (string t)
505 | AND (t, tt) -> Printf.sprintf "(%s && %s)" (string t) (string tt)
506 | OR (t, tt) -> Printf.sprintf "(%s || %s)" (string t) (string tt)
507 | EQ (v, vv) -> Printf.sprintf "(%s = %s)" (String.t v) (String.t vv)
508 | LT (v, vv) -> Printf.sprintf "(%s < %s)" (String.t v) (String.t vv)
509 | LE (v, vv) -> Printf.sprintf "(%s <= %s)" (String.t v) (String.t vv)
510 | GT (v, vv) -> Printf.sprintf "(%s > %s)" (String.t v) (String.t vv)
511 | GE (v, vv) -> Printf.sprintf "(%s >= %s)" (String.t v) (String.t vv)
513 exception Constraint of t * Constraint.t
519 = fun ~glob ?(enable=true) ?(optional=false)
520 ~descr ?(info=fun _ -> "") ?(info_exn=fun _ -> "")
522 Log.t ~glob (Printf.sprintf "# checking %s\n" descr);
523 Trace.format "checking %s %!" descr;
524 let line = (String.make (45 - String.length descr) '.') in
527 (match (try Some (test ~glob), Not_found
528 with exn -> None, exn) with
529 | Some t as option, _ ->
531 Trace.format "%s ok %s\n%!" line info;
532 Emit.bool ~glob macro true;
535 Trace.format "%s KO" line;
536 Emit.bool ~glob macro false;
538 then Trace.format " (optional) %s\n%!" (info_exn exn)
539 else (Trace.format " (NOT optional) %s\n%!" (info_exn exn); raise Check);
543 Trace.format "%s skip\n%!" line;
544 Emit.bool ~glob macro enable;
549 = fun ?optional ?enable ?(info_exn=fun _ -> "")
551 ?(constraint_=fun t -> Version.Constraint.EQ (t, t))
553 match t ~glob ?optional ?enable ~descr ~macro
554 ~info: (fun t -> Printf.sprintf "(version %s)" (Version.string t))
556 | Version.Constraint (v, c) ->
557 Printf.sprintf "(version %s MUST satisfy: %s)"
559 (Version.Constraint.string c)
560 | exn -> info_exn exn)
562 let v = version ~glob in
563 let c = constraint_ v in
564 if not (Version.Constraint.bool c)
565 then raise (Version.Constraint (v, c))
568 | Some version as option ->
569 let version = Version.string version in
570 Emit.string ~glob (String.macrofy ("VERSION_" ^ macro)) version;
571 Emit.bool ~glob (String.macrofy ("VERSION_" ^ macro ^ "_" ^ version)) true;
583 { ocamlc : Version.t option
584 ; ocamlopt : Version.t option
588 = fun ~glob ?optional
589 ?version_ocamlc ?version_ocamlopt
594 have := match Check.t ~glob ~descr: "ocamlfind" ~macro: "HAVE_ocamlfind" ~optional: true
595 (Shell.t "ocamlfind printconf")
600 Check.Version.t ~glob ~descr: "ocamlc" ~macro: "ocamlc"
601 ~optional: false ?constraint_:version_ocamlc
602 (fun ~glob -> Version.t (String.chomp
603 (Shell.string ~glob "ocamlfind ocamlc -version"))) in
605 Check.Version.t ~glob ~descr: "ocamlopt" ~macro: "ocamlopt"
606 ~optional: true ?constraint_:version_ocamlopt
607 (fun ~glob -> Version.t (String.chomp
608 (Shell.string ~glob "ocamlfind ocamlopt -version"))) in
611 ; ocamlopt = ocamlopt
621 = fun ~glob: ({Glob.c=c; caml=caml; camlpp=camlpp; make=make} as glob)
622 ?optional ?enable ?version
624 if enable = Some false
627 match Check.t () ~glob ~optional: false with
630 Check.Super.Version.t ~glob ?optional ?enable ?constraint_:version
631 ~descr: ("ocamlfind package " ^ name)
632 ~macro: (String.macrofy ("PACKAGE_" ^ name))
633 (fun ~glob -> Version.t (String.chomp
634 (Shell.string ~glob "ocamlfind query -format '%%v' %s" name)))
646 | "false" -> t := false
647 | "true" -> t := true
648 | _ -> assert false )
660 | Some false -> "false"
661 | Some true -> "true"
665 | "false" -> Some false
666 | "true" -> Some true
669 let string = String.t
673 ( ["auto"; "false"; "true"]
674 , ((:=) t <| String.t') )
691 ( ["auto"; "dynamic"; "static"]
693 | "auto" -> t := `Auto
694 | "static" -> t := `Static
695 | "dynamic" -> t := `Dynamic
701 | `Static -> "static"
702 | `Dynamic -> "dynamic"
706 let mode : Mode.t ref = ref `Auto
711 = fun ~glob:{Glob.make} ->
712 match !Param.mode with
713 | `Auto -> make.Out.format "export LINKING_MODE := \n"
714 | `Static -> make.Out.format "export LINKING_MODE := static\n"
715 | `Dynamic -> make.Out.format "export LINKING_MODE := dynamic\n"
728 let bool : t ref = ref false
733 = fun ~glob:{Glob.make} ->
734 match !Param.bool with
735 | true -> make.Out.format "export DEBUG := true\n";
736 | false -> make.Out.format "export DEBUG := \n";
754 | `Cygwin -> "Cygwin"
759 | "Cygwin" -> `Cygwin
762 let string = String.t
768 ( ["Unix"; "Win32"; "Cygwin"]
769 , ((:=) t <| String.t') )
774 let type_ : Type.t ref = ref (Type.String.t' Sys.os_type)
779 = fun ~glob:{Glob.camlpp} ->
780 let type_ = Type.string !Param.type_ in
781 camlpp.Out.format "DEFINE OS_TYPE = \"%s\"\n" type_;
782 camlpp.Out.format "DEFINE %s\n" type_
788 [ "--debug" , Arg.unit Debug.Param.bool , Printf.sprintf " activate debug (DEFAULT: %s)" (Debug.string !Debug.Param.bool)
789 ; "--link-mode" , Link.Mode.Arg.t Link.Param.mode , Printf.sprintf " link mode (DEFAULT: %s)" (Link.Mode.string !Link.Param.mode)
790 ; "--os-type" , OS.Type.Arg.t OS.Param.type_ , Printf.sprintf " type of the target OS (DEFAULT: %s)" (OS.Type.string !OS.Param.type_)
791 ; "--ocamlc" , Arg.Set_string Compile.Param.ocamlc , Printf.sprintf "<command> ocamlc command (DEFAULT: %s)" !Compile.Param.ocamlc
792 ; "--ext-obj" , Arg.Set_string Compile.Param.ext_obj , Printf.sprintf "<ext> C object files extension (DEFAULT: %s)" !Compile.Param.ext_obj
793 ; "--ccomp-type", Arg.Set_string Compile.Param.ccomp_type, Printf.sprintf "<name> C compiler type (DEFAULT: %s)" !Compile.Param.ccomp_type
794 ; "--c" , Arg.Set_string Glob.Param.c , Printf.sprintf "<file> C output (DEFAULT: %s)" !Glob.Param.c
795 ; "--caml" , Arg.Set_string Glob.Param.caml , Printf.sprintf "<file> OCaml output (DEFAULT: %s)" !Glob.Param.caml
796 ; "--camlpp" , Arg.Set_string Glob.Param.camlpp , Printf.sprintf "<file> OCaml Pa_macro output (DEFAULT: %s)" !Glob.Param.camlpp
797 ; "--make" , Arg.Set_string Glob.Param.make , Printf.sprintf "<file> Makefile output (DEFAULT: %s)" !Glob.Param.make
798 ; "--log" , Arg.Set_string Glob.Param.log , Printf.sprintf "<file> shell log output (DEFAULT: %s)" !Glob.Param.log
802 = fun ?(env="") ~argv fct ->
808 "check prerequisites and generate %s.* files\n\
810 \ C_INCLUDE_PATH colon-separated paths to search C headers\n\
811 \ LIBRARY_PATH colon-separated paths to search libraries\n\
812 \ CFLAGS_<name> compile-time flags for <name>\n\
813 \ LIBS_<name> link-time flags for <name>\n\
815 OPTIONS" Glob.Param.self env);
817 (fun ({Glob.make} as glob) ->