-
Notifications
You must be signed in to change notification settings - Fork 25
/
build.ml
422 lines (372 loc) · 11.2 KB
/
build.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
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
(* This file is distributed under the terms and conditions of the GNU GPL
(General Public License), as detailed in the file LICENSE.
Copyright (C) 2016 by Gerd Stolpmann.
*)
(* Bootstrap build driver for omake *)
(* You can override the commands ocaml, ocamlc, ocamlopt on the command-line,
e.g. ocaml build.ml OCAML=myocaml
*)
#warnings "-3";;
#load "str.cma";;
#load "unix.cma";;
open Printf
module StrMap = Map.Make(String)
module StrSet = Set.Make(String)
let ocaml vars =
try StrMap.find "OCAML" vars
with Not_found -> "ocaml"
let ocamlc vars =
try StrMap.find "OCAMLC" vars
with Not_found -> "ocamlc"
let ocamlopt vars =
try StrMap.find "OCAMLOPT" vars
with Not_found -> "ocamlopt"
let rec restart f arg =
try f arg
with
| Unix.Unix_error(Unix.EINTR,_,_) ->
restart f arg
let rec fold_lines f number acc fh =
match
try
let line = input_line fh in
let n = String.length line in
let line =
if n > 0 && line.[n-1] = '\r' then String.sub line 0 (n-1) else line in
Some line
with End_of_file -> None
with
| Some line ->
let acc' = f number line acc in
fold_lines f (number+1) acc' fh
| None ->
acc
let with_command cmd parse =
let ch = Unix.open_process_in cmd in
try
let data = parse ch in
let status = Unix.close_process_in ch in
match status with
| Unix.WEXITED 0 ->
data
| Unix.WEXITED n ->
failwith ("Command exited with error: " ^ cmd)
| _ ->
failwith ("Command exited with signal: " ^ cmd)
with
| Unix.Unix_error(code,_,name) ->
ignore(Unix.close_process_in ch);
let prefix =
if name = "" then "" else name ^ ": " in
raise (Sys_error(prefix ^ Unix.error_message code))
let bytecomp_c_compiler_re = Str.regexp "^bytecomp_c_compiler: \\(.*\\)$"
let ws_re = Str.regexp "[ \t]+"
let ocaml_cc vars =
try
StrMap.find "OCAML_CC" vars
with Not_found ->
with_command
"ocamlc -config"
(fold_lines
(fun i line acc ->
if Str.string_match bytecomp_c_compiler_re line 0 then
let full = Str.matched_group 1 line in
let words = Str.split ws_re full in
List.hd words
else
acc
)
1
"cc"
)
let ocaml_cflags vars =
try
StrMap.find "OCAML_CFLAGS" vars
with Not_found ->
with_command
"ocamlc -config"
(fold_lines
(fun i line acc ->
if Str.string_match bytecomp_c_compiler_re line 0 then
let full = Str.matched_group 1 line in
let words = Str.split ws_re full in
String.concat " " (List.tl words)
else
acc
)
1
""
)
let exec_command env prog args =
try
let cmd =
String.concat
" " (prog :: List.map Filename.quote (Array.to_list args)) in
printf "%s\n%!" cmd;
let env =
Array.of_list
(List.map (fun (n,v) -> n ^ "=" ^ v) env) in
let env = Array.append env (Unix.environment()) in
let args = Array.append [| prog |] args in
let pid =
Unix.create_process_env
prog args env Unix.stdin Unix.stdout Unix.stderr in
let _, st = Unix.waitpid [] pid in
match st with
| Unix.WEXITED 0 ->
()
| Unix.WEXITED n ->
failwith ("Command exited with error: " ^ cmd)
| _ ->
failwith ("Command exited with signal: " ^ cmd)
with
| Unix.Unix_error(code,_,name) ->
let prefix =
if name = "" then "" else name ^ ": " in
raise (Sys_error(prefix ^ Unix.error_message code))
let copy src dest =
(* only for small files... *)
let f_src = open_in_bin src in
let n = in_channel_length f_src in
let data = Bytes.create n in
really_input f_src data 0 n;
close_in f_src;
let f_dest = open_out_bin dest in
output f_dest data 0 n;
flush f_dest;
close_out f_dest
let copy_executable src dest =
let need_exe =
Sys.file_exists (src ^ ".exe") in
let with_exe file =
if need_exe then file ^ ".exe" else file in
let src = with_exe src in
let dest = with_exe dest in
copy src dest;
Unix.chmod dest 0o755
let touch file =
let f = open_out_gen [ Open_wronly; Open_append; Open_creat ] 0o666 file in
close_out f
let safe_mkdir file =
try Unix.mkdir file 0o777
with
| Unix.Unix_error(Unix.EEXIST,_,_) -> ()
let is_dir dir =
(* Sys.file_exists (Filename.concat dir ".") - doesn't work under Windows *)
try
let st = Unix.stat dir in
Unix.(st.st_kind = S_DIR)
with
| Unix.Unix_error _ -> false
let rec find dir pattern =
(* pattern: regexp of the files to match in this directory *)
let re = Str.regexp pattern in
let allfiles = Array.to_list (Sys.readdir dir) in
let allpaths =
List.map (Filename.concat dir) allfiles in
let files =
List.filter
(fun n ->
Str.string_match re n 0
)
allfiles in
let paths =
List.map (Filename.concat dir) files in
let subdirs =
List.filter is_dir allpaths in
let subpaths =
List.flatten
(List.map (fun dir -> find dir pattern) subdirs) in
paths @ subpaths
let safe_remove path =
try Sys.remove path
with Sys_error _ -> ()
let safe_remove_list l =
List.iter safe_remove l
let system_re = Str.regexp "^system: \\(.*\\)$"
let get_system vars =
let cmd =
sprintf "%s -config" (ocamlc vars) in
with_command
cmd
(fold_lines
(fun i line acc ->
if Str.string_match system_re line 0 then
Str.matched_group 1 line
else
acc
)
1
"unknown"
)
let have_ocamlopt vars =
let cmd = ocamlopt vars in
Sys.command cmd = 0
let configure_bootstrap vars =
safe_mkdir "boot";
copy "src/Makefile" "boot/Makefile";
touch "boot/Makefile.dep";
( match get_system vars with
| "mingw"
| "mingw64" ->
copy "mk/osconfig_mingw.mk" "boot/osconfig.mk"
| "win32"
| "win64" ->
copy "mk/osconfig_msvc.mk" "boot/osconfig.mk"
| _ ->
copy "mk/osconfig_unix.mk" "boot/osconfig.mk"
);
let ocaml_cc_val = ocaml_cc vars in
let ocaml_cflags_val = ocaml_cflags vars in
let f =
open_out_gen
[Open_wronly; Open_append; Open_binary] 0 "boot/osconfig.mk" in
fprintf f "\n\n# additions from build.ml:\n";
fprintf f "OCAML_CC = %s\n" ocaml_cc_val;
fprintf f "OCAML_CFLAGS = %s\n" ocaml_cflags_val;
close_out f
let make self makedir vars target =
let dir = Filename.dirname self in
let args =
StrMap.fold
(fun n v acc -> (n ^ "=" ^ v) :: acc)
vars
[] in
let args_a =
Array.of_list
("-I" :: "+str" :: "-I" :: "+unix" :: Filename.concat dir "make.ml" ::
"-C" :: makedir :: target :: args) in
exec_command [] (ocaml vars) args_a
let run_bootstrap self vars =
configure_bootstrap vars;
let vars =
if have_ocamlopt vars then
StrMap.add "PREFERRED" ".opt"
(StrMap.add "OCAMLSUFFIX" ".opt" vars)
else
vars in
make self "boot" vars "Makefile.dep";
make self "boot" vars "omake"
let do_bootstrap self vars ty =
match ty with
| `Force ->
run_bootstrap self vars;
("boot/omake", "src/main/prelim_omake")
| `Disable ->
("omake", "omake")
| `Auto ->
if not (Sys.file_exists "boot/omake") && not (Sys.file_exists "boot/omake.exe") then
run_bootstrap self vars;
("boot/omake", "src/main/prelim_omake")
let run_omake omake env vars args =
let args1 =
StrMap.fold
(fun n v acc -> (n ^ "=" ^ v) :: acc)
vars
[] in
exec_command env omake (Array.of_list (args @ args1))
let do_action self vars action omake1 omake2 =
let env =
[ "OMAKEFLAGS", "";
"OMAKEPATH", "lib"
] in
match action with
| `Build ->
if not (Sys.file_exists ".preconfig") then
failwith "Unconfigured. Run the configure script first!";
touch ".config";
run_omake
omake1
env
vars
[ "--dotomake"; ".omake"; "--force-dotomake"; "-j1"; "main" ];
(* Windows cannot replace running executables. Create a copy. *)
if omake1 <> omake2 then
copy_executable "src/main/omake" "src/main/prelim_omake";
run_omake
omake2
env
vars
[ "--dotomake"; ".omake"; "--force-dotomake"; "-j1"; "all" ]
| `Install ->
run_omake
omake2
env
vars
[ "--dotomake"; ".omake"; "--force-dotomake"; "install" ]
| `Clean ->
safe_remove ".config";
if Sys.file_exists "boot" then
find "boot" ".*" |> safe_remove_list;
find "." ".*\\.omc" |> safe_remove_list;
find "src" ".*\\.cmi" |> safe_remove_list;
find "src" ".*\\.cmo" |> safe_remove_list;
find "src" ".*\\.cmx" |> safe_remove_list;
find "src" ".*\\.o" |> safe_remove_list;
find "src" ".*\\.cmxa" |> safe_remove_list;
find "src" ".*\\.a" |> safe_remove_list;
find "src" ".*\\.so" |> safe_remove_list;
find "src" ".*\\.dll" |> safe_remove_list;
find "src" ".*\\.exe" |> safe_remove_list;
safe_remove "src/env/omake_ast_parse.mly";
safe_remove "src/libmojave/lm_thread_core.ml";
safe_remove "src/libmojave/lm_thread_pool.ml";
safe_remove "src/magic/omake_magic.ml";
safe_remove "src/shell/omake_shell_sys.ml";
safe_remove "src/main/omake";
safe_remove "src/main/prelim_omake";
safe_remove "src/main/osh";
safe_remove ".omakedb";
safe_remove ".omakedb.lock"
let set_re = Str.regexp "^\\([^=]+\\)=\\(.*\\)$"
let main() =
let self = Sys.argv.(0) in
let self =
if Filename.is_relative self then
Filename.concat (Sys.getcwd()) self
else
self in
let bootstrap = ref `Auto in
let action = ref `Build in
let vars = ref StrMap.empty in
let setvar name value =
vars := StrMap.add name value !vars in
Arg.parse
[ "-force-bootstrap", Arg.Unit (fun () -> bootstrap := `Force),
" do the bootstrap again";
"-no-bootstrap", Arg.Unit (fun () -> bootstrap := `Disable),
" no bootstrap, instead use the omake from $PATH";
"-auto-bootstrap", Arg.Unit (fun () -> bootstrap := `Auto),
" do the bootstrap if needed - this is the default";
"-build", Arg.Unit (fun () -> action := `Build),
" action: build omake - this is the default";
"-install", Arg.Unit (fun () -> action := `Install),
" action: install omake";
"-clean", Arg.Unit (fun () -> action := `Clean),
" action: clean up";
]
(fun s ->
if Str.string_match set_re s 0 then
let name = Str.matched_group 1 s in
let value = Str.matched_group 2 s in
setvar name value
else
raise(Arg.Bad ("Don't know what to do with: " ^ s))
)
"ocaml build.ml [options] (var=value) ...";
let boot_omake, made_omake =
if !action = `Clean then
"", ""
else
do_bootstrap self !vars !bootstrap in
do_action self !vars !action boot_omake made_omake
let () =
try main()
with
| Failure msg
| Arg.Bad msg
| Sys_error msg ->
flush stdout;
prerr_endline msg;
flush stderr;
exit 1