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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
open B0_std
open B0_std.Fut.Syntax
open B00
open B00_ocaml
let () = B0_def.Scope.lib "ocaml"
let libname = Lib.Name.v
type built_code = [ `Byte | `Native | `All ]
let pp_built_code ppf c = Fmt.string ppf (match c with
| `Byte -> "byte" | `Native -> "native" | `All -> "all")
let tag = B0_meta.Key.tag "ocaml" ~doc:"OCaml related entity"
module Meta = struct
let c_requires =
let doc = "Required C libraries" in
let pp_value = Cmd.pp in
B0_meta.Key.v "c-requires" ~doc ~pp_value
let requires =
let doc = "Required OCaml libraries" in
let pp_value = Fmt.(box @@ list ~sep:sp B00_ocaml.Lib.Name.pp) in
B0_meta.Key.v "ocaml-requires" ~doc ~pp_value
let library =
let pp_value = Fmt.using B00_ocaml.Lib.Name.to_string Fmt.string in
B0_meta.Key.v "ocaml-library" ~doc:"Defined OCaml library name" ~pp_value
let mod_srcs =
let pp_value = Fmt.any "<dynamic>" in
B0_meta.Key.v "srcs" ~doc:"Module sources" ~pp_value
let supported_code =
let pp_value = pp_built_code in
B0_meta.Key.v "ocaml-supported-code" ~doc:"Supported built code" ~pp_value
let needs_code =
let pp_value = pp_built_code in
B0_meta.Key.v "ocaml-needs-code" ~doc:"Needed built code" ~pp_value
end
let needed_code s m =
let find_need u acc =
let need = B0_unit.find_meta Meta.needs_code u in
match acc, need with
| need, None | None, need -> need
| Some `Byte, Some `Byte -> acc
| Some `All, _ -> acc | _, Some `All -> acc
| Some `Byte, Some `Native -> Some `All
| Some `Native, Some `Native -> acc
| Some `Native, Some `Byte -> Some `All
in
let* b = Store.get s B0_build.self in
Fut.return (B0_unit.Set.fold find_need (B0_build.may_build b) None)
let wanted_code = Store.key (fun _ _ -> Fut.return `Auto)
let built_code =
let of_wanted_code s m = function
| #built_code as v -> Fut.return v
| `Auto ->
let* need = needed_code s m in
match need with
| Some need -> Fut.return need
| None ->
let* ocamlopt = Memo.tool_opt m Tool.ocamlopt in
Fut.return @@ if Option.is_some ocamlopt then `Native else `Byte
in
let det s m =
let* wanted = Store.get s wanted_code in
of_wanted_code s m wanted
in
Store.key det
let conf : B00_ocaml.Conf.t B00.Store.key =
let conf_comp s m =
let of_built_code = function
| `Native | `All -> Tool.ocamlopt | `Byte -> Tool.ocamlc
in
Fut.map of_built_code (Store.get s built_code)
in
let det s m =
let* comp = conf_comp s m in
let file = Fpath.(Store.dir s / Memo.mark m) in
B00_ocaml.Conf.write m ~comp ~o:file;
B00_ocaml.Conf.read m file
in
Store.key ~mark:"ocaml.conf" det
let version b = Fut.map B00_ocaml.Conf.version (B0_build.get b conf)
let lib_of_unit b ocaml_conf u =
B0_build.require b u;
let m = B0_build.memo b in
let build_dir = B0_build.build_dir b u in
let name = B0_unit.get_meta Meta.library u |> Memo.fail_if_error m in
let requires = B0_unit.get_meta Meta.requires u |> Memo.fail_if_error m in
let archive = Lib.Name.to_archive_name name in
let base = Fpath.(build_dir / archive) in
let cma = Some Fpath.(base + ".cma") in
let cmxa = Some Fpath.(base + ".cmxa") in
let c_archive = Some Fpath.(base + (B00_ocaml.Conf.lib_ext ocaml_conf)) in
let c_stubs = [] in
let* srcs = B0_unit.get_meta Meta.mod_srcs u |> Memo.fail_if_error m in
let cmis, cmxs =
let rec loop cmis cmxs = function
| [] -> cmis, cmxs
| s :: ss ->
let cmis = Mod.Src.cmi_file s :: cmis in
let cmxs = match Mod.Src.cmx_file s with
| None -> cmxs | Some cmx -> cmx :: cmxs
in
loop cmis cmxs ss
in
loop [] [] (Mod.Name.Map.fold (fun _ v acc -> v :: acc) srcs [])
in
let js_stubs = [] in
Fut.return @@
Some (Lib.v ~name ~requires ~dir:build_dir ~cmis ~cmxs ~cma ~cmxa ~c_archive
~c_stubs ~js_stubs)
let libs_in_build
b ~conf : (B0_unit.t * (Lib.t option Fut.t Lazy.t)) Lib.Name.Map.t
=
let add u acc = match B0_unit.find_meta Meta.library u with
| None -> acc
| Some lib_name ->
match Lib.Name.Map.find_opt lib_name acc with
| None ->
let lib = lazy (lib_of_unit b conf u) in
Lib.Name.Map.add lib_name (u, lib) acc
| Some (lib_u, _) ->
Memo.notify (B0_build.memo b)
`Warn "@[OCaml library %a already defined in unit %a.@,\
Ignoring definition in unit %a@]"
Lib.Name.pp lib_name B0_unit.pp_name lib_u B0_unit.pp_name u;
acc
in
B0_unit.Set.fold add (B0_build.may_build b) Lib.Name.Map.empty
let lib_resolver_build_scope b conf =
let name = "build" in
let libs_in_build = libs_in_build b ~conf in
let find ocaml_conf m n = match Lib.Name.Map.find_opt n libs_in_build with
| None -> Fut.return None
| Some (_, lazy lib) -> lib
in
let suggest ocaml_conf m n = Fut.return None in
Lib.Resolver.scope ~name ~find ~suggest
let default_lib_resolver store m =
let* b = Store.get store B0_build.self in
let* ocaml_conf = B0_build.get b conf in
let build_scope = lib_resolver_build_scope b ocaml_conf in
let* ocamlpath = Ocamlpath.get m None in
let cache_dir = Fpath.(B0_build.shared_build_dir b / "ocamlib") in
let ocamlpath = Lib.Resolver.ocamlpath ~cache_dir ~ocamlpath in
let ocamlfind = Lib.Resolver.ocamlfind ~cache_dir in
Fut.return @@
Lib.Resolver.create m ocaml_conf [build_scope; ocamlpath; ocamlfind]
let lib_resolver = Store.key ~mark:"b0.ocamlib" default_lib_resolver
let compile_c_srcs m ~conf ~comp ~opts ~build_dir ~srcs =
let obj_ext = B00_ocaml.Conf.obj_ext conf in
let rec loop os cunits hs = function
| [] -> List.rev os
| c :: cs ->
let cname = Fpath.basename ~no_ext:true c in
match String.Map.find cname cunits with
| exception Not_found ->
let o = Fpath.(build_dir / Fmt.str "%s%s" cname obj_ext) in
Compile.c_to_o m ~comp ~opts ~reads:hs ~c ~o;
loop (o :: os) (String.Map.add cname c cunits) hs cs
| f ->
B00.Memo.notify m `Warn
"@[<v>%a:@,File ignored. %s's compilation unit already defined \
by file:@,%a:@]"
Fpath.pp c cname Fpath.pp f;
loop os cunits hs cs
in
let hs = B00_fexts.(find_files (ext ".h") srcs) in
let cs = B00_fexts.(find_files (ext ".c") srcs) in
let os = loop [] String.Map.empty hs cs in
Fut.return os
let unit_code b m meta =
let* built_code = B0_build.get b built_code in
let _supported_code = B0_meta.find Meta.supported_code meta in
let _needs_code = B0_meta.find Meta.needs_code meta in
Fut.return built_code
let exe_proc set_exe_path set_mod_srcs srcs b =
let m = B0_build.memo b in
let build_dir = B0_build.current_build_dir b in
let src_root = B0_build.current_scope_dir b in
let* srcs = B0_srcs.(Fut.map by_ext @@ select b srcs) in
let* mod_srcs = Mod.Src.map_of_files m ~build_dir ~src_root ~srcs in
let meta = B0_build.current_meta b in
let requires = B0_meta.get Meta.requires meta in set_mod_srcs mod_srcs;
let* unit_code = unit_code b m meta in
let* conf = B0_build.get b conf in
let* resolver = B0_build.get b lib_resolver in
let* comp_requires = Lib.Resolver.get_list resolver requires in
let exe_name = B0_meta.get B0_meta.exe_name meta in
let exe_ext = B00_ocaml.Conf.exe_ext conf in
let opts = Cmd.(atom "-g") in
let o = Fpath.(build_dir / (exe_name ^ exe_ext)) in
set_exe_path o;
let code = match unit_code with `All | `Native -> `Native |`Byte -> `Byte in
let all_code = match unit_code with `All -> true | _ -> false in
let comp = match unit_code with
| `Native | `All -> Tool.ocamlopt | `Byte -> Tool.ocamlc
in
Compile.intfs ~and_cmti:true m ~comp ~opts ~requires:comp_requires ~mod_srcs;
Compile.impls ~and_cmt:true m ~code ~opts ~requires:comp_requires ~mod_srcs;
if all_code then begin
Compile.impls
~and_cmt:false m ~code:`Byte ~opts ~requires:comp_requires ~mod_srcs;
end;
let* c_objs = compile_c_srcs m ~conf ~comp ~opts ~build_dir ~srcs in
let mod_srcs =
Mod.Src.sort ~deps:Mod.Src.ml_deps mod_srcs
in
let* link_requires = Lib.Resolver.get_list_and_deps resolver requires in
let archive ~code lib = match code with
| `Byte -> (match Lib.cma lib with None -> [] | Some cma -> [cma])
| `Native ->
let add v l = match v with None -> l | Some v -> v :: l in
add (Lib.cmxa lib) (add (Lib.c_archive lib) [])
in
let lib_objs = List.concat_map (archive ~code) link_requires in
let cobjs = List.filter_map (Mod.Src.impl_file ~code) mod_srcs in
let opts =
let c_requires = B0_meta.get Meta.c_requires meta in
Cmd.(opts %% (Cmd.list ~slip:"-ccopt" (Cmd.to_list c_requires)))
in
Link.code m ~conf ~code ~opts ~c_objs ~cobjs:(lib_objs @ cobjs) ~o;
if all_code then begin
let o = Fpath.(build_dir / (exe_name ^ ".byte" ^ exe_ext)) in
let lib_objs = List.concat_map (archive ~code:`Byte) link_requires in
let cobjs = List.filter_map (Mod.Src.impl_file ~code:`Byte) mod_srcs in
Link.code m ~conf ~code:`Byte ~opts ~c_objs ~cobjs:(lib_objs @ cobjs) ~o
end;
Fut.return ()
let lib_proc set_mod_srcs srcs b =
let m = B0_build.memo b in
let build_dir = B0_build.current_build_dir b in
let src_root = B0_build.current_scope_dir b in
let* srcs = B0_srcs.(Fut.map by_ext @@ select b srcs) in
let* mod_srcs = Mod.Src.map_of_files m ~build_dir ~src_root ~srcs in
set_mod_srcs mod_srcs;
let meta = B0_build.current_meta b in
let requires = B0_meta.get Meta.requires meta in
let library = B0_meta.get Meta.library meta in
let archive_name = Lib.Name.to_archive_name library in
let opts = Cmd.(atom "-g") in
let* built_code = B0_build.get b built_code in
let* conf = B0_build.get b conf in
let* resolver = B0_build.get b lib_resolver in
let* requires = Lib.Resolver.get_list resolver requires in
let code = match built_code with `All | `Native -> `Native |`Byte -> `Byte in
let all_code = match built_code with `All -> true | _ -> false in
let comp = match built_code with
| `Native | `All -> Tool.ocamlopt | `Byte -> Tool.ocamlc
in
Compile.intfs ~and_cmti:true m ~comp ~opts ~requires ~mod_srcs;
Compile.impls ~and_cmt:true m ~code ~opts ~requires ~mod_srcs;
if all_code
then (Compile.impls ~and_cmt:true m ~code:`Byte ~opts ~requires ~mod_srcs);
let* c_objs = compile_c_srcs m ~conf ~comp ~opts ~build_dir ~srcs in
let mod_srcs = Mod.Src.sort ~deps:Mod.Src.ml_deps mod_srcs in
let cobjs = List.filter_map (Mod.Src.impl_file ~code) mod_srcs in
let odir = build_dir and oname = archive_name in
let has_cstubs = c_objs <> [] in
if has_cstubs then Archive.cstubs m ~conf ~opts ~c_objs ~odir ~oname;
let opts =
let c_requires = B0_meta.get Meta.c_requires meta in
Cmd.(opts %% (Cmd.list ~slip:"-ccopt" (Cmd.to_list c_requires)))
in
Archive.code m ~conf ~code ~opts ~has_cstubs ~cobjs ~odir ~oname;
if all_code then begin
let cobjs = List.filter_map (Mod.Src.impl_file ~code:`Byte) mod_srcs in
Archive.code m ~conf ~code:`Byte ~opts ~has_cstubs ~cobjs ~odir ~oname
end;
Fut.return ()
let exe
?(wrap = fun proc b -> proc b) ?doc ?(meta = B0_meta.empty) ?action
?(c_requires = Cmd.empty) ?(requires = []) ?name exe_name ~srcs
=
let name = Option.value ~default:exe_name name in
let mod_srcs, set_mod_srcs = Fut.create () in
let exe_path, set_exe_path = Fut.create () in
let meta =
meta
|> B0_meta.tag tag
|> B0_meta.tag B0_meta.exe
|> B0_meta.add B0_meta.exe_name exe_name
|> B0_meta.add Meta.c_requires c_requires
|> B0_meta.add Meta.requires requires
|> B0_meta.add Meta.mod_srcs mod_srcs
|> B0_meta.add B0_meta.exe_file exe_path
in
let action = match action with None -> B0_unit.Action.exec | Some a -> a in
let proc = wrap (exe_proc set_exe_path set_mod_srcs srcs) in
B0_unit.v ?doc ~meta ~action name proc
let lib
?(wrap = fun proc b -> proc b) ?doc ?(meta = B0_meta.empty) ?action
?(c_requires = Cmd.empty) ?(requires = []) ?name lib_name ~srcs
=
let name = match name with
| None -> Lib.Name.undot ~rep:'-' lib_name
| Some name -> name
in
let mod_srcs, set_mod_srcs = Fut.create () in
let meta =
meta
|> B0_meta.tag tag
|> B0_meta.tag B0_meta.lib
|> B0_meta.add Meta.library lib_name
|> B0_meta.add Meta.c_requires c_requires
|> B0_meta.add Meta.requires requires
|> B0_meta.add Meta.mod_srcs mod_srcs
in
let proc = wrap (lib_proc set_mod_srcs srcs) in
B0_unit.v ?doc ~meta ?action name proc
let () = B0_def.Scope.close ()