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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
open EzToml.TYPES
open Types
open EzCompat
open Ez_file.V1
open EzFile.OP
let create ~name ~dir ~kind = { Globals.dummy_package with name; dir; kind }
let kind_encoding =
EzToml.enum_encoding
~to_string:(function
| Virtual -> "virtual"
| Library -> "library"
| Program -> "program" )
~of_string:(fun ~key:_ s ->
match s with
| "lib"
| "library" ->
Library
| "program"
| "executable"
| "exe" ->
Program
| "meta"
| "virtual" ->
Virtual
| kind ->
Error.raise {|unknown kind %S (should be "library" or "program")|} kind
)
let string_of_versions versions =
String.concat " "
(List.map
(function
| Version -> "version"
| NoVersion -> ""
| Semantic (major, minor, fix) ->
Printf.sprintf "%d.%d.%d" major minor fix
| Lt version -> Printf.sprintf "<%s" version
| Le version -> Printf.sprintf "<=%s" version
| Eq version -> Printf.sprintf "=%s" version
| Ge version -> Printf.sprintf ">=%s" version
| Gt version -> Printf.sprintf ">%s" version )
versions )
let versions_of_string versions =
List.map
(fun version ->
match Misc.semantic_version version with
| Some (major, minor, fix) -> Semantic (major, minor, fix)
| None -> (
if version = "" then
NoVersion
else if version = "version" then
Version
else
let len = String.length version in
match version.[0] with
| '=' -> Eq (String.sub version 1 (len - 1))
| '<' ->
if len > 1 && version.[1] = '=' then
Le (String.sub version 2 (len - 2))
else
Lt (String.sub version 1 (len - 1))
| '>' ->
if len > 1 && version.[1] = '=' then
Ge (String.sub version 2 (len - 2))
else
Gt (String.sub version 1 (len - 1))
| _ -> Ge version ) )
(EzString.split_simplify versions ' ')
let skip_encoding =
let to_toml list =
match list with
| [] -> TArray NodeEmpty
| list -> TArray (NodeString list)
in
let of_toml ~key value =
match value with
| TArray NodeEmpty -> []
| TArray (NodeString v) -> v
| TString s -> EzString.split s ' '
| _ -> EzToml.failwith "Wrong type for field %S" (EzToml.key2str key)
in
EzToml.encoding ~to_toml ~of_toml
let dependency_encoding =
EzToml.encoding
~to_toml:(fun d ->
let version = TString (string_of_versions d.depversions) in
if
{ d with depversions = [] }
= { depversions = [];
depname = None;
deptest = false;
depdoc = false;
depopt = false
}
then
version
else
let table = EzToml.empty in
let table = EzToml.put_string_option [ "libname" ] d.depname table in
let table =
match d.depversions with
| [] -> table
| _ -> EzToml.put [ "version" ] version table
in
let table =
if d.deptest then
EzToml.put [ "for-test" ] (TBool true) table
else
table
in
let table =
if d.depdoc then
EzToml.put [ "for-doc" ] (TBool true) table
else
table
in
let table =
if d.depopt then
EzToml.put [ "opt" ] (TBool true) table
else
table
in
TTable table )
~of_toml:(fun ~key v ->
match v with
| TString s ->
let depversions = versions_of_string s in
{ depname = None;
depversions;
depdoc = false;
deptest = false;
depopt = false
}
| TTable table ->
let depname = EzToml.get_string_option table [ "libname" ] in
let depversions = EzToml.get_string_default table [ "version" ] "" in
let depversions = versions_of_string depversions in
let deptest = EzToml.get_bool_default table [ "for-test" ] false in
let depdoc = EzToml.get_bool_default table [ "for-doc" ] false in
let depopt = EzToml.get_bool_default table [ "opt" ] false in
{ depname; depversions; depdoc; deptest; depopt }
| _ -> Error.raise "Bad dependency version for %s" (EzToml.key2str key) )
let dependencies_encoding =
EzToml.encoding
~to_toml:(fun deps ->
let table =
List.fold_left
(fun table (name, d) ->
EzToml.put_encoding dependency_encoding [ name ] d table )
EzToml.empty deps
in
TTable table )
~of_toml:(fun ~key v ->
let deps = EzToml.expect_table ~key ~name:"dependency list" v in
let dependencies = ref [] in
Table.iter
(fun name _version ->
let name = Table.Key.to_string name in
let d = EzToml.get_encoding dependency_encoding deps [ name ] in
dependencies := (name, d) :: !dependencies )
deps;
!dependencies )
let stringSet_encoding =
EzToml.encoding
~to_toml:(fun set -> TArray (NodeString (StringSet.to_list set)))
~of_toml:(fun ~key v ->
match v with
| TArray NodeEmpty -> StringSet.empty
| TArray (NodeString list) -> StringSet.of_list list
| _ -> EzToml.expecting_type "string list" key )
let fields_encoding = EzToml.ENCODING.stringMap EzToml.string_encoding
let to_string pk =
EzToml.CONST.(
s_
[ option "name" ~comment:[ "name of package" ] (string pk.name);
option "skeleton" (string_option pk.p_skeleton);
option "version"
~comment:[ "version if different from project version" ]
~default:{|version = "0.1.0"|}
(string_option pk.p_version);
option "synopsis"
~comment:[ "synopsis if different from project synopsis" ]
(string_option pk.p_synopsis);
option "description"
~comment:[ "description if different from project description" ]
(string_option pk.p_description);
option "kind"
~comment:[ {|kind is either "library", "program" or "virtual"|} ]
(encoding kind_encoding pk.kind);
option "authors"
~comment:[ "authors if different from project authors" ]
~default:{|authors = [ "Me <me@metoo.org>" ]|}
(string_list_option pk.p_authors);
option "gen-version"
~comment:[ "name of a file to generate with the current version" ]
~default:{|gen-version = "version.ml"|}
(string_option pk.p_gen_version);
option "generators"
~comment:
[ {|supported file generators are "ocamllex", "ocamlyacc" and "menhir" |};
{| default is [ "ocamllex", "ocamlyacc" ] |}
]
~default:{|generators = [ "ocamllex", "menhir" ]|}
(encoding_option stringSet_encoding pk.p_generators);
option "pack-modules"
~comment:
[ "whether all modules should be packed/wrapped (default is true)" ]
~default:{|pack-modules = false|}
(bool_option pk.p_pack_modules);
option "optional"
~comment:
[ "whether the package can be silently skipped if missing deps \
(default is false)"
]
~default:{|optional = true|}
(bool_option pk.p_optional);
option "pack"
~comment:
[ "module name used to pack modules (if pack-modules is true)" ]
~default:{|pack = "Mylib"|} (string_option pk.p_pack);
option "preprocess"
~comment:
[ "preprocessing options";
{| preprocess = "per-module (((action (run ./toto.sh %{input-file})) mod))" |}
]
~default:{|preprocess = "pps ppx_deriving_encoding"|}
(string_option pk.p_preprocess);
option "skip"
~comment:[ "files to skip while updating at package level" ]
~default:{|skip = []|}
(string_list_option pk.p_skip);
option "dependencies"
~comment:
[ "package library dependencies";
" [dependencies]";
{| ez_file = ">=0.1 <1.3"|};
{| base-unix = { libname = "unix", version = ">=base" } |}
]
(encoding dependencies_encoding pk.p_dependencies);
option "tools"
~comment:[ "package tools dependencies" ]
(encoding dependencies_encoding pk.p_tools);
option "fields"
~comment:
[ "package fields (depends on package skeleton)";
"Examples:";
{| dune-stanzas = "(preprocess (pps ppx_deriving_encoding))" |};
{| dune-libraries = "bigstring" |};
{| dune-trailer = "(install (..))" |};
{| opam-trailer = "pin-depends: [..]" |};
{| no-opam-test = "yes" |};
{| no-opam-doc = "yes" |};
{| gen-opam = "some" | "all" |}
]
(encoding fields_encoding pk.p_fields)
] )
let find ?default name =
let defaults =
match default with
| None -> []
| Some p -> p.packages
in
let rec iter defaults =
match defaults with
| [] -> { Globals.dummy_package with name; dir = "src" // name }
| package :: defaults ->
if package.name = name then
package
else
iter defaults
in
iter defaults
let of_toml ?default ?p_file table =
let dir = EzToml.get_string_option table [ "dir" ] in
let name =
try EzToml.get_string table [ "name" ] with
| Not_found ->
( match dir with
| Some dir_path ->
let package_path = dir_path // "package.toml" in
if Sys.file_exists package_path then
Printf.eprintf "Error: Missing field 'name' in %s\n%!" package_path
else
Printf.eprintf "Error: %s is missing \n%!" package_path
| None ->
Printf.eprintf
"Error: Missing field 'name' for a package in drom.toml\n%!" );
exit 2
in
let default = find ?default name in
let dir = Misc.option_value dir ~default:default.dir in
let kind =
EzToml.get_encoding_default kind_encoding table [ "kind" ] default.kind
in
let project = Globals.dummy_project in
let p_pack =
EzToml.get_string_option table [ "pack" ] ?default:default.p_pack
in
let p_preprocess =
EzToml.get_string_option table [ "preprocess" ] ?default:default.p_pack
in
let p_version =
EzToml.get_string_option table [ "version" ] ?default:default.p_version
in
let p_authors =
EzToml.get_string_list_option table [ "authors" ] ?default:default.p_authors
in
let p_synopsis =
EzToml.get_string_option table [ "synopsis" ] ?default:default.p_synopsis
in
let p_description =
EzToml.get_string_option table [ "description" ]
?default:default.p_description
in
let p_pack_modules =
EzToml.get_bool_option table [ "pack-modules" ]
?default:default.p_pack_modules
in
let p_optional =
EzToml.get_bool_option table [ "optional" ] ?default:default.p_optional
in
let p_dependencies =
EzToml.get_encoding_default dependencies_encoding table [ "dependencies" ]
default.p_dependencies
in
let p_tools =
EzToml.get_encoding_default dependencies_encoding table [ "tools" ]
default.p_tools
in
let p_gen_version =
EzToml.get_string_option table [ "gen-version" ]
?default:default.p_gen_version
in
let p_skeleton =
EzToml.get_string_option table [ "skeleton" ] ?default:default.p_skeleton
in
let p_generators =
EzToml.get_encoding_option stringSet_encoding table [ "generators" ]
?default:default.p_generators
in
let p_fields =
EzToml.get_encoding_default fields_encoding table [ "fields" ]
StringMap.empty
in
let p_fields =
StringMap.union (fun _k a1 _a2 -> Some a1) p_fields default.p_fields
in
if Globals.verbose_subst then
StringMap.iter
(fun k _ -> Printf.eprintf "Package defined field %S\n%!" k)
p_fields;
let p_skip =
EzToml.get_encoding_option skip_encoding table [ "skip" ]
?default:default.p_skip
in
{ name;
dir;
project;
p_pack;
p_preprocess;
p_file;
kind;
p_version;
p_authors;
p_synopsis;
p_description;
p_dependencies;
p_tools;
p_pack_modules;
p_gen_version;
p_fields;
p_skeleton;
p_generators;
p_skip;
p_optional
}
let of_toml ?default table =
let dir = EzToml.get_string_option table [ "dir" ] in
let table, p_file =
match dir with
| None -> (table, None)
| Some dir ->
let filename = dir // "package.toml" in
if Sys.file_exists filename then
let package_table =
match EzToml.from_file filename with
| `Ok table -> table
| `Error (s, loc) ->
Error.raise "Could not parse %S: %s at %s" filename s
(EzToml.string_of_location loc)
in
let table =
Toml.Types.Table.union
(fun _key _ v ->
Some v
)
package_table table
in
(table, Some filename)
else
(table, None)
in
of_toml ?default ?p_file table
let of_string ~msg ?default content =
let table =
match EzToml.from_string content with
| `Ok table -> table
| `Error (s, loc) ->
Error.raise "Could not parse:\n<<<\n%s\n>>>\n at %s" content msg s
(EzToml.string_of_location loc)
in
of_toml ?default table