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
open Types
open Ezcmd.TYPES
open EzFile.OP
open EzCompat
type build_args =
{ mutable arg_switch : switch_arg option;
mutable arg_yes : bool;
mutable arg_edition : string option;
mutable arg_upgrade : bool
}
let build_args () =
let args =
{ arg_switch = None;
arg_yes = false;
arg_edition = None;
arg_upgrade = false
}
in
let specs =
[ ( [ "switch" ],
Arg.String (fun s -> args.arg_switch <- Some (Global s)),
Ezcmd.info "Use global switch SWITCH instead of creating a local switch"
);
( [ "local" ],
Arg.Unit (fun () -> args.arg_switch <- Some Local),
Ezcmd.info "Create a local switch instead of using a global switch" );
( [ "edition" ],
Arg.String (fun s -> args.arg_edition <- Some s),
Ezcmd.info "Use this OCaml edition" );
( [ "y"; "yes" ],
Arg.Unit (fun () -> args.arg_yes <- true),
Ezcmd.info "Reply yes to all questions" );
( [ "upgrade" ],
Arg.Unit (fun () -> args.arg_upgrade <- true),
Ezcmd.info "Upgrade project files from drom.toml" )
]
in
(args, specs)
let build ~args ?(setup_opam = true) ?(build_deps = true)
?(force_build_deps = false)
?(
dev_deps = false) ?(force_dev_deps = false)
?(
build = true) () =
let p, _inferred_dir = Project.get () in
let { arg_switch; arg_yes = y; arg_edition = edition; arg_upgrade } = args in
( match edition with
| None -> ()
| Some edition -> (
match VersionCompare.compare p.min_edition edition with
| 1 ->
Error.raise
"Option --edition %s should specify a version compatible with the \
project, whose min-edition is currently %s"
edition p.min_edition
| _ -> () ) );
( match arg_switch with
| None
| Some Local ->
()
| Some (Global switch) -> (
match VersionCompare.compare p.min_edition switch with
| 1 ->
Error.raise
"Option --switch %s should specify a version compatible with the \
project, whose min-edition is currently %s"
switch p.min_edition
| _ -> () ) );
( if arg_upgrade then
let create = false in
Update.update_files ~create p
else
let hashes = Hashes.load () in
if
match Hashes.get hashes "." with
| exception Not_found -> true
| old_hash ->
let files =
( match p.file with
| None -> assert false
| Some file -> file )
:: List.flatten
( List.map
(fun package ->
match package.p_file with
| None -> []
| Some file -> [ file ] )
p.packages
)
in
old_hash
<> Update.compute_config_hash
(List.map (fun file -> (file, EzFile.read_file file)) files)
then
Printf.eprintf
"Warning: 'drom.toml' changed since last update,\n\
\ you should run `drom project` to regenerate files.\n\
%!" );
EzFile.make_dir ~p:true "_drom";
let opam_filename = (Globals.drom_dir // p.package.name) ^ "-deps.opam" in
let had_switch, switch_packages =
if setup_opam then (
let had_switch =
match arg_switch with
| None -> Sys.file_exists "_opam"
| Some Local ->
(try Sys.remove "_opam" with _ -> ());
Sys.file_exists "_opam"
| Some (Global switch) ->
( match Unix.lstat "_opam" with
| exception _ -> ()
| st -> (
match st.Unix.st_kind with
| Unix.S_DIR ->
Error.raise
"You must remove the local switch `_opam` before using option \
--switch"
| Unix.S_LNK -> ()
| _ -> Error.raise "Corrupted local switch '_opam'" ) );
Opam.run ~y ~switch ?edition [ "switch"; "link" ] [ switch ];
false
in
let env_switch = Globals.opam_switch_prefix in
( match Unix.lstat "_opam" with
| exception _ -> Opam.run ~y [ "switch"; "create" ] [ "."; "--empty" ]
| st -> (
let current_switch =
match st.Unix.st_kind with
| Unix.S_LNK -> Filename.basename (Unix.readlink "_opam")
| _ -> Unix.getcwd () // "_opam"
in
if Misc.verbose 1 then
Printf.eprintf "In opam switch %s\n%!" current_switch;
match env_switch with
| None -> ()
| Some env_switch ->
let env_switch =
if Filename.basename env_switch = "_opam" then
env_switch
else
Filename.basename env_switch
in
if env_switch <> current_switch then
Printf.eprintf
"Warning: your current environment contains a different opam \
switch %S, be careful.\n\
%!"
env_switch ) );
let packages_dir = "_opam" // ".opam-switch" // "packages" in
let packages =
match Sys.readdir packages_dir with
| exception _ -> [||]
| packages -> packages
in
let map = ref StringMap.empty in
Array.iter
(fun nv ->
let n, v = EzString.cut_at nv '.' in
map := StringMap.add n v !map;
map := StringMap.add nv v !map)
packages;
(had_switch, !map)
) else
(true, StringMap.empty)
in
if setup_opam then (
let vscode_dir = ".vscode" in
let vscode_file = vscode_dir // "settings.json" in
if not (Sys.file_exists vscode_file) then (
EzFile.make_dir ~p:true vscode_dir;
EzFile.write_file vscode_file
(Printf.sprintf
{|
{
"ocaml.sandbox": {
"kind": "opam"
"switch": "%s"
}
}
|}
(Sys.getcwd ()))
);
match StringMap.find "ocaml" switch_packages with
| exception Not_found ->
let ocaml_nv =
"ocaml."
^
match edition with
| None -> p.edition
| Some edition -> edition
in
Opam.run ~y [ "install" ] [ ocaml_nv ];
Opam.run [ "switch"; "set-base" ] [ "ocaml" ]
| v -> (
match edition with
| Some edition ->
if edition = v then
Error.raise
"Switch edition %s is not compatible with option --edition %s. You \
should remove the switch first."
v edition
| None -> (
match VersionCompare.compare p.min_edition v with
| 1 ->
Error.raise
"Wrong ocaml version %S in _opam. Expecting %S. You may want to \
remove _opam, or change the project min-edition field."
v p.min_edition
| _ -> () ) )
);
let deps_package = Misc.deps_package p in
EzFile.write_file opam_filename (Opam.opam_of_project Deps deps_package);
let drom_opam_filename = "_drom/opam.current" in
let drom_opam_deps = "_drom/opam.deps" in
let former_opam_file =
if Sys.file_exists drom_opam_filename then
Some (EzFile.read_file drom_opam_filename)
else
None
in
let new_opam_file = EzFile.read_file opam_filename in
let has_dev_deps =
try EzFile.read_file drom_opam_deps = "dev-deps" with _ -> false
in
let need_update =
force_build_deps || force_dev_deps
|| (build_deps || dev_deps)
&& (former_opam_file <> Some new_opam_file || not had_switch)
|| (dev_deps && not has_dev_deps)
in
let need_dev_deps =
dev_deps || force_dev_deps || (has_dev_deps && not force_build_deps)
in
Git.update_submodules ();
if need_update then (
let tmp_opam_filename = "_drom/new.opam" in
EzFile.write_file tmp_opam_filename new_opam_file;
let vendor_packages = Misc.vendor_packages () in
Opam.run ~y [ "install" ]
( [ "--deps-only"; "." // tmp_opam_filename ]
@ ( if need_dev_deps then
[ "--with-doc"; "--with-test" ]
else
[] )
@ vendor_packages );
(try Sys.remove drom_opam_filename with _ -> ());
Sys.rename tmp_opam_filename drom_opam_filename;
EzFile.write_file drom_opam_deps
( if need_dev_deps then
"dev-deps"
else
"build-deps" )
);
if build then
Opam.run [ "exec" ]
( [ "--"; "dune"; "build"; "@install" ]
@
match p.profile with
| None -> []
| Some profile -> [ "--profile"; profile ] );
p