package dkml-c-probe

  1. Overview
  2. Docs
Cross-compiler friendly ABI and library discovery for OCaml's native C compilers

Install

Dune Dependency

Authors

Maintainers

Sources

v3.0.0.tar.gz
md5=6cb75ec4f6d6de79904177c42e38fbe1
sha512=05e71a0605d93d5161ef9bffcc6264a205fcc89e497e726db9e622c7e61bc505f5973acbffe8c949186c26c9b05fd0b7b4e2ac4376754fb8700e428a391f1a76

doc/src/dkml-c-probe/c_conf.ml.html

Source file c_conf.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
(******************************************************************************)
(*  Copyright 2022 Diskuv, Inc.                                               *)
(*                                                                            *)
(*  Licensed under the Apache License, Version 2.0 (the "License");           *)
(*  you may not use this file except in compliance with the License.          *)
(*  You may obtain a copy of the License at                                   *)
(*                                                                            *)
(*      http://www.apache.org/licenses/LICENSE-2.0                            *)
(*                                                                            *)
(*  Unless required by applicable law or agreed to in writing, software       *)
(*  distributed under the License is distributed on an "AS IS" BASIS,         *)
(*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *)
(*  See the License for the specific language governing permissions and       *)
(*  limitations under the License.                                            *)
(******************************************************************************)

type t = {
  envname_suffixes : string list;
      (** suffixes for the environment variables, in precedence order *)
  env_getter : env_getter;
}

and env_getter = string -> string option

let default_env_getter = Sys.getenv_opt

type t_env = { cc_flags : t_cc_flag list; link_flags : t_link_flag list }

and t_link_flag = Libpath of string | Library of string

and t_cc_flag = Includepath of string

module C_flags = struct
  type t = {
    c_cc_flags : string list;
    c_link_flags : string list;
    c_link_flags_pathonly : string list;
    c_link_flags_libonly : string list;
  }

  let cc_flags { c_cc_flags; _ } = c_cc_flags

  let link_flags { c_link_flags; _ } = c_link_flags

  let link_flags_pathonly { c_link_flags_pathonly; _ } = c_link_flags_pathonly

  let link_flags_libonly { c_link_flags_libonly; _ } = c_link_flags_libonly
end

module Ocamlmklib_flags = struct
  type t = { om_lib_flags : string list }

  let lib_flags { om_lib_flags; _ } = om_lib_flags
end

let load_from_findlib_toolchain ?(getenv = default_env_getter) = function
  | None -> Ok { envname_suffixes = [ "DEFAULT" ]; env_getter = getenv }
  | Some toolchain ->
      Ok
        {
          envname_suffixes =
            [
              (* Search for ABI-specific settings first *)
              "DEFAULT_" ^ Probe_common.normalize_into_upper_alnum toolchain;
              "DEFAULT";
            ];
          env_getter = getenv;
        }

let default_dot = "default."

let l_default_dot = String.length default_dot

let load_from_dune_context_name ?(getenv = default_env_getter) ctxname =
  let l = String.length ctxname in
  let create_error () =
    Error
      ("The Dune context name '" ^ ctxname
     ^ "' is not 'default' or 'default.NAME'")
  in
  if l > l_default_dot then
    if String.sub ctxname 0 l_default_dot = default_dot then
      let after_default_dot =
        String.sub ctxname l_default_dot (l - l_default_dot)
      in
      load_from_findlib_toolchain ~getenv (Some after_default_dot)
    else create_error ()
  else if ctxname = "default" then load_from_findlib_toolchain ~getenv None
  else create_error ()

let load ?(getenv = default_env_getter) () =
  let abi_name_res = C_abi.V3.get_abi_name () in
  match abi_name_res with
  | Ok abi_name -> load_from_findlib_toolchain ~getenv (Some abi_name)
  | Error msg -> Error msg

let parse_nonempty s =
  String.split_on_char ';' s |> List.filter (fun s -> String.length s > 0)

let parse_short_opt s =
  let l = String.length s in
  if l >= 2 && s.[0] = '-' then Ok (s.[1], String.sub s 2 (l - 2))
  else Error ("The list item '" ^ s ^ "' is not a command line option")

let parse_link_flags s =
  parse_nonempty s
  |> List.fold_left
       (fun acc_res item ->
         match (acc_res, parse_short_opt item) with
         | Error msg, _ | _, Error msg -> Error msg
         | Ok acc, Ok ('L', value) -> Ok (Libpath value :: acc)
         | Ok acc, Ok ('l', value) -> Ok (Library value :: acc)
         | Ok _, Ok (c, _) -> Error ("Invalid option: -" ^ String.make 1 c))
       (Ok [])

let parse_cc_flags s =
  parse_nonempty s
  |> List.fold_left
       (fun acc_res item ->
         match (acc_res, parse_short_opt item) with
         | Error msg, _ | _, Error msg -> Error msg
         | Ok acc, Ok ('I', value) -> Ok (Includepath value :: acc)
         | Ok _, Ok (c, _) -> Error ("Invalid option: -" ^ String.make 1 c))
       (Ok [])

let parse_env_helper ~envname_suffix ~env_getter ~clibrary =
  let envname part =
    "CP_"
    ^ Probe_common.normalize_into_upper_alnum clibrary
    ^ "_" ^ part ^ "_" ^ envname_suffix
  in
  let envvalue part =
    (* Get from the environment. Treat empty = unset. *)
    match env_getter (envname part) with
    | None | Some "" -> None
    | Some v -> Some v
  in
  match (envvalue "CC", envvalue "LINK") with
  | None, None | Some _, None | None, Some _ -> Ok None
  | Some cc_str, Some link_str -> (
      match (parse_cc_flags cc_str, parse_link_flags link_str) with
      | Ok cc_flags, Ok link_flags ->
          Ok
            (Some
               {
                 cc_flags = List.rev cc_flags;
                 link_flags = List.rev link_flags;
               })
      | Error msg, _ | _, Error msg -> Error msg)

let parse_env { envname_suffixes; env_getter } ~clibrary =
  let rec search_in_suffixes = function
    | [] -> Ok None
    | envname_suffix :: remaining_suffixes -> (
        match parse_env_helper ~envname_suffix ~env_getter ~clibrary with
        | Error e -> Error e
        | Ok (Some v) -> Ok (Some v)
        | Ok None -> search_in_suffixes remaining_suffixes)
  in
  search_in_suffixes envname_suffixes

let filter_link_flags_pathonly = function Libpath _ -> true | _ -> false

let filter_link_flags_libonly = function Library _ -> true | _ -> false

let compiler_flags_msvc conf ~clibrary =
  match parse_env conf ~clibrary with
  | Error msg -> Error msg
  | Ok None -> Ok None
  | Ok (Some { cc_flags; link_flags }) ->
      let f_link_flags = function
        | Libpath p -> "-LIBPATH:" ^ p
        | Library l -> l ^ ".lib"
      in
      Ok
        (Some
           {
             C_flags.c_cc_flags =
               List.map (function Includepath p -> "-I" ^ p) cc_flags;
             c_link_flags = List.map f_link_flags link_flags;
             c_link_flags_pathonly =
               List.filter filter_link_flags_pathonly link_flags
               |> List.map f_link_flags;
             c_link_flags_libonly =
               List.filter filter_link_flags_libonly link_flags
               |> List.map f_link_flags;
           })

let compiler_flags_gcc conf ~clibrary =
  match parse_env conf ~clibrary with
  | Error msg -> Error msg
  | Ok None -> Ok None
  | Ok (Some { cc_flags; link_flags }) ->
      let f_link_flags = function
        | Libpath p -> "-L" ^ p
        | Library l -> "-l" ^ l
      in
      Ok
        (Some
           {
             C_flags.c_cc_flags =
               List.map (function Includepath p -> "-I" ^ p) cc_flags;
             c_link_flags = List.map f_link_flags link_flags;
             c_link_flags_pathonly =
               List.filter filter_link_flags_pathonly link_flags
               |> List.map f_link_flags;
             c_link_flags_libonly =
               List.filter filter_link_flags_libonly link_flags
               |> List.map f_link_flags;
           })

let compiler_flags_of_ccomp_type conf ~ccomp_type ~clibrary =
  match ccomp_type with
  | "msvc" -> compiler_flags_msvc conf ~clibrary
  | _ -> compiler_flags_gcc conf ~clibrary

let tool_flags_ocamlmklib conf ~clibrary =
  match parse_env conf ~clibrary with
  | Error msg -> Error msg
  | Ok None -> Ok None
  | Ok (Some { link_flags; _ }) ->
      Ok
        (Some
           {
             Ocamlmklib_flags.om_lib_flags =
               List.map
                 (function Libpath p -> "-L" ^ p | Library l -> "-l" ^ l)
                 link_flags;
           })
OCaml

Innovation. Community. Security.