This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
3
unikernel/duniverse/dune_/otherlibs/configurator/dune
Normal file
3
unikernel/duniverse/dune_/otherlibs/configurator/dune
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(env
|
||||
(_
|
||||
(flags :standard \ -alert -unstable)))
|
||||
|
|
@ -0,0 +1 @@
|
|||
module V1 = V1
|
||||
17
unikernel/duniverse/dune_/otherlibs/configurator/src/dune
Normal file
17
unikernel/duniverse/dune_/otherlibs/configurator/src/dune
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
(ocamllex extract_obj)
|
||||
|
||||
(library
|
||||
(name configurator)
|
||||
(public_name dune-configurator)
|
||||
(private_modules import dune_lang ocaml_config)
|
||||
(libraries unix csexp)
|
||||
(flags
|
||||
(:standard
|
||||
-safe-string
|
||||
(:include flags/flags.sexp)))
|
||||
(special_builtin_support
|
||||
(configurator
|
||||
(api_version 1))))
|
||||
|
||||
(documentation
|
||||
(package dune-configurator))
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
open Import
|
||||
|
||||
module Escape : sig
|
||||
val quoted : string -> string
|
||||
end = struct
|
||||
let quote_length s =
|
||||
let n = ref 0 in
|
||||
let len = String.length s in
|
||||
for i = 0 to len - 1 do
|
||||
n
|
||||
:= !n
|
||||
+
|
||||
match String.unsafe_get s i with
|
||||
| '\"' | '\\' | '\n' | '\t' | '\r' | '\b' -> 2
|
||||
| '%' -> if i + 1 < len && s.[i + 1] = '{' then 2 else 1
|
||||
| ' ' .. '~' -> 1
|
||||
| _ -> 4
|
||||
done;
|
||||
!n
|
||||
;;
|
||||
|
||||
let escape_to s ~dst:s' ~ofs =
|
||||
let n = ref ofs in
|
||||
let len = String.length s in
|
||||
for i = 0 to len - 1 do
|
||||
(match String.unsafe_get s i with
|
||||
| ('\"' | '\\') as c ->
|
||||
Bytes.unsafe_set s' !n '\\';
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n c
|
||||
| '\n' ->
|
||||
Bytes.unsafe_set s' !n '\\';
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n 'n'
|
||||
| '\t' ->
|
||||
Bytes.unsafe_set s' !n '\\';
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n 't'
|
||||
| '\r' ->
|
||||
Bytes.unsafe_set s' !n '\\';
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n 'r'
|
||||
| '\b' ->
|
||||
Bytes.unsafe_set s' !n '\\';
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n 'b'
|
||||
| '%' when i + 1 < len && s.[i + 1] = '{' ->
|
||||
Bytes.unsafe_set s' !n '\\';
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n '%'
|
||||
| ' ' .. '~' as c -> Bytes.unsafe_set s' !n c
|
||||
| c ->
|
||||
let a = Char.code c in
|
||||
Bytes.unsafe_set s' !n '\\';
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n (Char.unsafe_chr (48 + (a / 100)));
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n (Char.unsafe_chr (48 + (a / 10 mod 10)));
|
||||
incr n;
|
||||
Bytes.unsafe_set s' !n (Char.unsafe_chr (48 + (a mod 10))));
|
||||
incr n
|
||||
done
|
||||
;;
|
||||
|
||||
(* Surround [s] with quotes, escaping it if necessary. *)
|
||||
let quoted s =
|
||||
let len = String.length s in
|
||||
let n = quote_length s in
|
||||
let s' = Bytes.create (n + 2) in
|
||||
Bytes.unsafe_set s' 0 '"';
|
||||
if len = 0 || n > len
|
||||
then escape_to s ~dst:s' ~ofs:1
|
||||
else Bytes.blit_string ~src:s ~src_pos:0 ~dst:s' ~dst_pos:1 ~len;
|
||||
Bytes.unsafe_set s' (n + 1) '"';
|
||||
Bytes.unsafe_to_string s'
|
||||
;;
|
||||
end
|
||||
|
||||
type t =
|
||||
| Quoted_string of string
|
||||
| List of t list
|
||||
|
||||
let rec to_string t =
|
||||
match t with
|
||||
| Quoted_string s -> Escape.quoted s
|
||||
| List l -> Printf.sprintf "(%s)" (List.map l ~f:to_string |> String.concat ~sep:" ")
|
||||
;;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
(** Subset of dune_lang to print flag lists *)
|
||||
type t =
|
||||
| Quoted_string of string
|
||||
| List of t list
|
||||
|
||||
val to_string : t -> string
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(** Read and extract the strings between a pair of BEGIN-\d+- and -END
|
||||
delimiters. This is used to extract the compile time values from .obj files *)
|
||||
val extract : (int * string) list -> Lexing.lexbuf -> (int * string) list
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{}
|
||||
rule extract acc = parse
|
||||
| "BEGIN-" (['0' - '9']+ as i) "-"
|
||||
{ read acc (int_of_string i) (Buffer.create 8) lexbuf }
|
||||
| _ { extract acc lexbuf }
|
||||
| eof { List.rev acc }
|
||||
and read acc i b = parse
|
||||
| "-END" { extract ((i, Buffer.contents b) :: acc) lexbuf }
|
||||
| _ as c { Buffer.add_char b c; read acc i b lexbuf }
|
||||
| eof { failwith "Unterminated BEGIN-" }
|
||||
{}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
(executable
|
||||
(name mk))
|
||||
|
||||
(rule
|
||||
(with-stdout-to
|
||||
flags.sexp
|
||||
(run ./mk.exe -ocamlv %{ocaml_version})))
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
open Printf
|
||||
|
||||
let parse_version s = Scanf.sscanf s "%d.%d.%d" (fun a b c -> a, b, c)
|
||||
|
||||
let () =
|
||||
let usage = sprintf "%s -ocamlv version" (Filename.basename Sys.executable_name) in
|
||||
let ocaml_version = ref "" in
|
||||
let anon _ = raise (Arg.Bad "anonymous arguments aren't accepted") in
|
||||
Arg.parse
|
||||
[ "-ocamlv", Arg.String (fun s -> ocaml_version := s), "Version of ocaml being used" ]
|
||||
anon
|
||||
usage;
|
||||
if !ocaml_version = ""
|
||||
then raise (Arg.Bad "Provide version with -ocamlv")
|
||||
else (
|
||||
let x, y, _ = parse_version !ocaml_version in
|
||||
if x >= 4 && y > 2 then printf "()\n" else printf "(-w -50)\n")
|
||||
;;
|
||||
347
unikernel/duniverse/dune_/otherlibs/configurator/src/import.ml
Normal file
347
unikernel/duniverse/dune_/otherlibs/configurator/src/import.ml
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
let sprintf = Printf.sprintf
|
||||
let eprintf = Printf.eprintf
|
||||
let ( ^/ ) = Filename.concat
|
||||
|
||||
exception Fatal_error of string
|
||||
|
||||
let die fmt = Printf.ksprintf (fun s -> raise (Fatal_error s)) fmt
|
||||
let warn fmt = Printf.ksprintf (fun msg -> prerr_endline ("Warning: " ^ msg)) fmt
|
||||
|
||||
module Result = struct
|
||||
type ('a, 'b) t = ('a, 'b) result =
|
||||
| Ok of 'a
|
||||
| Error of 'b
|
||||
|
||||
let to_option = function
|
||||
| Ok x -> Some x
|
||||
| Error _ -> None
|
||||
;;
|
||||
end
|
||||
|
||||
module Exn = struct
|
||||
external reraise : exn -> _ = "%reraise"
|
||||
|
||||
let protectx x ~f ~finally =
|
||||
match f x with
|
||||
| y ->
|
||||
finally x;
|
||||
y
|
||||
| exception e ->
|
||||
finally x;
|
||||
raise e
|
||||
;;
|
||||
|
||||
let protect ~f ~finally = protectx () ~f ~finally
|
||||
|
||||
include struct
|
||||
[@@@ocaml.warning "-32"]
|
||||
|
||||
let raise_with_backtrace exn _bt = reraise exn
|
||||
end
|
||||
|
||||
include Printexc
|
||||
end
|
||||
|
||||
module Option = struct
|
||||
let map t ~f =
|
||||
match t with
|
||||
| None -> None
|
||||
| Some x -> Some (f x)
|
||||
;;
|
||||
|
||||
let some_if cond x = if cond then Some x else None
|
||||
let some x = Some x
|
||||
|
||||
let iter t ~f =
|
||||
match t with
|
||||
| None -> ()
|
||||
| Some x -> f x
|
||||
;;
|
||||
|
||||
module O = struct
|
||||
let ( >>= ) x f =
|
||||
match x with
|
||||
| None -> None
|
||||
| Some x -> f x
|
||||
;;
|
||||
|
||||
let ( >>| ) x f = map x ~f
|
||||
end
|
||||
end
|
||||
|
||||
module List = struct
|
||||
include ListLabels
|
||||
|
||||
let rec find_map l ~f =
|
||||
match l with
|
||||
| [] -> None
|
||||
| x :: l ->
|
||||
(match f x with
|
||||
| None -> find_map l ~f
|
||||
| Some _ as res -> res)
|
||||
;;
|
||||
end
|
||||
|
||||
module Array = ArrayLabels
|
||||
|
||||
module Bool = struct
|
||||
let of_string s =
|
||||
match bool_of_string s with
|
||||
| s -> Some s
|
||||
| exception Invalid_argument _ -> None
|
||||
;;
|
||||
end
|
||||
|
||||
module Map (S : Map.OrderedType) = struct
|
||||
module M = MoreLabels.Map.Make (S)
|
||||
include M
|
||||
|
||||
let update (type a) (t : a t) (key : M.key) ~(f : a option -> a option) : a t =
|
||||
let v =
|
||||
match find key t with
|
||||
| exception Not_found -> None
|
||||
| v -> Some v
|
||||
in
|
||||
match f v, v with
|
||||
| None, None -> t
|
||||
| None, Some _ -> remove key t
|
||||
| Some data, _ -> add ~key ~data t
|
||||
;;
|
||||
|
||||
let find m k =
|
||||
match find k m with
|
||||
| exception Not_found -> None
|
||||
| s -> Some s
|
||||
;;
|
||||
|
||||
let set t k v = add ~key:k ~data:v t
|
||||
|
||||
let of_list =
|
||||
let rec loop acc = function
|
||||
| [] -> Result.Ok acc
|
||||
| (k, v) :: l ->
|
||||
(match find acc k with
|
||||
| None -> loop (set acc k v) l
|
||||
| Some v_old -> Error (k, v_old, v))
|
||||
in
|
||||
fun l -> loop empty l
|
||||
;;
|
||||
|
||||
let of_list_exn l =
|
||||
match of_list l with
|
||||
| Ok s -> s
|
||||
| Error (_, _, _) -> failwith "Map.of_list_exn: duplicate key"
|
||||
;;
|
||||
end
|
||||
|
||||
module Int = struct
|
||||
let of_string s =
|
||||
match int_of_string s with
|
||||
| s -> Some s
|
||||
| exception Failure _ -> None
|
||||
;;
|
||||
|
||||
module Map = struct
|
||||
include Map (struct
|
||||
type t = int
|
||||
|
||||
let compare = compare
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
module Bytes = struct
|
||||
include struct
|
||||
[@@@ocaml.warning "-32"]
|
||||
|
||||
let blit_string ~(src : string) ~src_pos ~(dst : Bytes.t) ~dst_pos ~len =
|
||||
for i = 0 to len - 1 do
|
||||
Bytes.set dst (i + dst_pos) src.[i + src_pos]
|
||||
done
|
||||
;;
|
||||
end
|
||||
|
||||
include BytesLabels
|
||||
end
|
||||
|
||||
module String = struct
|
||||
include StringLabels
|
||||
module Map = Map (String)
|
||||
|
||||
let take s i = sub s ~pos:0 ~len:(min i (String.length s))
|
||||
|
||||
let drop s n =
|
||||
let len = length s in
|
||||
sub s ~pos:(min n len) ~len:(max (len - n) 0)
|
||||
;;
|
||||
|
||||
let index s i =
|
||||
match String.index s i with
|
||||
| exception Not_found -> None
|
||||
| s -> Some s
|
||||
;;
|
||||
|
||||
let split_lines s =
|
||||
let rec loop ~last_is_cr ~acc i j =
|
||||
if j = length s
|
||||
then (
|
||||
let acc =
|
||||
if j = i || (j = i + 1 && last_is_cr)
|
||||
then acc
|
||||
else sub s ~pos:i ~len:(j - i) :: acc
|
||||
in
|
||||
List.rev acc)
|
||||
else (
|
||||
match s.[j] with
|
||||
| '\r' -> loop ~last_is_cr:true ~acc i (j + 1)
|
||||
| '\n' ->
|
||||
let line =
|
||||
let len = if last_is_cr then j - i - 1 else j - i in
|
||||
sub s ~pos:i ~len
|
||||
in
|
||||
loop ~acc:(line :: acc) (j + 1) (j + 1) ~last_is_cr:false
|
||||
| _ -> loop ~acc i (j + 1) ~last_is_cr:false)
|
||||
in
|
||||
loop ~acc:[] 0 0 ~last_is_cr:false
|
||||
;;
|
||||
|
||||
let exists =
|
||||
let rec loop s i len f =
|
||||
if i = len then false else f (unsafe_get s i) || loop s (i + 1) len f
|
||||
in
|
||||
fun s ~f -> loop s 0 (length s) f
|
||||
;;
|
||||
|
||||
let is_empty = function
|
||||
| "" -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let extract_words s ~is_word_char =
|
||||
let rec skip_blanks i =
|
||||
if i = length s
|
||||
then []
|
||||
else if is_word_char s.[i]
|
||||
then parse_word i (i + 1)
|
||||
else skip_blanks (i + 1)
|
||||
and parse_word i j =
|
||||
if j = length s
|
||||
then [ sub s ~pos:i ~len:(j - i) ]
|
||||
else if is_word_char s.[j]
|
||||
then parse_word i (j + 1)
|
||||
else sub s ~pos:i ~len:(j - i) :: skip_blanks (j + 1)
|
||||
in
|
||||
skip_blanks 0
|
||||
;;
|
||||
|
||||
let extract_comma_space_separated_words s =
|
||||
extract_words s ~is_word_char:(function
|
||||
| ',' | ' ' | '\t' | '\n' -> false
|
||||
| _ -> true)
|
||||
;;
|
||||
|
||||
let extract_blank_separated_words s =
|
||||
extract_words s ~is_word_char:(function
|
||||
| ' ' | '\t' -> false
|
||||
| _ -> true)
|
||||
;;
|
||||
|
||||
let split s ~on =
|
||||
let rec loop i j =
|
||||
if j = length s
|
||||
then [ sub s ~pos:i ~len:(j - i) ]
|
||||
else if s.[j] = on
|
||||
then sub s ~pos:i ~len:(j - i) :: loop (j + 1) (j + 1)
|
||||
else loop i (j + 1)
|
||||
in
|
||||
loop 0 0
|
||||
;;
|
||||
end
|
||||
|
||||
module Io = struct
|
||||
let open_in ?(binary = true) fn = if binary then open_in_bin fn else open_in fn
|
||||
let open_out ?(binary = true) fn = if binary then open_out_bin fn else open_out fn
|
||||
|
||||
let input_lines =
|
||||
let rec loop ic acc =
|
||||
match input_line ic with
|
||||
| exception End_of_file -> List.rev acc
|
||||
| line -> loop ic (line :: acc)
|
||||
in
|
||||
fun ic -> loop ic []
|
||||
;;
|
||||
|
||||
let with_file_in ?binary fn ~f = Exn.protectx (open_in ?binary fn) ~finally:close_in ~f
|
||||
let with_file_out ?binary p ~f = Exn.protectx (open_out ?binary p) ~finally:close_out ~f
|
||||
|
||||
let write_file ?binary fn data =
|
||||
with_file_out ?binary fn ~f:(fun oc -> output_string oc data)
|
||||
;;
|
||||
|
||||
let write_lines ?binary fn lines =
|
||||
with_file_out ?binary fn ~f:(fun oc ->
|
||||
List.iter
|
||||
~f:(fun line ->
|
||||
output_string oc line;
|
||||
output_string oc "\n")
|
||||
lines)
|
||||
;;
|
||||
|
||||
let read_all =
|
||||
(* We use 65536 because that is the size of OCaml's IO buffers. *)
|
||||
let chunk_size = 65536 in
|
||||
(* Generic function for channels such that seeking is unsupported or
|
||||
broken *)
|
||||
let read_all_generic t buffer =
|
||||
let rec loop () =
|
||||
Buffer.add_channel buffer t chunk_size;
|
||||
loop ()
|
||||
in
|
||||
try loop () with
|
||||
| End_of_file -> Buffer.contents buffer
|
||||
in
|
||||
fun t ->
|
||||
(* Optimisation for regular files: if the channel supports seeking, we
|
||||
compute the length of the file so that we read exactly what we need and
|
||||
avoid an extra memory copy. We expect that most files Dune reads are
|
||||
regular files so this optimizations seems worth it. *)
|
||||
match in_channel_length t with
|
||||
| exception Sys_error _ -> read_all_generic t (Buffer.create chunk_size)
|
||||
| n ->
|
||||
let s = really_input_string t n in
|
||||
(* For some files [in_channel_length] returns an invalid value. For
|
||||
instance for files in /proc it returns [0]. So we try to read one
|
||||
more character to make sure we did indeed reach the end of the
|
||||
file *)
|
||||
(match input_char t with
|
||||
| exception End_of_file -> s
|
||||
| c ->
|
||||
(* The [+ chunk_size] is to make sure there is at least [chunk_size]
|
||||
free space so that the first [Buffer.add_channel buffer t
|
||||
chunk_size] in [read_all_generic] does not grow the buffer. *)
|
||||
let buffer = Buffer.create (String.length s + 1 + chunk_size) in
|
||||
Buffer.add_string buffer s;
|
||||
Buffer.add_char buffer c;
|
||||
read_all_generic t buffer)
|
||||
;;
|
||||
|
||||
let read_file ?binary fn = with_file_in fn ~f:read_all ?binary
|
||||
|
||||
let with_lexbuf_from_file fn ~f =
|
||||
with_file_in fn ~f:(fun ic ->
|
||||
let lb = Lexing.from_channel ic in
|
||||
lb.lex_curr_p <- { pos_fname = fn; pos_lnum = 1; pos_bol = 0; pos_cnum = 0 };
|
||||
f lb)
|
||||
;;
|
||||
end
|
||||
|
||||
module Sexp = struct
|
||||
module T = struct
|
||||
type t =
|
||||
| Atom of string
|
||||
| List of t list
|
||||
end
|
||||
|
||||
include T
|
||||
include Csexp.Make (T)
|
||||
end
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
{1 [dune-configurator] - Helper library for gathering system configuration }
|
||||
|
||||
[dune-configurator] is a small library that helps writing OCaml scripts that
|
||||
test features available on the system, in order to generate [config.h]
|
||||
files for instance.
|
||||
|
||||
Among other things, dune-configurator allows one to:
|
||||
- test if a C program compiles
|
||||
- query [pkg-config]
|
||||
- import [#define] from OCaml header files
|
||||
- generate a [config.h] file
|
||||
|
||||
{2 API Documentation }
|
||||
|
||||
The entry point for this library is {!Configurator.V1}.
|
||||
|
||||
{2 Example }
|
||||
|
||||
The following happens in a [dune] project that contains some C code that needs
|
||||
to link against [libpng].
|
||||
|
||||
The following program ([discover/discover.ml]) uses [dune-configurator] to
|
||||
query [pkg-config] and create [cflags.sexp] and [libs.sexp]:
|
||||
|
||||
{[
|
||||
let () =
|
||||
Configurator.V1.main ~name:"libpng"
|
||||
(fun c ->
|
||||
let pkg_config =
|
||||
match Configurator.V1.Pkg_config.get c with
|
||||
| Some p -> p
|
||||
| None -> failwith "Cannot find pkg-config"
|
||||
in
|
||||
let conf = Configurator.V1.Pkg_config.query ~package:"libpng" in
|
||||
Configurator.V1.Flags.write_sexp "cflags.sexp" conf.cflags;
|
||||
Configurator.V1.Flags.write_sexp "libs.sexp" conf.libs)
|
||||
]}
|
||||
|
||||
It can be built using the following [discover/dune] file:
|
||||
|
||||
{v
|
||||
(executable
|
||||
(name discover)
|
||||
(libraries dune-configurator))
|
||||
|
||||
(rule
|
||||
(targets cflags.sexp libs.sexp)
|
||||
(action
|
||||
(run ./discover.exe)))
|
||||
v}
|
||||
|
||||
And used when building the C code in the following [dune] file:
|
||||
|
||||
{v
|
||||
(library
|
||||
(name png)
|
||||
(foreign_stubs
|
||||
(language c)
|
||||
(names bindings)
|
||||
(flags :standard (:include discover/cflags.sexp)))
|
||||
(c_library_flags :standard (:include discover/libs.sexp)))
|
||||
v}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
open Import
|
||||
|
||||
module Vars = struct
|
||||
type t = string String.Map.t
|
||||
|
||||
let of_lines lines =
|
||||
let rec loop acc = function
|
||||
| [] -> Ok acc
|
||||
| line :: lines ->
|
||||
(match String.index line ':' with
|
||||
| Some i ->
|
||||
let x =
|
||||
(* skipping 2 chars because we also need to skip the space *)
|
||||
String.take line i, String.drop line (i + 2)
|
||||
in
|
||||
loop (x :: acc) lines
|
||||
| None -> Error (Printf.sprintf "Unrecognized line: %S" line))
|
||||
in
|
||||
match loop [] lines with
|
||||
| Error _ as e -> e
|
||||
| Ok s ->
|
||||
(match String.Map.of_list s with
|
||||
| Ok _ as s -> s
|
||||
| Error (var, _, _) -> Error (sprintf "Variable %S present twice." var))
|
||||
;;
|
||||
|
||||
let of_list_exn = String.Map.of_list_exn
|
||||
let find t x = String.Map.find t x
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
(** Represent the parsed but uninterpreted output of [ocamlc -config] or
|
||||
contents of [Makefile.config]. *)
|
||||
module Vars : sig
|
||||
type t
|
||||
|
||||
val find : t -> string -> string option
|
||||
val of_list_exn : (string * string) list -> t
|
||||
|
||||
(** Parse the output of [ocamlc -config] given as a list of lines. *)
|
||||
val of_lines : string list -> (t, string) result
|
||||
end
|
||||
812
unikernel/duniverse/dune_/otherlibs/configurator/src/v1.ml
Normal file
812
unikernel/duniverse/dune_/otherlibs/configurator/src/v1.ml
Normal file
|
|
@ -0,0 +1,812 @@
|
|||
open Import
|
||||
|
||||
let die = die
|
||||
|
||||
type t =
|
||||
{ name : string
|
||||
; dest_dir : string
|
||||
; log : string -> unit
|
||||
; mutable counter : int
|
||||
; ext_obj : string
|
||||
; c_compiler : string
|
||||
; stdlib_dir : string
|
||||
; ccomp_type : string
|
||||
; c_libraries : string list
|
||||
; ocamlc_config : Ocaml_config.Vars.t
|
||||
; ocamlc_config_cmd : string
|
||||
}
|
||||
|
||||
let rec rm_rf dir =
|
||||
Array.iter (Sys.readdir dir) ~f:(fun fn ->
|
||||
let fn = dir ^/ fn in
|
||||
if Sys.is_directory fn then rm_rf fn else Unix.unlink fn);
|
||||
Unix.rmdir dir
|
||||
;;
|
||||
|
||||
module Temp = struct
|
||||
(* Copied from filename.ml and adapted for directories *)
|
||||
|
||||
let prng = lazy (Random.State.make_self_init ())
|
||||
|
||||
let gen_name ~temp_dir ~prefix ~suffix =
|
||||
let rnd = Random.State.bits (Lazy.force prng) land 0xFFFFFF in
|
||||
temp_dir ^/ Printf.sprintf "%s%06x%s" prefix rnd suffix
|
||||
;;
|
||||
|
||||
let create ~prefix ~suffix ~mk =
|
||||
let temp_dir = Filename.get_temp_dir_name () in
|
||||
let rec try_name counter =
|
||||
let name = gen_name ~temp_dir ~prefix ~suffix in
|
||||
match mk name with
|
||||
| () -> name
|
||||
| exception Unix.Unix_error _ when counter < 1000 -> try_name (counter + 1)
|
||||
in
|
||||
try_name 0
|
||||
;;
|
||||
|
||||
let create_temp_dir ~prefix ~suffix =
|
||||
let dir = create ~prefix ~suffix ~mk:(fun name -> Unix.mkdir name 0o700) in
|
||||
at_exit (fun () -> rm_rf dir);
|
||||
dir
|
||||
;;
|
||||
end
|
||||
|
||||
module Flags = struct
|
||||
let extract_words = String.extract_words
|
||||
let extract_comma_space_separated_words = String.extract_comma_space_separated_words
|
||||
let extract_blank_separated_words = String.extract_blank_separated_words
|
||||
let write_lines path s = Io.write_lines path s
|
||||
|
||||
let write_sexp path s =
|
||||
let sexp = Dune_lang.List (List.map s ~f:(fun s -> Dune_lang.Quoted_string s)) in
|
||||
Io.write_file path (Dune_lang.to_string sexp)
|
||||
;;
|
||||
end
|
||||
|
||||
module Find_in_path = struct
|
||||
let path_sep = if Sys.win32 then ';' else ':'
|
||||
|
||||
let get_path () =
|
||||
match Sys.getenv "PATH" with
|
||||
| exception Not_found -> []
|
||||
| s -> String.split s ~on:path_sep
|
||||
;;
|
||||
|
||||
let exe = if Sys.win32 then ".exe" else ""
|
||||
let prog_not_found prog = die "Program %s not found in PATH" prog
|
||||
|
||||
let best_prog dir prog =
|
||||
let fn = dir ^/ prog ^ ".opt" ^ exe in
|
||||
if Sys.file_exists fn
|
||||
then Some fn
|
||||
else (
|
||||
let fn = dir ^/ prog ^ exe in
|
||||
if Sys.file_exists fn then Some fn else None)
|
||||
;;
|
||||
|
||||
let find_ocaml_prog prog =
|
||||
match List.find_map (get_path ()) ~f:(fun dir -> best_prog dir prog) with
|
||||
| None -> prog_not_found prog
|
||||
| Some fn -> fn
|
||||
;;
|
||||
|
||||
let which prog =
|
||||
if Filename.is_implicit prog
|
||||
then
|
||||
List.find_map (get_path ()) ~f:(fun dir ->
|
||||
let fn = dir ^/ prog ^ exe in
|
||||
Option.some_if (Sys.file_exists fn) fn)
|
||||
else (
|
||||
let fn = if Filename.check_suffix prog exe then prog else prog ^ exe in
|
||||
Option.some_if (Sys.file_exists fn) fn)
|
||||
;;
|
||||
end
|
||||
|
||||
let logf t fmt = Printf.ksprintf t.log fmt
|
||||
|
||||
let gen_id t =
|
||||
let n = t.counter in
|
||||
t.counter <- n + 1;
|
||||
n
|
||||
;;
|
||||
|
||||
let quote_if_needed =
|
||||
let need_quote = function
|
||||
| ' ' | '\"' -> true
|
||||
| _ -> false
|
||||
in
|
||||
fun s ->
|
||||
if String.is_empty s || String.exists ~f:need_quote s then Filename.quote s else s
|
||||
;;
|
||||
|
||||
module Process = struct
|
||||
type result =
|
||||
{ exit_code : int
|
||||
; stdout : string
|
||||
; stderr : string
|
||||
}
|
||||
|
||||
let command_line prog args =
|
||||
String.concat ~sep:" " (List.map (prog :: args) ~f:quote_if_needed)
|
||||
;;
|
||||
|
||||
let run_process t ?dir ?env prog args =
|
||||
let prog_command_line = command_line prog args in
|
||||
logf t "run: %s" prog_command_line;
|
||||
let n = gen_id t in
|
||||
let create_process =
|
||||
let args = Array.of_list (prog :: args) in
|
||||
match env with
|
||||
| None -> Unix.create_process prog args
|
||||
| Some env ->
|
||||
let env = Array.of_list env in
|
||||
Unix.create_process_env prog args env
|
||||
in
|
||||
let stdout_fn = t.dest_dir ^/ sprintf "stdout-%d" n in
|
||||
let stderr_fn = t.dest_dir ^/ sprintf "stderr-%d" n in
|
||||
let status =
|
||||
let run () =
|
||||
let openfile f =
|
||||
Unix.openfile f [ O_WRONLY; O_CREAT; O_TRUNC; O_SHARE_DELETE ] 0o666
|
||||
in
|
||||
let stdout = openfile stdout_fn in
|
||||
let stderr = openfile stderr_fn in
|
||||
let stdin, stdin_w = Unix.pipe () in
|
||||
Unix.close stdin_w;
|
||||
let p = create_process stdin stdout stderr in
|
||||
Unix.close stdin;
|
||||
Unix.close stdout;
|
||||
Unix.close stderr;
|
||||
let _pid, status = Unix.waitpid [] p in
|
||||
status
|
||||
in
|
||||
match dir with
|
||||
| None -> run ()
|
||||
| Some d ->
|
||||
let old_dir = Sys.getcwd () in
|
||||
Exn.protect
|
||||
~f:(fun () ->
|
||||
Sys.chdir d;
|
||||
run ())
|
||||
~finally:(fun () -> Sys.chdir old_dir)
|
||||
in
|
||||
match status with
|
||||
| Unix.WSIGNALED signal -> die "signal %d killed process: %s" signal prog_command_line
|
||||
| WSTOPPED signal -> die "signal %d stopped process: %s" signal prog_command_line
|
||||
| WEXITED exit_code ->
|
||||
logf t "-> process exited with code %d" exit_code;
|
||||
let stdout = Io.read_file stdout_fn in
|
||||
let stderr = Io.read_file stderr_fn in
|
||||
logf t "-> stdout:";
|
||||
List.iter (String.split_lines stdout) ~f:(logf t " | %s");
|
||||
logf t "-> stderr:";
|
||||
List.iter (String.split_lines stderr) ~f:(logf t " | %s");
|
||||
{ exit_code; stdout; stderr }
|
||||
;;
|
||||
|
||||
(* [cmd] which cannot be quoted (such as [t.c_compiler] which contains some
|
||||
flags) followed by additional arguments. *)
|
||||
let command_args cmd args =
|
||||
String.concat ~sep:" " (cmd :: List.map args ~f:quote_if_needed)
|
||||
;;
|
||||
|
||||
let run_command t ?dir ?(env = []) cmd =
|
||||
logf t "run: %s" cmd;
|
||||
let n = gen_id t in
|
||||
let stdout_fn = t.dest_dir ^/ sprintf "stdout-%d" n in
|
||||
let stderr_fn = t.dest_dir ^/ sprintf "stderr-%d" n in
|
||||
let in_dir =
|
||||
match dir with
|
||||
| None -> ""
|
||||
| Some dir -> sprintf "cd %s && " (Filename.quote dir)
|
||||
in
|
||||
let with_env =
|
||||
match env with
|
||||
| [] -> ""
|
||||
| _ -> "env " ^ String.concat ~sep:" " env
|
||||
in
|
||||
let exit_code =
|
||||
Printf.ksprintf
|
||||
Sys.command
|
||||
"%s%s %s > %s 2> %s"
|
||||
in_dir
|
||||
with_env
|
||||
cmd
|
||||
(Filename.quote stdout_fn)
|
||||
(Filename.quote stderr_fn)
|
||||
in
|
||||
let stdout = Io.read_file stdout_fn in
|
||||
let stderr = Io.read_file stderr_fn in
|
||||
logf t "-> process exited with code %d" exit_code;
|
||||
logf t "-> stdout:";
|
||||
List.iter (String.split_lines stdout) ~f:(logf t " | %s");
|
||||
logf t "-> stderr:";
|
||||
List.iter (String.split_lines stderr) ~f:(logf t " | %s");
|
||||
{ exit_code; stdout; stderr }
|
||||
;;
|
||||
|
||||
let run_command_capture_exn t ?dir ?env cmd =
|
||||
let { exit_code; stdout; stderr } = run_command t ?dir ?env cmd in
|
||||
if exit_code <> 0
|
||||
then die "command exited with code %d: %s" exit_code cmd
|
||||
else if not (String.is_empty stderr)
|
||||
then die "command has non-empty stderr: %s" cmd
|
||||
else stdout
|
||||
;;
|
||||
|
||||
let run_command_ok t ?dir ?env cmd = (run_command t ?dir ?env cmd).exit_code = 0
|
||||
let run t ?dir ?env prog args = run_command t ?dir ?env (command_line prog args)
|
||||
|
||||
let run_capture_exn t ?dir ?env prog args =
|
||||
run_command_capture_exn t ?dir ?env (command_line prog args)
|
||||
;;
|
||||
|
||||
let run_ok t ?dir ?env prog args = run_command_ok t ?dir ?env (command_line prog args)
|
||||
end
|
||||
|
||||
let ocaml_config_var t var = Ocaml_config.Vars.find t.ocamlc_config var
|
||||
|
||||
let ocaml_config_var_exn t var =
|
||||
match Ocaml_config.Vars.find t.ocamlc_config var with
|
||||
| None -> die "variable %S not found in the output of `%s`" var t.ocamlc_config_cmd
|
||||
| Some s -> s
|
||||
;;
|
||||
|
||||
type config =
|
||||
{ ocamlc : string
|
||||
; vars : Ocaml_config.Vars.t
|
||||
}
|
||||
|
||||
let dune_is_too_old ~min:v =
|
||||
die
|
||||
"You seem to be running dune < %s. This version of dune-configurator requires at \
|
||||
least dune %s."
|
||||
v
|
||||
v
|
||||
;;
|
||||
|
||||
let read_dot_dune_configurator_file ~build_dir =
|
||||
let file = Filename.concat build_dir ".dune/configurator.v2" in
|
||||
if not (Sys.file_exists file) then dune_is_too_old ~min:"2.6";
|
||||
let open Sexp in
|
||||
let unable_to_parse err = die "Unable to parse %S.@.%s@." file err in
|
||||
let sexp =
|
||||
match Io.with_file_in file ~f:Sexp.input with
|
||||
| Ok s -> s
|
||||
| Error e -> unable_to_parse e
|
||||
in
|
||||
match sexp with
|
||||
| Atom _ -> unable_to_parse "unexpected atom"
|
||||
| List xs ->
|
||||
let field name =
|
||||
match
|
||||
List.find_map xs ~f:(function
|
||||
| List [ Atom name'; f ] when name = name' -> Some f
|
||||
| _ -> None)
|
||||
with
|
||||
| None -> die "unable to find field %S" name
|
||||
| Some f -> f
|
||||
in
|
||||
let ocamlc =
|
||||
match field "ocamlc" with
|
||||
| Atom o -> o
|
||||
| _ -> die "invalid ocamlc field"
|
||||
in
|
||||
let vars =
|
||||
let bindings =
|
||||
match field "ocaml_config_vars" with
|
||||
| List bindings ->
|
||||
List.map bindings ~f:(function
|
||||
| List [ Atom k; Atom v ] -> k, v
|
||||
| _ -> die "invalid output")
|
||||
| _ -> die "invalid output"
|
||||
in
|
||||
Ocaml_config.Vars.of_list_exn bindings
|
||||
in
|
||||
{ ocamlc; vars }
|
||||
;;
|
||||
|
||||
let fill_in_fields_that_depends_on_ocamlc_config t =
|
||||
let get = ocaml_config_var_exn t in
|
||||
let get_flags var = get var |> String.trim |> Flags.extract_blank_separated_words in
|
||||
let c_compiler, c_libraries =
|
||||
match Ocaml_config.Vars.find t.ocamlc_config "c_compiler" with
|
||||
| Some c_comp -> c_comp ^ " " ^ get "ocamlc_cflags", get_flags "native_c_libraries"
|
||||
| None -> get "bytecomp_c_compiler", get_flags "bytecomp_c_libraries"
|
||||
in
|
||||
{ t with
|
||||
ext_obj = get "ext_obj"
|
||||
; c_compiler
|
||||
; stdlib_dir = get "standard_library"
|
||||
; ccomp_type = get "ccomp_type"
|
||||
; c_libraries
|
||||
}
|
||||
;;
|
||||
|
||||
let create_from_inside_dune ~dest_dir ~log ~build_dir ~name =
|
||||
let dest_dir =
|
||||
match dest_dir with
|
||||
| Some dir -> dir
|
||||
| None -> Temp.create_temp_dir ~prefix:"ocaml-configurator" ~suffix:""
|
||||
in
|
||||
let { ocamlc; vars = ocamlc_config } = read_dot_dune_configurator_file ~build_dir in
|
||||
let ocamlc_config_cmd = Process.command_line ocamlc [ "-config" ] in
|
||||
fill_in_fields_that_depends_on_ocamlc_config
|
||||
{ name
|
||||
; log
|
||||
; dest_dir
|
||||
; counter = 0
|
||||
; ocamlc_config
|
||||
; ocamlc_config_cmd
|
||||
; ext_obj = ""
|
||||
; c_compiler = ""
|
||||
; stdlib_dir = ""
|
||||
; ccomp_type = ""
|
||||
; c_libraries = []
|
||||
}
|
||||
;;
|
||||
|
||||
let create ?dest_dir ?ocamlc ?(log = ignore) name =
|
||||
let inside_dune =
|
||||
match Sys.getenv "INSIDE_DUNE" with
|
||||
| exception Not_found -> None
|
||||
| n -> Some n
|
||||
in
|
||||
match ocamlc, inside_dune with
|
||||
| None, Some build_dir when build_dir <> "1" ->
|
||||
create_from_inside_dune ~dest_dir ~log ~build_dir ~name
|
||||
| _ ->
|
||||
let dest_dir =
|
||||
match dest_dir with
|
||||
| Some dir -> dir
|
||||
| None -> Temp.create_temp_dir ~prefix:"ocaml-configurator" ~suffix:""
|
||||
in
|
||||
let ocamlc =
|
||||
match ocamlc with
|
||||
| Some fn -> fn
|
||||
| None -> Find_in_path.find_ocaml_prog "ocamlc"
|
||||
in
|
||||
let ocamlc_config_cmd = Process.command_line ocamlc [ "-config" ] in
|
||||
let t =
|
||||
{ name
|
||||
; log
|
||||
; dest_dir
|
||||
; counter = 0
|
||||
; ext_obj = ""
|
||||
; c_compiler = ""
|
||||
; stdlib_dir = ""
|
||||
; ccomp_type = ""
|
||||
; c_libraries = []
|
||||
; ocamlc_config = Ocaml_config.Vars.of_list_exn []
|
||||
; ocamlc_config_cmd
|
||||
}
|
||||
in
|
||||
let ocamlc_config =
|
||||
let ocamlc_config_output =
|
||||
Process.run_command_capture_exn t ~dir:dest_dir ocamlc_config_cmd
|
||||
|> String.split_lines
|
||||
in
|
||||
match Ocaml_config.Vars.of_lines ocamlc_config_output with
|
||||
| Ok x -> x
|
||||
| Error msg -> die "Failed to parse the output of '%s':@\n%s" ocamlc_config_cmd msg
|
||||
in
|
||||
fill_in_fields_that_depends_on_ocamlc_config { t with ocamlc_config }
|
||||
;;
|
||||
|
||||
let is_msvc t =
|
||||
match t.ccomp_type with
|
||||
| "msvc" -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let compile_and_link_c_prog t ?(c_flags = []) ?(link_flags = []) code =
|
||||
let dir = t.dest_dir ^/ sprintf "c-test-%d" (gen_id t) in
|
||||
Unix.mkdir dir 0o777;
|
||||
let base = dir ^/ "test" in
|
||||
let c_fname = base ^ ".c" in
|
||||
let exe_fname = base ^ ".exe" in
|
||||
Io.write_file c_fname code;
|
||||
logf t "compiling c program:";
|
||||
List.iter (String.split_lines code) ~f:(logf t " | %s");
|
||||
let run_ok args =
|
||||
Process.run_command_ok t ~dir (Process.command_args t.c_compiler args)
|
||||
in
|
||||
let output_flag = if is_msvc t then [ "-Fe" ^ exe_fname ] else [ "-o"; exe_fname ] in
|
||||
let ok =
|
||||
run_ok
|
||||
(List.concat
|
||||
[ c_flags
|
||||
; [ "-I"; t.stdlib_dir ]
|
||||
; output_flag
|
||||
; [ c_fname ]
|
||||
; t.c_libraries
|
||||
; link_flags
|
||||
])
|
||||
in
|
||||
if ok then Ok () else Error ()
|
||||
;;
|
||||
|
||||
let compile_c_prog t ?(c_flags = []) code =
|
||||
let dir = t.dest_dir ^/ sprintf "c-test-%d" (gen_id t) in
|
||||
Unix.mkdir dir 0o777;
|
||||
let base = dir ^/ "test" in
|
||||
let c_fname = base ^ ".c" in
|
||||
let obj_fname = base ^ t.ext_obj in
|
||||
Io.write_file c_fname code;
|
||||
logf t "compiling c program:";
|
||||
List.iter (String.split_lines code) ~f:(logf t " | %s");
|
||||
let ok =
|
||||
let output_flag = if is_msvc t then [ "-Fo" ^ obj_fname ] else [ "-o"; obj_fname ] in
|
||||
Process.run_command_ok
|
||||
t
|
||||
~dir
|
||||
(Process.command_args
|
||||
t.c_compiler
|
||||
(List.concat
|
||||
[ c_flags
|
||||
; [ "-I"; t.stdlib_dir ]
|
||||
; output_flag
|
||||
; [ "-c"; c_fname ]
|
||||
; t.c_libraries
|
||||
]))
|
||||
in
|
||||
if ok then Ok obj_fname else Error ()
|
||||
;;
|
||||
|
||||
let c_test t ?c_flags ?link_flags code =
|
||||
match compile_and_link_c_prog t ?c_flags ?link_flags code with
|
||||
| Ok _ -> true
|
||||
| Error _ -> false
|
||||
;;
|
||||
|
||||
module C_define = struct
|
||||
module Type = struct
|
||||
type t =
|
||||
| Switch
|
||||
| Int
|
||||
| String
|
||||
|
||||
let name = function
|
||||
| Switch -> "bool"
|
||||
| Int -> "int"
|
||||
| String -> "string"
|
||||
;;
|
||||
end
|
||||
|
||||
module Value = struct
|
||||
type t =
|
||||
| Switch of bool
|
||||
| Int of int
|
||||
| String of string
|
||||
end
|
||||
|
||||
let extract_program ?prelude includes vars =
|
||||
let has_type t = List.exists vars ~f:(fun (_, t') -> t = t') in
|
||||
let buf = Buffer.create 1024 in
|
||||
let pr fmt = Printf.bprintf buf (fmt ^^ "\n") in
|
||||
List.iter includes ~f:(pr "#include <%s>");
|
||||
pr "";
|
||||
Option.iter prelude ~f:(pr "%s");
|
||||
if has_type Type.Int
|
||||
then
|
||||
pr
|
||||
{|
|
||||
#define DUNE_ABS(x) ((x >= 0)? x: -(x))
|
||||
#define DUNE_D0(x) ('0'+(DUNE_ABS(x)/1 )%%10)
|
||||
#define DUNE_D1(x) ('0'+(DUNE_ABS(x)/10 )%%10), DUNE_D0(x)
|
||||
#define DUNE_D2(x) ('0'+(DUNE_ABS(x)/100 )%%10), DUNE_D1(x)
|
||||
#define DUNE_D3(x) ('0'+(DUNE_ABS(x)/1000 )%%10), DUNE_D2(x)
|
||||
#define DUNE_D4(x) ('0'+(DUNE_ABS(x)/10000 )%%10), DUNE_D3(x)
|
||||
#define DUNE_D5(x) ('0'+(DUNE_ABS(x)/100000 )%%10), DUNE_D4(x)
|
||||
#define DUNE_D6(x) ('0'+(DUNE_ABS(x)/1000000 )%%10), DUNE_D5(x)
|
||||
#define DUNE_D7(x) ('0'+(DUNE_ABS(x)/10000000 )%%10), DUNE_D6(x)
|
||||
#define DUNE_D8(x) ('0'+(DUNE_ABS(x)/100000000 )%%10), DUNE_D7(x)
|
||||
#define DUNE_D9(x) ('0'+(DUNE_ABS(x)/1000000000)%%10), DUNE_D8(x)
|
||||
#define DUNE_SIGN(x) ((x >= 0)? '0': '-')
|
||||
|};
|
||||
List.iteri vars ~f:(fun i (name, t) ->
|
||||
match t with
|
||||
| Type.Int ->
|
||||
let c_arr_i =
|
||||
let b = Buffer.create 8 in
|
||||
let is = string_of_int i in
|
||||
for i = 0 to String.length is - 1 do
|
||||
Printf.bprintf b "'%c', " is.[i]
|
||||
done;
|
||||
Buffer.contents b
|
||||
in
|
||||
pr
|
||||
{|
|
||||
const char s%i[] = {
|
||||
'B', 'E', 'G', 'I', 'N', '-', %s'-',
|
||||
DUNE_SIGN((%s)),
|
||||
DUNE_D9((%s)),
|
||||
'-', 'E', 'N', 'D'
|
||||
};
|
||||
|}
|
||||
i
|
||||
c_arr_i
|
||||
name
|
||||
name
|
||||
| String -> pr {|const char *s%i = "BEGIN-%i-" %s "-END";|} i i name
|
||||
| Switch ->
|
||||
pr
|
||||
{|
|
||||
#ifdef %s
|
||||
const char *s%i = "BEGIN-%i-true-END";
|
||||
#else
|
||||
const char *s%i = "BEGIN-%i-false-END";
|
||||
#endif
|
||||
|}
|
||||
name
|
||||
i
|
||||
i
|
||||
i
|
||||
i);
|
||||
Buffer.contents buf
|
||||
;;
|
||||
|
||||
let extract_values obj_file vars =
|
||||
let values =
|
||||
Io.with_lexbuf_from_file obj_file ~f:(Extract_obj.extract [])
|
||||
|> List.fold_left ~init:Int.Map.empty ~f:(fun acc (key, v) ->
|
||||
Int.Map.update acc key ~f:(function
|
||||
| None -> Some [ v ]
|
||||
| Some vs -> Some (v :: vs)))
|
||||
in
|
||||
List.mapi vars ~f:(fun i (name, t) ->
|
||||
let raw_vals =
|
||||
match Int.Map.find values i with
|
||||
| Some v -> v
|
||||
| None -> die "Unable to get value for %s" name
|
||||
in
|
||||
let parse_val_or_exn f =
|
||||
let f x =
|
||||
match f x with
|
||||
| Some s -> s
|
||||
| None ->
|
||||
die
|
||||
"Unable to read variable %S of type %s. Invalid value %S in %s found"
|
||||
name
|
||||
(Type.name t)
|
||||
x
|
||||
obj_file
|
||||
in
|
||||
let vs =
|
||||
List.map ~f:(fun x -> x, f x) raw_vals
|
||||
|> List.sort_uniq ~cmp:(fun (_, x) (_, y) -> compare x y)
|
||||
in
|
||||
match vs with
|
||||
| [] -> assert false
|
||||
| [ (_, v) ] -> v
|
||||
| vs ->
|
||||
let vs = List.map ~f:fst vs in
|
||||
die
|
||||
"Duplicate values for %s:\n%s"
|
||||
name
|
||||
(vs |> List.map ~f:(sprintf "- %s") |> String.concat ~sep:"\n")
|
||||
in
|
||||
let value =
|
||||
match t with
|
||||
| Type.Switch -> Value.Switch (parse_val_or_exn Bool.of_string)
|
||||
| Int -> Value.Int (parse_val_or_exn Int.of_string)
|
||||
| String -> String (parse_val_or_exn Option.some)
|
||||
in
|
||||
name, value)
|
||||
;;
|
||||
|
||||
let import t ?prelude ?c_flags ~includes vars =
|
||||
let program = extract_program ?prelude ("stdio.h" :: includes) vars in
|
||||
match compile_c_prog t ?c_flags program with
|
||||
| Error _ -> die "failed to compile program"
|
||||
| Ok obj -> extract_values obj vars
|
||||
;;
|
||||
|
||||
let gen_header_file t ~fname ?protection_var vars =
|
||||
let protection_var =
|
||||
match protection_var with
|
||||
| Some v -> v
|
||||
| None ->
|
||||
String.map
|
||||
(t.name ^ "_" ^ Filename.basename fname)
|
||||
~f:(function
|
||||
| 'a' .. 'z' as c -> Char.uppercase_ascii c
|
||||
| ('A' .. 'Z' | '0' .. '9') as c -> c
|
||||
| _ -> '_')
|
||||
in
|
||||
let vars = List.sort vars ~cmp:(fun (a, _) (b, _) -> compare a b) in
|
||||
let lines =
|
||||
List.map vars ~f:(fun (name, value) ->
|
||||
match (value : Value.t) with
|
||||
| Switch false -> sprintf "#undef %s" name
|
||||
| Switch true -> sprintf "#define %s" name
|
||||
| Int n -> sprintf "#define %s (%d)" name n
|
||||
| String s -> sprintf "#define %s %S" name s)
|
||||
in
|
||||
let lines =
|
||||
List.concat
|
||||
[ [ sprintf "#ifndef %s" protection_var; sprintf "#define %s" protection_var ]
|
||||
; lines
|
||||
; [ "#endif" ]
|
||||
]
|
||||
in
|
||||
logf t "writing header file %s" fname;
|
||||
List.iter lines ~f:(logf t " | %s");
|
||||
let tmp_fname = fname ^ ".tmp" in
|
||||
Io.write_lines tmp_fname lines;
|
||||
Sys.rename tmp_fname fname
|
||||
;;
|
||||
end
|
||||
|
||||
let which t prog =
|
||||
logf t "which: %s" prog;
|
||||
let x = Find_in_path.which prog in
|
||||
logf
|
||||
t
|
||||
"-> %s"
|
||||
(match x with
|
||||
| None -> "not found"
|
||||
| Some fn -> "found: " ^ quote_if_needed fn);
|
||||
x
|
||||
;;
|
||||
|
||||
module Pkg_config = struct
|
||||
type nonrec t =
|
||||
{ pkg_config : string
|
||||
; pkg_config_args : string list
|
||||
; configurator : t
|
||||
}
|
||||
|
||||
let get c =
|
||||
let get_pkg_config_args default =
|
||||
match Sys.getenv "PKG_CONFIG_ARGN" with
|
||||
| s -> String.split ~on:' ' s
|
||||
| exception Not_found -> default
|
||||
in
|
||||
match Sys.getenv "PKG_CONFIG" with
|
||||
| s ->
|
||||
Option.map (which c s) ~f:(fun pkg_config ->
|
||||
let pkg_config_args = get_pkg_config_args [] in
|
||||
{ pkg_config; pkg_config_args; configurator = c })
|
||||
| exception Not_found ->
|
||||
(match which c "pkgconf" with
|
||||
| None ->
|
||||
Option.map (which c "pkg-config") ~f:(fun pkg_config ->
|
||||
let pkg_config_args = get_pkg_config_args [] in
|
||||
{ pkg_config; pkg_config_args; configurator = c })
|
||||
| Some pkg_config ->
|
||||
let pkg_config_args =
|
||||
get_pkg_config_args
|
||||
(match ocaml_config_var c "target" with
|
||||
| None -> []
|
||||
| Some target -> [ "--personality"; target ])
|
||||
in
|
||||
Some { pkg_config; pkg_config_args; configurator = c })
|
||||
;;
|
||||
|
||||
type package_conf =
|
||||
{ libs : string list
|
||||
; cflags : string list
|
||||
}
|
||||
|
||||
let gen_query t ~package ~expr =
|
||||
let c = t.configurator in
|
||||
let dir = c.dest_dir in
|
||||
let expr =
|
||||
match expr with
|
||||
| Some e -> e
|
||||
| None ->
|
||||
if
|
||||
String.exists package ~f:(function
|
||||
| '=' | '>' | '<' -> true
|
||||
| _ -> false)
|
||||
then
|
||||
warn
|
||||
"Package name %S contains invalid characters. Use Pkg_config.query_expr to \
|
||||
construct proper queries"
|
||||
package;
|
||||
package
|
||||
in
|
||||
let env =
|
||||
match ocaml_config_var c "system" with
|
||||
| Some "macosx" ->
|
||||
let open Option.O in
|
||||
which c "brew"
|
||||
>>= fun brew ->
|
||||
let new_pkg_config_path =
|
||||
let prefix = String.trim (Process.run_capture_exn c ~dir brew [ "--prefix" ]) in
|
||||
let p = sprintf "%s/opt/%s/lib/pkgconfig" (quote_if_needed prefix) package in
|
||||
Option.some_if
|
||||
(match Sys.is_directory p with
|
||||
| s -> s
|
||||
| exception Sys_error _ -> false)
|
||||
p
|
||||
in
|
||||
new_pkg_config_path
|
||||
>>| fun new_pkg_config_path ->
|
||||
let _PKG_CONFIG_PATH = "PKG_CONFIG_PATH" in
|
||||
let pkg_config_path =
|
||||
match Sys.getenv _PKG_CONFIG_PATH with
|
||||
| s -> s ^ ":"
|
||||
| exception Not_found -> ""
|
||||
in
|
||||
[ sprintf "%s=%s%s" _PKG_CONFIG_PATH pkg_config_path new_pkg_config_path ]
|
||||
| _ -> None
|
||||
in
|
||||
let pc_flags = "--print-errors" in
|
||||
let { Process.exit_code; stderr; _ } =
|
||||
Process.run_process c ~dir ?env t.pkg_config (t.pkg_config_args @ [ pc_flags; expr ])
|
||||
in
|
||||
if exit_code = 0
|
||||
then (
|
||||
let run what =
|
||||
match
|
||||
String.trim
|
||||
(Process.run_capture_exn
|
||||
c
|
||||
~dir
|
||||
?env
|
||||
t.pkg_config
|
||||
(t.pkg_config_args @ [ what; package ]))
|
||||
with
|
||||
| "" -> []
|
||||
| s -> String.extract_blank_separated_words s
|
||||
in
|
||||
Ok { libs = run "--libs"; cflags = run "--cflags" })
|
||||
else Error stderr
|
||||
;;
|
||||
|
||||
let query t ~package = Result.to_option @@ gen_query t ~package ~expr:None
|
||||
|
||||
let query_expr t ~package ~expr =
|
||||
Result.to_option @@ gen_query t ~package ~expr:(Some expr)
|
||||
;;
|
||||
|
||||
let query_expr_err t ~package ~expr = gen_query t ~package ~expr:(Some expr)
|
||||
end
|
||||
|
||||
let main ?(args = []) ~name f =
|
||||
let build_dir =
|
||||
match Sys.getenv "INSIDE_DUNE" with
|
||||
| exception Not_found ->
|
||||
die
|
||||
"Configurator scripts must be run with Dune. To manually run a script, use $ \
|
||||
dune exec."
|
||||
| "1" -> dune_is_too_old ~min:"2.3"
|
||||
| s -> s
|
||||
in
|
||||
let verbose = ref false in
|
||||
let dest_dir = ref None in
|
||||
let args =
|
||||
Arg.align
|
||||
([ "-verbose", Arg.Set verbose, " be verbose"
|
||||
; ( "-dest-dir"
|
||||
, Arg.String (fun s -> dest_dir := Some s)
|
||||
, "DIR save temporary files to this directory" )
|
||||
]
|
||||
@ args)
|
||||
in
|
||||
let anon s = raise (Arg.Bad (sprintf "don't know what to do with %s" s)) in
|
||||
let usage = sprintf "%s [OPTIONS]" (Filename.basename Sys.executable_name) in
|
||||
Arg.parse args anon usage;
|
||||
let log_db = ref [] in
|
||||
let log s = log_db := s :: !log_db in
|
||||
try
|
||||
let t =
|
||||
create_from_inside_dune
|
||||
~dest_dir:!dest_dir
|
||||
~log:(if !verbose then prerr_endline else log)
|
||||
~build_dir
|
||||
~name
|
||||
in
|
||||
f t
|
||||
with
|
||||
| exn ->
|
||||
let bt = Printexc.get_raw_backtrace () in
|
||||
List.iter (List.rev !log_db) ~f:(eprintf "%s\n");
|
||||
(match exn with
|
||||
| Fatal_error msg ->
|
||||
eprintf "Error: %s\n%!" msg;
|
||||
exit 1
|
||||
| _ -> Exn.raise_with_backtrace exn bt)
|
||||
;;
|
||||
185
unikernel/duniverse/dune_/otherlibs/configurator/src/v1.mli
Normal file
185
unikernel/duniverse/dune_/otherlibs/configurator/src/v1.mli
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
type t
|
||||
|
||||
val create
|
||||
: ?dest_dir:string
|
||||
-> ?ocamlc:string
|
||||
-> ?log:(string -> unit)
|
||||
-> string (** name, such as library name *)
|
||||
-> t
|
||||
|
||||
(** Return the value associated to a variable in the output of [ocamlc -config] *)
|
||||
val ocaml_config_var : t -> string -> string option
|
||||
|
||||
val ocaml_config_var_exn : t -> string -> string
|
||||
|
||||
(** [c_test t ?c_flags ?link_flags c_code] try to compile and link the C code
|
||||
given in [c_code]. Return whether compilation was successful. *)
|
||||
val c_test
|
||||
: t
|
||||
-> ?c_flags:string list (** default: [] *)
|
||||
-> ?link_flags:string list (** default: [] *)
|
||||
-> string
|
||||
-> bool
|
||||
|
||||
module C_define : sig
|
||||
module Type : sig
|
||||
type t =
|
||||
| Switch (** defined/undefined *)
|
||||
| Int
|
||||
| String
|
||||
end
|
||||
|
||||
module Value : sig
|
||||
type t =
|
||||
| Switch of bool
|
||||
| Int of int
|
||||
| String of string
|
||||
end
|
||||
|
||||
(** Import some #define from the given header files. For instance:
|
||||
|
||||
{v
|
||||
# C.C_define.import c ~includes:"caml/config.h" ["ARCH_SIXTYFOUR", Switch];;
|
||||
- (string * Configurator.C_define.Value.t) list = ["ARCH_SIXTYFOUR", Switch true]
|
||||
v} *)
|
||||
val import
|
||||
: t
|
||||
-> ?prelude:string
|
||||
(** Define extra code be used with extracting values below. Note that
|
||||
the compiled code is never executed. *)
|
||||
-> ?c_flags:string list
|
||||
-> includes:string list
|
||||
-> (string * Type.t) list
|
||||
-> (string * Value.t) list
|
||||
|
||||
(** Generate a C header file containing the following #define.
|
||||
[protection_var] is used to enclose the file with:
|
||||
|
||||
{[
|
||||
#ifndef BLAH #define BLAH ... #endif
|
||||
]}
|
||||
|
||||
If not specified, it is inferred from the name given to [create] and the
|
||||
filename. *)
|
||||
val gen_header_file
|
||||
: t
|
||||
-> fname:string
|
||||
-> ?protection_var:string
|
||||
-> (string * Value.t) list
|
||||
-> unit
|
||||
end
|
||||
|
||||
module Pkg_config : sig
|
||||
type configurator = t
|
||||
type t
|
||||
|
||||
(** Search a pkg-config implementation in PATH. Use the one
|
||||
defined in [PKG_CONFIG] environment variable if set else try
|
||||
[pkgconf] then [pkg-config]. Append the [PKG_CONFIG_PATH]
|
||||
environment variable to the searched pathes. Returns [None] if
|
||||
nothing is not found. *)
|
||||
val get : configurator -> t option
|
||||
|
||||
type package_conf =
|
||||
{ libs : string list
|
||||
; cflags : string list
|
||||
}
|
||||
|
||||
(** [query t ~package] query pkg-config for the [package]. The package must
|
||||
not contain a version constraint. Multiple, unversioned packages are
|
||||
separated with spaces, for example "gtk+-3.0 gtksourceview-3.0". By
|
||||
default, the OCaml compiler [target] is passed to pkgconf as
|
||||
[--personality] argument. An alternative list of arguments can be
|
||||
specified by setting the [PKG_CONFIG_ARGN] environment variable.
|
||||
Returns [None] if [package] is not available *)
|
||||
val query : t -> package:string -> package_conf option
|
||||
|
||||
val query_expr : t -> package:string -> expr:string -> package_conf option
|
||||
[@@ocaml.deprecated "please use [query_expr_err]"]
|
||||
|
||||
(** [query_expr_err t ~package ~expr] query pkg-config for the [package].
|
||||
[expr] may contain a version constraint, for example "gtk+-3.0 >= 3.18".
|
||||
[package] must be just the name of the package. If [expr] is specified,
|
||||
[package] must be specified as well. By default, the OCaml compiler
|
||||
"target" is passed to pkgconf as [--personality] argument. An
|
||||
alternative list of arguments can be specified by setting the
|
||||
[PKG_CONFIG_ARGN] environment variable.
|
||||
Returns [Error error_msg] if [package] is not available *)
|
||||
val query_expr_err
|
||||
: t
|
||||
-> package:string
|
||||
-> expr:string
|
||||
-> (package_conf, string) result
|
||||
end
|
||||
with type configurator := t
|
||||
|
||||
module Flags : sig
|
||||
(** [write_sexp fname s] writes the list of strings [s] to the file [fname] in
|
||||
an appropriate format so that it can used in [dune] files with
|
||||
[(:include [fname])]. *)
|
||||
val write_sexp : string -> string list -> unit
|
||||
|
||||
(** [write_lines fname s] writes the list of string [s] to the file [fname]
|
||||
with one line per string so that it can be used in Dune action rules with
|
||||
[%{read-lines:<path>}]. *)
|
||||
val write_lines : string -> string list -> unit
|
||||
|
||||
(** [extract_comma_space_separated_words s] returns a list of words in [s]
|
||||
that are separated by a newline, tab, space or comma character. *)
|
||||
val extract_comma_space_separated_words : string -> string list
|
||||
|
||||
(** [extract_blank_separated_words s] returns a list of words in [s] that are
|
||||
separated by a tab or space character. *)
|
||||
val extract_blank_separated_words : string -> string list
|
||||
|
||||
(** [extract_words s ~is_word_char] will split the string [s] into a list of
|
||||
words. A valid word character is defined by the [is_word_char] predicate
|
||||
returning true and anything else is considered a separator. Any blank
|
||||
words are filtered out of the results. *)
|
||||
val extract_words : string -> is_word_char:(char -> bool) -> string list
|
||||
end
|
||||
|
||||
(** [which t prog] seek [prog] in the PATH and return the name of the program
|
||||
prefixed with the first path where it is found. Return [None] the the
|
||||
program is not found. *)
|
||||
val which : t -> string -> string option
|
||||
|
||||
(** Execute external programs. *)
|
||||
module Process : sig
|
||||
type result =
|
||||
{ exit_code : int
|
||||
; stdout : string
|
||||
; stderr : string
|
||||
}
|
||||
|
||||
(** [run t prog args] runs [prog] with arguments [args] and returns its exit
|
||||
status together with the content of stdout and stderr. The action is
|
||||
logged.
|
||||
|
||||
@param dir change to [dir] before running the command.
|
||||
@param env specify additional environment variables as a list of the form
|
||||
NAME=VALUE. *)
|
||||
val run : t -> ?dir:string -> ?env:string list -> string -> string list -> result
|
||||
|
||||
(** [run_capture_exn t prog args] same as [run t prog args] but returns
|
||||
[stdout] and {!die} if the error code is nonzero or there is some output
|
||||
on [stderr]. *)
|
||||
val run_capture_exn
|
||||
: t
|
||||
-> ?dir:string
|
||||
-> ?env:string list
|
||||
-> string
|
||||
-> string list
|
||||
-> string
|
||||
|
||||
(** [run_ok t prog args] same as [run t prog args] but only cares whether the
|
||||
execution terminated successfully (i.e., returned an error code of [0]). *)
|
||||
val run_ok : t -> ?dir:string -> ?env:string list -> string -> string list -> bool
|
||||
end
|
||||
|
||||
(** Typical entry point for configurator programs *)
|
||||
val main : ?args:(Arg.key * Arg.spec * Arg.doc) list -> name:string -> (t -> unit) -> unit
|
||||
|
||||
(** Abort execution. If raised from within [main], the argument of [die] is
|
||||
printed as [Error: <message>]. *)
|
||||
val die : ('a, unit, string, 'b) format4 -> 'a
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
module C = Configurator.V1
|
||||
|
||||
let unix = {|
|
||||
#include <math.h>
|
||||
void *addr = &sin;
|
||||
int main(void) {
|
||||
return 0;
|
||||
}
|
||||
|}
|
||||
|
||||
let windows = {|
|
||||
#include <winsock2.h>
|
||||
void *addr = &gethostname;
|
||||
int main(void) {
|
||||
return 0;
|
||||
}
|
||||
|}
|
||||
|
||||
let main c =
|
||||
let code = if Sys.os_type = "Win32" then windows else unix in
|
||||
let b = C.c_test c code in
|
||||
let f = open_out_bin "out" in
|
||||
output_char f (if b then '1' else '0');
|
||||
close_out f
|
||||
|
||||
let () = C.main ~name:"configurator-c-libraries" main
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(executable
|
||||
(name discover)
|
||||
(libraries dune.configurator))
|
||||
|
|
@ -0,0 +1 @@
|
|||
(lang dune 2.8)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
Test that configurator always picks the value of the `c_libraries`
|
||||
flag from `ocamlc -config`. If not, there's a failure to link a
|
||||
configuration test program that uses functions from these libraries.
|
||||
For that, we need functions outside of libc. On Unix, that would be
|
||||
`sin(3)` that requires the `-lm` flag for the math library, and on
|
||||
Windows `gethostname` that requires WinSock2 (`ws2_32.dll`).
|
||||
|
||||
link successfully
|
||||
==================================
|
||||
|
||||
$ dune exec -- ./discover.exe
|
||||
$ cat out
|
||||
1
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
let () =
|
||||
let module C = Configurator.V1 in
|
||||
C.main ~name:"foo" (fun _c ->
|
||||
C.Flags.write_lines "foo" ["asdf"])
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
(executable
|
||||
(name discover)
|
||||
(modules discover)
|
||||
(libraries dune.configurator))
|
||||
|
|
@ -0,0 +1 @@
|
|||
(lang dune 1.1)
|
||||
|
|
@ -0,0 +1 @@
|
|||
$ dune exec ./discover.exe
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(executable
|
||||
(name run)
|
||||
(libraries dune.configurator))
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
module Configurator = Configurator.V1
|
||||
|
||||
let () =
|
||||
Configurator.main ~name:"c_test" (fun t ->
|
||||
let c_result =
|
||||
Configurator.c_test t {c|
|
||||
#include <stdio.h>
|
||||
int main(void)
|
||||
{
|
||||
printf("Hello, World!");
|
||||
return 0;
|
||||
}
|
||||
|c} in
|
||||
assert c_result;
|
||||
print_endline "Successfully compiled c program"
|
||||
)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(executable
|
||||
(name run)
|
||||
(libraries dune.configurator))
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
module Configurator = Configurator.V1
|
||||
|
||||
let () =
|
||||
begin match Sys.getenv "INSIDE_DUNE" with
|
||||
| exception Not_found -> failwith "INSIDE_DUNE is not passed"
|
||||
| "1" -> print_endline "INSIDE_DUNE is from an old dune"
|
||||
| dir -> print_endline "INSIDE_DUNE is present";
|
||||
let config_path = ".dune/configurator.v2" in
|
||||
Printf.printf "%s file is %s\n" config_path
|
||||
(if Sys.file_exists (Filename.concat dir config_path) then
|
||||
"present"
|
||||
else
|
||||
"not present")
|
||||
end;
|
||||
Configurator.main ~name:"config" (fun t ->
|
||||
match Configurator.ocaml_config_var t "version" with
|
||||
| None -> failwith "version is absent"
|
||||
| Some _ -> print_endline "version is present"
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
(lang dune 1.0)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(executable
|
||||
(name run)
|
||||
(libraries dune.configurator))
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
module Configurator = Configurator.V1
|
||||
|
||||
let () =
|
||||
let module C_define = Configurator.C_define in
|
||||
Configurator.main ~name:"c_test" (fun t ->
|
||||
C_define.import t
|
||||
~prelude:"#define CONFIGURATOR_TESTING \"foobar\"\n\
|
||||
#define CONFIGURATOR_NEG_INT -127\n"
|
||||
~includes:["caml/config.h"]
|
||||
[ "CAML_CONFIG_H", C_define.Type.Switch
|
||||
; "Page_log", C_define.Type.Int
|
||||
; "CONFIGURATOR_TESTING", C_define.Type.String
|
||||
; "CONFIGURATOR_NEG_INT", C_define.Type.Int
|
||||
; "sizeof(char)", C_define.Type.Int
|
||||
]
|
||||
|> List.iter (fun (n, v) ->
|
||||
Printf.printf "%s=%s\n"
|
||||
n (match v with
|
||||
| C_define.Value.String s -> s
|
||||
| Int i -> string_of_int i
|
||||
| Switch b -> string_of_bool b
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
Show that config values are present
|
||||
$ dune exec config/run.exe
|
||||
INSIDE_DUNE is present
|
||||
.dune/configurator.v2 file is present
|
||||
version is present
|
||||
|
||||
We're able to compile C program successfully
|
||||
$ dune exec c_test/run.exe
|
||||
Successfully compiled c program
|
||||
|
||||
Importing #define's from code is successful
|
||||
$ dune exec import-define/run.exe
|
||||
CAML_CONFIG_H=true
|
||||
Page_log=12
|
||||
CONFIGURATOR_TESTING=foobar
|
||||
CONFIGURATOR_NEG_INT=-127
|
||||
sizeof(char)=1
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
(cram
|
||||
(deps
|
||||
(package dune)
|
||||
(package dune-configurator)))
|
||||
|
||||
(cram
|
||||
(applies_to pkg-config-quoting)
|
||||
(deps %{bin:pkg-config}))
|
||||
|
||||
(cram
|
||||
(enabled_if
|
||||
(<> %{ocaml-config:system} win))
|
||||
(applies_to configurator.t))
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
(*
|
||||
* OWL - OCaml Scientific and Engineering Computing
|
||||
* Copyright (c) 2016-2022 Liang Wang <liang@ocaml.xyz>
|
||||
*)
|
||||
|
||||
module Configurator = Configurator.V1
|
||||
|
||||
let header = {|
|
||||
#define TEST "test"
|
||||
|}
|
||||
|
||||
let default_cflags c =
|
||||
let test =
|
||||
let headerfile =
|
||||
let file, fd =
|
||||
Filename.open_temp_file ~mode:[ Open_wronly ] "discover" "test.h"
|
||||
in
|
||||
output_string fd header;
|
||||
close_out fd;
|
||||
file
|
||||
in
|
||||
let platform =
|
||||
assert (Sys.file_exists headerfile);
|
||||
Configurator.C_define.import c ~includes:[ headerfile ] [ ("TEST", String) ]
|
||||
in
|
||||
match List.map snd platform with
|
||||
| [ String "test" ] -> `test
|
||||
| _ -> `unknown
|
||||
in
|
||||
match test with `test -> [] | _ -> assert false
|
||||
|
||||
let () =
|
||||
let flags_file = ref "" in
|
||||
let args = ["-target", Arg.Set_string flags_file , "flags file"] in
|
||||
Configurator.main ~args ~name:"test" (fun c ->
|
||||
let libs = [] in
|
||||
let cflags = default_cflags c in
|
||||
let conf : Configurator.Pkg_config.package_conf = { cflags; libs } in
|
||||
Configurator.Flags.write_sexp !flags_file conf.cflags)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(executable
|
||||
(name configure)
|
||||
(libraries dune.configurator))
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(rule
|
||||
(targets c_flags.sexp)
|
||||
(action (run configure/configure.exe -target %{targets})))
|
||||
|
|
@ -0,0 +1 @@
|
|||
(lang dune 2.0)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Test that dune-configurator's `C_define.import` is able to include
|
||||
custom header files correctly.
|
||||
|
||||
C_define.import functions properly
|
||||
====================================================================
|
||||
$ dune build ./c_flags.sexp
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
module C = Configurator.V1
|
||||
|
||||
|
||||
let () =
|
||||
C.main ~name:"config_test" (fun t ->
|
||||
let pkg_config =
|
||||
match C.Pkg_config.get t with
|
||||
| None -> assert false
|
||||
| Some p -> p
|
||||
in
|
||||
let query package = ignore (C.Pkg_config.query pkg_config ~package) in
|
||||
query "dummy-pkg";
|
||||
)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
(executable
|
||||
(name pkgconf)
|
||||
(modules pkgconf))
|
||||
|
||||
(env
|
||||
(_
|
||||
(binaries
|
||||
(./pkgconf.exe as pkgconf))))
|
||||
|
||||
(executable
|
||||
(name config_test)
|
||||
(libraries dune.configurator)
|
||||
(modules config_test))
|
||||
|
||||
(rule
|
||||
(alias default)
|
||||
(deps %{bin:pkgconf})
|
||||
(action
|
||||
(run ./config_test.exe -verbose)))
|
||||
|
|
@ -0,0 +1 @@
|
|||
(lang dune 3.8)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
(* We'd like to use String.equal but that's OCaml >= 4.03 *)
|
||||
let not_flag x = not ("--print-errors" = x)
|
||||
|
||||
let () =
|
||||
let args = List.tl (Array.to_list Sys.argv) in
|
||||
let args = List.filter not_flag args in
|
||||
Format.printf "@[<v>%a@]@."
|
||||
(Format.pp_print_list Format.pp_print_string) args
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
$ unset PKG_CONFIG_ARGN
|
||||
$ unset PKG_CONFIG
|
||||
|
||||
These tests show that setting `PKG_CONFIG_ARGN` passes extra args to `pkg-config`
|
||||
|
||||
$ dune build 2>&1 | awk '/run:.*bin\/pkgconf/{a=1}/stderr/{a=0}a' | sed s/$(ocamlc -config | sed -n "/^target:/ {s/target: //; p; }")/\$TARGET/g
|
||||
run: $TESTCASE_ROOT/_build/default/.bin/pkgconf --personality $TARGET --print-errors dummy-pkg
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --personality
|
||||
| $TARGET
|
||||
| dummy-pkg
|
||||
run: $TESTCASE_ROOT/_build/default/.bin/pkgconf --personality $TARGET --cflags dummy-pkg
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --personality
|
||||
| $TARGET
|
||||
| --cflags
|
||||
| dummy-pkg
|
||||
run: $TESTCASE_ROOT/_build/default/.bin/pkgconf --personality $TARGET --libs dummy-pkg
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --personality
|
||||
| $TARGET
|
||||
| --libs
|
||||
| dummy-pkg
|
||||
|
||||
$ dune clean
|
||||
$ PKG_CONFIG_ARGN="--static" dune build 2>&1 | awk '/run:.*bin\/pkgconf/{a=1}/stderr/{a=0}a'
|
||||
run: $TESTCASE_ROOT/_build/default/.bin/pkgconf --static --print-errors dummy-pkg
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --static
|
||||
| dummy-pkg
|
||||
run: $TESTCASE_ROOT/_build/default/.bin/pkgconf --static --cflags dummy-pkg
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --static
|
||||
| --cflags
|
||||
| dummy-pkg
|
||||
run: $TESTCASE_ROOT/_build/default/.bin/pkgconf --static --libs dummy-pkg
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --static
|
||||
| --libs
|
||||
| dummy-pkg
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
module C = Configurator.V1
|
||||
|
||||
|
||||
let () =
|
||||
C.main ~name:"config_test" (fun t ->
|
||||
let pkg_config =
|
||||
match C.Pkg_config.get t with
|
||||
| None -> assert false
|
||||
| Some p -> p
|
||||
in
|
||||
let query package = ignore (C.Pkg_config.query pkg_config ~package) in
|
||||
query "gtk+-quartz-3.0";
|
||||
query "gtk+-quartz-3.0 >= 3.18";
|
||||
query "gtksourceview-3.0 >= 3.18"
|
||||
)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
(executable
|
||||
(name pkg_config)
|
||||
(public_name pkg-config)
|
||||
(modules pkg_config))
|
||||
|
||||
(executable
|
||||
(name config_test)
|
||||
(libraries dune.configurator)
|
||||
(modules config_test))
|
||||
|
||||
(alias
|
||||
(name default)
|
||||
(deps (package pkg-config))
|
||||
(action (run ./config_test.exe -verbose)))
|
||||
|
|
@ -0,0 +1 @@
|
|||
(lang dune 1.7)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
(* We'd like to use String.equal but that's OCaml >= 4.03 *)
|
||||
let not_flag x = not ("--print-errors" = x)
|
||||
|
||||
let () =
|
||||
let args = List.tl (Array.to_list Sys.argv) in
|
||||
let args = List.filter not_flag args in
|
||||
Format.printf "@[<v>%a@]@."
|
||||
(Format.pp_print_list Format.pp_print_string) args
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
These tests show how various pkg-config invocations get quotes (and test specifying a custom PKG_CONFIG):
|
||||
$ PKG_CONFIG=$PWD/_build/install/default/bin/pkg-config dune build 2>&1 | awk '/run:.*bin\/pkg-config/{a=1}/stderr/{a=0}a'
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --print-errors gtk+-quartz-3.0
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| gtk+-quartz-3.0
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --cflags gtk+-quartz-3.0
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --cflags
|
||||
| gtk+-quartz-3.0
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --libs gtk+-quartz-3.0
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --libs
|
||||
| gtk+-quartz-3.0
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --print-errors 'gtk+-quartz-3.0 >= 3.18'
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| gtk+-quartz-3.0 >= 3.18
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --cflags 'gtk+-quartz-3.0 >= 3.18'
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --cflags
|
||||
| gtk+-quartz-3.0 >= 3.18
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --libs 'gtk+-quartz-3.0 >= 3.18'
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --libs
|
||||
| gtk+-quartz-3.0 >= 3.18
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --print-errors 'gtksourceview-3.0 >= 3.18'
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| gtksourceview-3.0 >= 3.18
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --cflags 'gtksourceview-3.0 >= 3.18'
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --cflags
|
||||
| gtksourceview-3.0 >= 3.18
|
||||
run: $TESTCASE_ROOT/_build/install/default/bin/pkg-config --libs 'gtksourceview-3.0 >= 3.18'
|
||||
-> process exited with code 0
|
||||
-> stdout:
|
||||
| --libs
|
||||
| gtksourceview-3.0 >= 3.18
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
(test
|
||||
(name test_configurator)
|
||||
(package dune-configurator)
|
||||
(libraries configurator))
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module Configurator = Configurator.V1
|
||||
|
||||
let () = Configurator.main ~name:"test_configurator" (fun _ -> ())
|
||||
Loading…
Add table
Add a link
Reference in a new issue