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 '_'
106 let string = copy string in
107 for pos = 0 to min 0 (pred (length string))
108 do match unsafe_get string pos with
109 | 'a'..'z' | 'A'..'Z' | '_' -> ()
110 | _ -> unsafe_set string pos '_'
119 let pid = Unix.create_process "sh" [| "sh"; "-c"; cmd |]
120 Unix.stdin Unix.stdout Unix.stderr in
121 match snd (Unix.waitpid [] pid) with
122 | Unix.WEXITED n -> n
123 | Unix.WSIGNALED n -> n
124 | Unix.WSTOPPED n -> n
129 let (//) = Filename.concat
131 match Sys.os_type with
141 Pervasives.prerr_endline
142 ("File.rm: " ^ Printexc.to_string exn);
145 = fun ?(prefix="tmp") ?(suffix="tmp") fct ->
146 let (file, chan) = Filename.open_temp_file prefix suffix in
151 try let t = fct ~file ~chan in clean (); t
152 with exn -> clean (); raise exn
157 { close : unit -> unit
158 ; format : 'a. ('a, Pervasives.out_channel, unit) format -> 'a
163 { close = Pervasives.ignore
164 ; format = (fun format -> Printf.ifprintf Pervasives.stderr format)
167 let chan = Pervasives.open_out file in
168 { close = (fun _ -> Pervasives.close_out chan)
169 ; format = (fun format -> Printf.fprintf chan format)
177 = String.Split.char ((=) Path.char)
180 try split (Sys.getenv name)
185 try Some (Sys.getenv name)
186 with Not_found -> None
192 let self = Sys.argv.(0)
193 let c = ref (self^".h")
194 and caml = ref (self^".ml")
195 and camlpp = ref (Filename.concat (Filename.dirname self) ("pa_"^Filename.basename self^".ml"))
196 and make = ref (self^".make")
197 and log = ref (self^".log")
209 and caml = !Param.caml
210 and camlpp = !Param.camlpp
211 and make = !Param.make
212 and log = !Param.log in
215 and caml = Out.t caml
216 and camlpp = Out.t camlpp
217 and make = Out.t make
218 and c_def = String.macrofy c in
225 if Sys.file_exists log
227 let t = fct {c=c; caml=caml; camlpp=camlpp; make=make; log=log} in
247 = fun ~context:{Context.c=c; caml=caml; camlpp=camlpp; make=make} name bool ->
253 c .Out.format "#define %s\n" name;
254 caml .Out.format "let _%s = true\n" name;
255 camlpp .Out.format "DEFINE %s\n" name;
256 make .Out.format "%s := true\n" name
258 c .Out.format "#undef %s\n" name;
259 caml .Out.format "let _%s = false\n" name;
260 camlpp .Out.format "(* DEFINE %s *)\n" name;
261 make .Out.format "%s := \n" name
263 = fun ~context:{Context.c=c; caml=caml; camlpp=camlpp; make=make} name string ->
267 c .Out.format "#define %s = %S\n" name string;
268 caml .Out.format "let _%s = %S\n" name string;
269 camlpp .Out.format "DEFINE %s = %S\n" name string;
270 make .Out.format "%s := \"%s\"\n" name string
280 = fun ~context:{Context.log=log} string ->
284 let log = Pervasives.open_out_gen
285 [Open_wronly; Open_append; Open_creat; Open_text] 0o660 log in
286 Pervasives.output_string log string;
287 Pervasives.close_out log;
295 Log.t ~context (Printf.sprintf "$ %s\n" string);
298 if 0 = Sys.command string
302 match context.Context.log with
307 string (Filename.quote log))
308 let buffer_size = ref 2048
309 exception Exit of int * string
314 Log.t ~context (Printf.sprintf "$ %s\n" string);
317 let size = !buffer_size in
318 let pipe_out, pipe_in = Unix.pipe () in
319 Unix.set_close_on_exec pipe_out;
320 let pid = Unix.create_process "sh" [| "sh"; "-c"; cmd |]
321 Unix.stdin pipe_in Unix.stderr in
323 let chan = Unix.in_channel_of_descr pipe_out in
324 let buf = Buffer.create size
325 and string = String.create size
328 read := Pervasives.input chan string 0 size;
329 Buffer.add_substring buf string 0 !read
331 let string = Buffer.contents buf in
332 Log.t ~context string;
333 match snd (Unix.waitpid [] pid) with
334 | Unix.WEXITED 0 -> string
335 | Unix.WEXITED int -> raise (Exit (int, string))
337 | Unix.WSTOPPED int ->
340 "command received signal %i: %s"
342 match context.Context.log with
344 | log -> Printf.ksprintf sh "{ %s ; } 2>&1" string)
350 let ocamlc = ref "ocamlfind ocamlc"
351 and ext_obj = ref ".o"
352 and ccomp_type = ref "cc"
363 (fun dir -> {inc=dir//"include"; lib=dir//"lib"})
379 [ List.map (fun dir -> {inc=dir; lib=dir//".."//"lib"})
380 (Env.Path.list "C_INCLUDE_PATH")
381 ; List.map (fun dir -> {inc=dir//".." //"include"; lib=dir})
382 (Env.Path.list "LIBRARY_PATH")
387 Log.t ~context (Printf.sprintf "# searching for %s .. " h);
391 Log.t ~context (Printf.sprintf "found NOTHING; \
392 you may use environment variables: \
393 C_INCLUDE_PATH and LIBRARY_PATH\n");
395 | {inc=inc; lib=lib}::list ->
396 match Sys.file_exists (inc//h) with
399 Log.t ~context (Printf.sprintf "found include:%s lib:%s\n" inc lib);
400 Some {inc=inc; lib=lib}
408 = fun ~context ~name ~default ->
411 ( Env.Path.list ("CFLAGS_" ^ name)
412 , Env.Path.list ("LIBS_" ^ name)
414 | (_::_ as opt), (_::_ as lib) -> (opt, lib)
416 let opt, lib = default () in
418 | (_::_ as opt), [] -> (opt, lib)
419 | [] , (_::_ as lib) -> (opt, lib)
420 | [] , [] -> (opt, lib)
421 | _ -> assert false in
422 let {Context.make=make} = context in
423 make.Out.format "CFLAGS_%s := %s\n" name (String.concat " " inc);
424 make.Out.format "LIBS_%s := %s\n" name (String.concat " " lib);
431 File.tmp ~prefix: "configure_ml_" ~suffix: ".ml"
433 Pervasives.output_string chan string;
434 Pervasives.close_out chan;
437 File.rm (Filename.chop_extension file ^ ".cmi");
438 File.rm (Filename.chop_extension file ^ ".cmo");
439 File.rm (Filename.chop_extension file ^ ".byte");
441 try let t = fct ~ml:file in clean (); t
442 with exn -> clean (); raise exn
449 File.tmp ~prefix: "configure_c_" ~suffix: ".c"
451 Pervasives.output_string chan string;
452 Pervasives.close_out chan;
455 File.rm (Filename.chop_extension file ^ !Param.ext_obj);
457 try let t = fct ~c:file in clean (); t
458 with exn -> clean (); raise exn
462 = fun ~context (opt, lib) c ->
464 external t : unit -> unit = \"t\"\n\
468 (fun ~c -> Shell.t ~context
469 "cd %s && %s -o %s -verbose -custom %s %s %s %s"
470 (Filename.quote (Filename.dirname c))
472 (Filename.quote ((Filename.chop_extension ml) ^ ".byte"))
473 (String.concat " " (List.map (fun t -> Printf.sprintf "-ccopt %s" (Filename.quote t)) opt))
474 (String.concat " " (List.map (fun t -> Printf.sprintf "-cclib %s" (Filename.quote t)) lib))
476 (Filename.quote ml)))
490 try Int (Pervasives.int_of_string string)
491 with Failure "int_of_string" -> String string)
492 (String.Split.char_with_separators_glued
493 ((not) <| Char.decimal) string)
499 | Int int -> Pervasives.string_of_int int
500 | String string -> string) t)
503 let string = String.t
518 | NOT (t) -> not (bool t)
519 | AND (t, tt) -> bool t && bool tt
520 | OR (t, tt) -> bool t || bool tt
521 | EQ (v, vv) -> v = vv
522 | LT (v, vv) -> v < vv
523 | LE (v, vv) -> v <= vv
524 | GT (v, vv) -> v > vv
525 | GE (v, vv) -> v >= vv
528 | NOT (t) -> Printf.sprintf "(not %s)" (string t)
529 | AND (t, tt) -> Printf.sprintf "(%s && %s)" (string t) (string tt)
530 | OR (t, tt) -> Printf.sprintf "(%s || %s)" (string t) (string tt)
531 | EQ (v, vv) -> Printf.sprintf "(%s = %s)" (String.t v) (String.t vv)
532 | LT (v, vv) -> Printf.sprintf "(%s < %s)" (String.t v) (String.t vv)
533 | LE (v, vv) -> Printf.sprintf "(%s <= %s)" (String.t v) (String.t vv)
534 | GT (v, vv) -> Printf.sprintf "(%s > %s)" (String.t v) (String.t vv)
535 | GE (v, vv) -> Printf.sprintf "(%s >= %s)" (String.t v) (String.t vv)
537 exception Constraint of t * Constraint.t
543 = fun ~context ?(enable=true) ?(optional=false) ?(mem=ref None)
544 ~descr ?(info=fun _ -> "") ?(info_exn=fun _ -> "")
553 | false -> raise Check))
555 Log.t ~context (Printf.sprintf "# checking %s\n" descr);
556 Trace.format "checking %s %!" descr;
557 let line = (String.make (45 - String.length descr) '.') in
560 Trace.format "%s skip\n%!" line;
561 Emit.bool ~context macro enable;
564 match (try Some (test ~context), Not_found
565 with exn -> None, exn) with
566 | Some t as option, _ ->
568 Trace.format "%s ok %s\n%!" line info;
569 Emit.bool ~context macro true;
573 Trace.format "%s KO" line;
574 Emit.bool ~context macro false;
578 Trace.format " (optional) %s\n%!" (info_exn exn);
581 Trace.format " (NOT optional) %s\n%!" (info_exn exn);
586 = fun ?optional ?enable ?(mem=ref None) ?(info_exn=fun _ -> "")
587 ~context ~descr ~macro
588 ?(constraint_=fun t -> Version.Constraint.EQ (t, t))
590 let old_mem = !mem in
591 match t ~context ?optional ?enable ~mem ~descr ~macro
592 ~info: (fun t -> Printf.sprintf "(version %s)" (Version.string t))
594 | Version.Constraint (v, c) ->
595 Printf.sprintf "(version %s MUST satisfy: %s)"
597 (Version.Constraint.string c)
598 | exn -> info_exn exn)
600 let v = version ~context in
601 let c = constraint_ v in
602 if not (Version.Constraint.bool c)
603 then raise (Version.Constraint (v, c))
606 | Some version as option ->
609 let version = Version.string version in
610 Emit.string ~context (String.macrofy ("VERSION_" ^ macro)) version;
611 Emit.bool ~context (String.macrofy ("VERSION_" ^ macro ^ "_" ^ version)) true;
623 { ocamlc : Version.t option
624 ; ocamlopt : Version.t option
627 and mem_ocamlc = ref None
628 and mem_ocamlopt = ref None
630 = fun ~context ?optional
631 ?version_ocamlc ?version_ocamlopt
633 match Check.t ~context ~descr: "ocamlfind"
634 ~macro: "HAVE_ocamlfind" ?optional ~mem
635 (Shell.t "ocamlfind printconf")
640 Check.Version.t ~context ~descr: "ocamlc" ~macro: "OCAMLC"
641 ~optional: false ?constraint_:version_ocamlc ~mem: mem_ocamlc
642 (fun ~context -> Version.t (String.chomp
643 (Shell.string ~context "ocamlfind ocamlc -version 2>/dev/null"))) in
645 Check.Version.t ~context ~descr: "ocamlopt" ~macro: "OCAMLOPT"
646 ~optional: true ?constraint_:version_ocamlopt ~mem: mem_ocamlopt
647 (fun ~context -> Version.t (String.chomp
648 (Shell.string ~context "ocamlfind ocamlopt -version 2>/dev/null"))) in
651 ; ocamlopt = ocamlopt
659 = fun ~context: ({Context.c=c; caml=caml; camlpp=camlpp; make=make} as context)
660 ?optional ?enable ?mem ?version
662 assert (None <> Check.t () ~context ~optional: false);
663 Check.Super.Version.t ~context ?optional ?enable ?mem ?constraint_:version
664 ~descr: ("ocamlfind package " ^ name)
665 ~macro: (String.macrofy ("PACKAGE_" ^ name))
666 (fun ~context -> Version.t (String.chomp
667 (Shell.string ~context "ocamlfind query -format '%%v' %s 2>/dev/null" name)))
679 | "false" -> t := false
680 | "true" -> t := true
681 | _ -> assert false )
693 | Some false -> "false"
694 | Some true -> "true"
698 | "false" -> Some false
699 | "true" -> Some true
702 let string = String.t
706 ( ["auto"; "false"; "true"]
707 , ((:=) t <| String.t') )
714 = String.concat (String.make 1 Path.char)
716 = String.Split.char ((=) Path.char)
738 ( ["auto"; "dynamic"; "static"]
740 | "auto" -> t := `Auto
741 | "static" -> t := `Static
742 | "dynamic" -> t := `Dynamic
748 | `Static -> "static"
749 | `Dynamic -> "dynamic"
753 let mode : Mode.t ref = ref `Auto
758 = fun ~context:{Context.make=make} ->
759 match !Param.mode with
760 | `Auto -> make.Out.format "export LINKING_MODE := \n"
761 | `Static -> make.Out.format "export LINKING_MODE := static\n"
762 | `Dynamic -> make.Out.format "export LINKING_MODE := dynamic\n"
775 let bool : t ref = ref false
780 = fun ~context:{Context.make=make} ->
781 match !Param.bool with
782 | true -> make.Out.format "export DEBUG := true\n";
783 | false -> make.Out.format "export DEBUG := \n";
801 | `Cygwin -> "Cygwin"
806 | "Cygwin" -> `Cygwin
809 let string = String.t
815 ( ["Unix"; "Win32"; "Cygwin"]
816 , ((:=) t <| String.t') )
821 let type_ : Type.t ref = ref (Type.String.t' Sys.os_type)
826 = fun ~context:{Context.camlpp=camlpp} ->
827 let type_ = Type.string !Param.type_ in
828 camlpp.Out.format "DEFINE OS_TYPE = \"%s\"\n" type_;
829 camlpp.Out.format "DEFINE %s\n" (String.uidentify type_)
835 [ "--debug" , Arg.unit Debug.Param.bool , Printf.sprintf " activate debug (DEFAULT: %s)" (Debug.string !Debug.Param.bool)
836 ; "--link-mode" , Link.Mode.Arg.t Link.Param.mode , Printf.sprintf " link mode (DEFAULT: %s)" (Link.Mode.string !Link.Param.mode)
837 ; "--os-type" , OS.Type.Arg.t OS.Param.type_ , Printf.sprintf " type of the target OS (DEFAULT: %s)" (OS.Type.string !OS.Param.type_)
838 ; "--ocamlc" , Arg.Set_string Compile.Param.ocamlc , Printf.sprintf "<command> ocamlc command (DEFAULT: %s)" !Compile.Param.ocamlc
839 ; "--ext-obj" , Arg.Set_string Compile.Param.ext_obj , Printf.sprintf "<ext> C object files extension (DEFAULT: %s)" !Compile.Param.ext_obj
840 ; "--ccomp-type", Arg.Set_string Compile.Param.ccomp_type, Printf.sprintf "<name> C compiler type (DEFAULT: %s)" !Compile.Param.ccomp_type
841 ; "--c" , Arg.Set_string Context.Param.c , Printf.sprintf "<file> C output (DEFAULT: %s)" !Context.Param.c
842 ; "--caml" , Arg.Set_string Context.Param.caml , Printf.sprintf "<file> OCaml output (DEFAULT: %s)" !Context.Param.caml
843 ; "--camlpp" , Arg.Set_string Context.Param.camlpp , Printf.sprintf "<file> OCaml Pa_macro output (DEFAULT: %s)" !Context.Param.camlpp
844 ; "--make" , Arg.Set_string Context.Param.make , Printf.sprintf "<file> Makefile output (DEFAULT: %s)" !Context.Param.make
845 ; "--log" , Arg.Set_string Context.Param.log , Printf.sprintf "<file> shell log output (DEFAULT: %s)" !Context.Param.log
849 = fun ?(env="") ~argv fct ->
855 "check prerequisites and generate %s.* files\n\
857 \ C_INCLUDE_PATH colon-separated paths to search C headers\n\
858 \ LIBRARY_PATH colon-separated paths to search libraries\n\
859 \ CFLAGS_<name> compile-time flags for <name>\n\
860 \ LIBS_<name> link-time flags for <name>\n\
862 OPTIONS" Context.Param.self env);
864 (fun ({Context.make=make} as context) ->
865 Debug.Emit.t ~context;
866 Link.Emit.t ~context;
868 let t = fct ~context in