11 | ' '|'\n'|'\r'|'\t' -> true
21 ~(next:string -> int -> int -> (int * int))
22 ~(sub:acc:'acc -> skip: int -> string -> int -> int -> 'acc)
25 and siz = String.length string in
26 let max = bgn + siz in
29 and last_skip = ref 0 in
30 while !bgn < max || !last_skip > 0 do
32 let keep, skip = next string pos (max - pos) in
33 acc := sub ~acc:!acc ~skip string pos keep;
34 let inc = keep + skip in
35 (*Printf.printf "max=%i bgn=%i keep=%i, skip=%i\n" max !bgn keep skip;*)
36 assert (inc > 0 || (inc = 0 && !last_skip > 0));
45 ~sub: (fun ~acc ~skip string pos siz ->
46 String.sub string pos siz::acc)
47 ~next: (fun string pos siz ->
49 while let keep = !keep in keep < siz
50 && not (char (String.unsafe_get string (pos + keep)))
52 (!keep, if !keep = siz then 0 else 1)))
53 let char_with_separators_glued
57 ~sub: (fun ~acc ~skip string pos siz ->
58 let list = String.sub string pos siz::acc in
60 then String.sub string (pos + siz) skip::list
62 ~next: (fun string pos siz ->
65 while let keep = !keep in keep < siz
66 && not (char (String.unsafe_get string (pos + keep)))
70 and siz = siz - keep in
71 while let skip = !skip in skip < siz
72 && char (String.unsafe_get string (pos + skip))
79 let len = String.length string in
84 match String.unsafe_get string (pred curr) with
85 | ' '|'\n'|'\r'|'\t' -> loop (pred curr)
86 | _ when curr = len -> string
87 | _ -> String.sub string 0 curr
92 let string = copy string in
93 for pos = 0 to pred (length string)
94 do match unsafe_get string pos with
95 | 'a'..'z' | 'A'..'Z' | '_' -> ()
96 | '0'..'9' when pos > 0 -> ()
97 | _ -> unsafe_set string pos '_'
104 let (//) = Filename.concat
112 Pervasives.prerr_endline
113 ("File.rm: " ^ Printexc.to_string exn);
116 = fun ?(prefix="tmp") ?(suffix="tmp") fct ->
117 let (file, chan) = Filename.open_temp_file prefix suffix in
122 try let t = fct ~file ~chan in clean (); t
123 with exn -> clean (); raise exn
128 { close : unit -> unit
129 ; format : 'a. ('a, Pervasives.out_channel, unit) format -> 'a
134 { close = Pervasives.ignore
135 ; format = (fun format -> Printf.ifprintf Pervasives.stderr format)
138 let chan = Pervasives.open_out file in
139 { close = (fun _ -> Pervasives.close_out chan)
140 ; format = (fun format -> Printf.fprintf chan format)
148 match Sys.os_type with
152 = String.Split.char char
155 try split (Sys.getenv name)
160 try Some (Sys.getenv name)
161 with Not_found -> None
169 let self = Sys.argv.(0)
170 let c = ref (self^".h")
171 and caml = ref (self^".ml")
172 and camlpp = ref (self^".mlp")
173 and make = ref (self^".make")
174 and log = ref (self^".log")
186 and caml = !Param.caml
187 and camlpp = !Param.camlpp
188 and make = !Param.make
189 and log = !Param.log in
192 and caml = Out.t caml
193 and camlpp = Out.t camlpp
194 and make = Out.t make
195 and c_def = String.macrofy c in
202 if Sys.file_exists log
204 let t = fct {c=c; caml=caml; camlpp=camlpp; make=make; log=log} in
224 = fun ~glob:{Glob.c=c; caml=caml; camlpp=camlpp; make=make} name bool ->
230 c .Out.format "#define %s\n" name;
231 caml .Out.format "let _%s = true\n" name;
232 camlpp .Out.format "DEFINE %s\n" name;
233 make .Out.format "%s := true\n" name
235 c .Out.format "#undef %s\n" name;
236 caml .Out.format "let _%s = false\n" name;
237 camlpp .Out.format "(* DEFINE %s *)\n" name;
238 make .Out.format "%s := \n" name
240 = fun ~glob:{Glob.c=c; caml=caml; camlpp=camlpp; make=make} name string ->
244 c .Out.format "#define %s = %S\n" name string;
245 caml .Out.format "let _%s = %S\n" name string;
246 camlpp .Out.format "DEFINE %s = %S\n" name string;
247 make .Out.format "%s := \"%s\"\n" name string
257 = fun ~glob:{Glob.log=log} string ->
261 let log = Pervasives.open_out_gen
262 [Open_wronly; Open_append; Open_creat; Open_text] 0o660 log in
263 Pervasives.output_string log string;
264 Pervasives.close_out log;
272 Log.t ~glob (Printf.sprintf "$ %s\n" string);
275 if 0 = Sys.command string
279 match glob.Glob.log with
284 string (Filename.quote log))
285 let buffer_size = ref 2048
286 exception Exit of int * string
291 Log.t ~glob (Printf.sprintf "$ %s\n" string);
294 let size = !buffer_size in
295 let chan = Unix.open_process_in cmd
296 and buf = Buffer.create size
297 and string = String.create size
300 read := Pervasives.input chan string 0 size;
301 Buffer.add_substring buf string 0 !read
303 let string = Buffer.contents buf in
305 match Unix.close_process_in chan with
306 | Unix.WEXITED 0 -> string
307 | Unix.WEXITED int -> raise (Exit (int, string))
309 | Unix.WSTOPPED int ->
312 "command received signal %i: %s"
314 match glob.Glob.log with
316 | log -> Printf.ksprintf sh "{ %s ; } 2>&1" string)
322 let ocamlc = ref "ocamlfind ocamlc"
323 and ext_obj = ref ".o"
324 and ccomp_type = ref "cc"
335 (fun dir -> {inc=dir//"include"; lib=dir//"lib"})
351 [ List.map (fun dir -> {inc=dir; lib=dir//".."//"lib"})
352 (Env.list "C_INCLUDE_PATH")
353 ; List.map (fun dir -> {inc=dir//".." //"include"; lib=dir})
354 (Env.list "LIBRARY_PATH")
359 Log.t ~glob (Printf.sprintf "# searching for %s .. " h);
363 Log.t ~glob (Printf.sprintf "found NOTHING; \
364 you may use environment variables: \
365 C_INCLUDE_PATH and LIBRARY_PATH\n");
367 | {inc=inc; lib=lib}::list ->
368 match Sys.file_exists (inc//h) with
371 Log.t ~glob (Printf.sprintf "found include:%s lib:%s\n" inc lib);
372 Some {inc=inc; lib=lib}
380 = fun ~glob ~name ~default ->
383 ( Env.list ("CFLAGS_" ^ name)
384 , Env.list ("LIBS_" ^ name)
386 | (_::_ as opt), (_::_ as lib) -> (opt, lib)
388 let opt, lib = default () in
390 | (_::_ as opt), [] -> (opt, lib)
391 | [] , (_::_ as lib) -> (opt, lib)
392 | [] , [] -> (opt, lib)
393 | _ -> assert false in
394 let {Glob.make} = glob in
395 make.Out.format "CFLAGS_%s := %s\n" name (String.concat " " inc);
396 make.Out.format "LIBS_%s := %s\n" name (String.concat " " lib);
403 File.tmp ~prefix: "configure_ml_" ~suffix: ".ml"
405 Pervasives.output_string chan string;
406 Pervasives.close_out chan;
409 File.rm (Filename.chop_extension file ^ ".cmi");
410 File.rm (Filename.chop_extension file ^ ".cmo");
411 File.rm (Filename.chop_extension file ^ ".byte");
413 try let t = fct ~ml:file in clean (); t
414 with exn -> clean (); raise exn
421 File.tmp ~prefix: "configure_c_" ~suffix: ".c"
423 Pervasives.output_string chan string;
424 Pervasives.close_out chan;
427 File.rm (Filename.chop_extension file ^ !Param.ext_obj);
429 try let t = fct ~c:file in clean (); t
430 with exn -> clean (); raise exn
434 = fun ~glob (opt, lib) c ->
436 external t : unit -> unit = \"t\"\n\
440 (fun ~c -> Shell.t ~glob
441 "cd %s && %s -o %s -verbose -custom %s %s %s %s"
442 (Filename.quote (Filename.dirname c))
444 (Filename.quote ((Filename.chop_extension ml) ^ ".byte"))
445 (String.concat " " (List.map (fun t -> Printf.sprintf "-ccopt %s" (Filename.quote t)) opt))
446 (String.concat " " (List.map (fun t -> Printf.sprintf "-cclib %s" (Filename.quote t)) lib))
448 (Filename.quote ml)))
462 try Int (Pervasives.int_of_string string)
463 with Failure "int_of_string" -> String string)
464 (String.Split.char_with_separators_glued
465 (fun char -> not (Char.decimal char)) string)
471 | Int int -> Pervasives.string_of_int int
472 | String string -> string) t)
475 let string = String.t
490 | NOT (t) -> not (bool t)
491 | AND (t, tt) -> bool t && bool tt
492 | OR (t, tt) -> bool t || bool tt
493 | EQ (v, vv) -> v = vv
494 | LT (v, vv) -> v < vv
495 | LE (v, vv) -> v <= vv
496 | GT (v, vv) -> v > vv
497 | GE (v, vv) -> v >= vv
500 | NOT (t) -> Printf.sprintf "(not %s)" (string t)
501 | AND (t, tt) -> Printf.sprintf "(%s && %s)" (string t) (string tt)
502 | OR (t, tt) -> Printf.sprintf "(%s || %s)" (string t) (string tt)
503 | EQ (v, vv) -> Printf.sprintf "(%s = %s)" (String.t v) (String.t vv)
504 | LT (v, vv) -> Printf.sprintf "(%s < %s)" (String.t v) (String.t vv)
505 | LE (v, vv) -> Printf.sprintf "(%s <= %s)" (String.t v) (String.t vv)
506 | GT (v, vv) -> Printf.sprintf "(%s > %s)" (String.t v) (String.t vv)
507 | GE (v, vv) -> Printf.sprintf "(%s >= %s)" (String.t v) (String.t vv)
509 exception Constraint of t * Constraint.t
515 = fun ~glob ?(enable=true) ?(optional=false)
516 ~descr ?(info=fun _ -> "") ?(info_exn=fun _ -> "")
518 Log.t ~glob (Printf.sprintf "# checking %s\n" descr);
519 Trace.format "checking %s %!" descr;
520 let line = (String.make (45 - String.length descr) '.') in
523 (match (try Some (test ~glob), Not_found
524 with exn -> None, exn) with
525 | Some t as option, _ ->
527 Trace.format "%s ok %s\n%!" line info;
528 Emit.bool ~glob macro true;
531 Trace.format "%s KO" line;
532 Emit.bool ~glob macro false;
534 then Trace.format " (optional) %s\n%!" (info_exn exn)
535 else (Trace.format " (NOT optional) %s\n%!" (info_exn exn); raise Check);
539 Trace.format "%s skip\n%!" line;
540 Emit.bool ~glob macro enable;
545 = fun ?optional ?enable ?(info_exn=fun _ -> "")
547 ?(constraint_=fun t -> Version.Constraint.EQ (t, t))
549 match t ~glob ?optional ?enable ~descr ~macro
550 ~info: (fun t -> Printf.sprintf "(version %s)" (Version.string t))
552 | Version.Constraint (v, c) ->
553 Printf.sprintf "(version %s MUST satisfy: %s)"
555 (Version.Constraint.string c)
556 | exn -> info_exn exn)
558 let v = version ~glob in
559 let c = constraint_ v in
560 if not (Version.Constraint.bool c)
561 then raise (Version.Constraint (v, c))
564 | Some version as option ->
565 let version = Version.string version in
566 Emit.string ~glob (String.macrofy ("VERSION_" ^ macro)) version;
567 Emit.bool ~glob (String.macrofy ("VERSION_" ^ macro ^ "_" ^ version)) true;
579 { ocamlc : Version.t option
580 ; ocamlopt : Version.t option
584 = fun ~glob ?optional
585 ?version_ocamlc ?version_ocamlopt
590 have := match Check.t ~glob ~descr: "ocamlfind" ~macro: "HAVE_ocamlfind" ~optional: true
591 (Shell.t "ocamlfind printconf")
596 Check.Version.t ~glob ~descr: "ocamlc" ~macro: "ocamlc"
597 ~optional: false ?constraint_:version_ocamlc
598 (fun ~glob -> Version.t (String.chomp
599 (Shell.string ~glob "ocamlfind ocamlc -version"))) in
601 Check.Version.t ~glob ~descr: "ocamlopt" ~macro: "ocamlopt"
602 ~optional: true ?constraint_:version_ocamlopt
603 (fun ~glob -> Version.t (String.chomp
604 (Shell.string ~glob "ocamlfind ocamlopt -version"))) in
607 ; ocamlopt = ocamlopt
617 = fun ~glob: ({Glob.c=c; caml=caml; camlpp=camlpp; make=make} as glob)
618 ?optional ?enable ?version
620 if enable = Some false
623 match Check.t () ~glob ~optional: false with
626 Check.Super.Version.t ~glob ?optional ?enable ?constraint_:version
627 ~descr: ("ocamlfind package " ^ name)
628 ~macro: (String.macrofy ("PACKAGE_" ^ name))
629 (fun ~glob -> Version.t (String.chomp
630 (Shell.string ~glob "ocamlfind query -format '%%v' %s" name)))
642 | "false" -> t := false
643 | "true" -> t := true
644 | _ -> assert false )
656 | Some false -> "false"
657 | Some true -> "true"
661 | "false" -> Some false
662 | "true" -> Some true
665 let string = String.t
669 ( ["auto"; "false"; "true"]
670 , (fun string -> t := String.t' string) )
687 ( ["auto"; "dynamic"; "static"]
689 | "auto" -> t := `Auto
690 | "static" -> t := `Static
691 | "dynamic" -> t := `Dynamic
697 | `Static -> "static"
698 | `Dynamic -> "dynamic"
702 let mode : Mode.t ref = ref `Auto
707 = fun ~glob:{Glob.make} ->
708 match !Param.mode with
709 | `Auto -> make.Out.format "export LINKING_MODE := \n"
710 | `Static -> make.Out.format "export LINKING_MODE := static\n"
711 | `Dynamic -> make.Out.format "export LINKING_MODE := dynamic\n"
724 let bool : t ref = ref false
729 = fun ~glob:{Glob.make} ->
730 match !Param.bool with
731 | true -> make.Out.format "export DEBUG := true\n";
732 | false -> make.Out.format "export DEBUG := \n";
750 | `Cygwin -> "Cygwin"
755 | "Cygwin" -> `Cygwin
758 let string = String.t
764 ( ["Unix"; "Win32"; "Cygwin"]
765 , (fun string -> t := String.t' string) )
770 let type_ : Type.t ref = ref (Type.String.t' Sys.os_type)
775 = fun ~glob:{Glob.camlpp} ->
776 let type_ = Type.string !Param.type_ in
777 camlpp.Out.format "DEFINE OS_TYPE = \"%s\"\n" type_;
778 camlpp.Out.format "DEFINE %s\n" type_
784 [ "--debug" , Arg.unit Debug.Param.bool , Printf.sprintf " activate debug (DEFAULT: %s)" (Debug.string !Debug.Param.bool)
785 ; "--link-mode" , Link.Mode.Arg.t Link.Param.mode , Printf.sprintf " link mode (DEFAULT: %s)" (Link.Mode.string !Link.Param.mode)
786 ; "--os-type" , OS.Type.Arg.t OS.Param.type_ , Printf.sprintf " type of the target OS (DEFAULT: %s)" (OS.Type.string !OS.Param.type_)
787 ; "--ocamlc" , Arg.Set_string Compile.Param.ocamlc , Printf.sprintf "<command> ocamlc command (DEFAULT: %s)" !Compile.Param.ocamlc
788 ; "--ext-obj" , Arg.Set_string Compile.Param.ext_obj , Printf.sprintf "<ext> C object files extension (DEFAULT: %s)" !Compile.Param.ext_obj
789 ; "--ccomp-type", Arg.Set_string Compile.Param.ccomp_type, Printf.sprintf "<name> C compiler type (DEFAULT: %s)" !Compile.Param.ccomp_type
790 ; "--c" , Arg.Set_string Glob.Param.c , Printf.sprintf "<file> C output (DEFAULT: %s)" !Glob.Param.c
791 ; "--caml" , Arg.Set_string Glob.Param.caml , Printf.sprintf "<file> OCaml output (DEFAULT: %s)" !Glob.Param.caml
792 ; "--camlpp" , Arg.Set_string Glob.Param.camlpp , Printf.sprintf "<file> OCaml Pa_macro output (DEFAULT: %s)" !Glob.Param.camlpp
793 ; "--make" , Arg.Set_string Glob.Param.make , Printf.sprintf "<file> Makefile output (DEFAULT: %s)" !Glob.Param.make
794 ; "--log" , Arg.Set_string Glob.Param.log , Printf.sprintf "<file> shell log output (DEFAULT: %s)" !Glob.Param.log
798 = fun ?(env="") ~argv fct ->
804 "check prerequisites and generate %s.* files\n\
806 \ C_INCLUDE_PATH colon-separated paths to search C headers\n\
807 \ LIBRARY_PATH colon-separated paths to search libraries\n\
808 \ CFLAGS_<name> compile-time flags for <name>\n\
809 \ LIBS_<name> link-time flags for <name>\n\
811 OPTIONS" Glob.Param.self env);
813 (fun ({Glob.make} as glob) ->