forked from dbuenzli/cmdliner
-
Notifications
You must be signed in to change notification settings - Fork 1
/
build.ml
executable file
·155 lines (120 loc) · 4.32 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
#!/usr/bin/env ocaml
(* Usage: ocaml build.ml [cma|cmxa|cmxs|clean] *)
let root_dir = Sys.getcwd ()
let build_dir = "_build"
let src_dir = "src"
let base_ocaml_opts =
[ "-g"; "-bin-annot";
"-safe-string"; (* Remove once we require >= 4.06 *) ]
(* Logging *)
let strf = Printf.sprintf
let err fmt = Printf.kfprintf (fun oc -> flush oc; exit 1) stderr fmt
let log fmt = Printf.kfprintf (fun oc -> flush oc) stdout fmt
(* The running joke *)
let rev_cut ~sep s = match String.rindex s sep with
| exception Not_found -> None
| i -> String.(Some (sub s 0 i, sub s (i + 1) (length s - (i + 1))))
let cuts ~sep s =
let rec loop acc = function
| "" -> acc
| s ->
match rev_cut ~sep s with
| None -> s :: acc
| Some (l, r) -> loop (r :: acc) l
in
loop [] s
(* Read, write and collect files *)
let fpath ~dir f = String.concat "" [dir; "/"; f]
let string_of_file f =
let ic = open_in_bin f in
let len = in_channel_length ic in
let buf = Bytes.create len in
really_input ic buf 0 len;
close_in ic;
Bytes.unsafe_to_string buf
let string_to_file f s =
let oc = open_out_bin f in
output_string oc s;
close_out oc
let cp src dst = string_to_file dst (string_of_file src)
let ml_srcs dir =
let add_file dir acc f = match rev_cut ~sep:'.' f with
| Some (m, e) when e = "ml" || e = "mli" -> f :: acc
| Some _ | None -> acc
in
Array.fold_left (add_file dir) [] (Sys.readdir dir)
(* Finding and running commands *)
let find_cmd cmds =
let test, null = match Sys.win32 with
| true -> "where", " NUL"
| false -> "command -v", "/dev/null"
in
let cmd c = Sys.command (strf "%s %s 1>%s 2>%s" test c null null) = 0 in
try Some (List.find cmd cmds) with Not_found -> None
let err_cmd exit cmd = err "exited with %d: %s\n" exit cmd
let quote_cmd = match Sys.win32 with
| false -> fun cmd -> cmd
| true -> fun cmd -> strf "\"%s\"" cmd
let run_cmd args =
let cmd = String.concat " " (List.map Filename.quote args) in
(* log "[EXEC] %s\n" cmd; *)
let exit = Sys.command (quote_cmd cmd) in
if exit = 0 then () else err_cmd exit cmd
let read_cmd args =
let stdout = Filename.temp_file (Filename.basename Sys.argv.(0)) "b00t" in
at_exit (fun () -> try ignore (Sys.remove stdout) with _ -> ());
let cmd = String.concat " " (List.map Filename.quote args) in
let cmd = quote_cmd @@ strf "%s 1>%s" cmd (Filename.quote stdout) in
let exit = Sys.command cmd in
if exit = 0 then string_of_file stdout else err_cmd exit cmd
(* Create and delete directories *)
let mkdir dir =
try match Sys.file_exists dir with
| true -> ()
| false -> run_cmd ["mkdir"; dir]
with
| Sys_error e -> err "%s: %s" dir e
let rmdir dir =
try match Sys.file_exists dir with
| false -> ()
| true ->
let rm f = Sys.remove (fpath ~dir f) in
Array.iter rm (Sys.readdir dir);
run_cmd ["rmdir"; dir]
with
| Sys_error e -> err "%s: %s" dir e
(* Lookup OCaml compilers and ocamldep *)
let really_find_cmd alts = match find_cmd alts with
| Some cmd -> cmd
| None -> err "No %s found in PATH\n" (List.hd @@ List.rev alts)
let ocamlc () = really_find_cmd ["ocamlc.opt"; "ocamlc"]
let ocamlopt () = really_find_cmd ["ocamlopt.opt"; "ocamlopt"]
let ocamldep () = really_find_cmd ["ocamldep.opt"; "ocamldep"]
(* Build *)
let sort_srcs srcs =
let srcs = List.sort String.compare srcs in
read_cmd (ocamldep () :: "-slash" :: "-sort" :: srcs)
|> String.trim |> cuts ~sep:' '
let common srcs = base_ocaml_opts @ sort_srcs srcs
let build_cma srcs =
run_cmd ([ocamlc ()] @ common srcs @ ["-a"; "-o"; "cmdliner.cma"])
let build_cmxa srcs =
run_cmd ([ocamlopt ()] @ common srcs @ ["-a"; "-o"; "cmdliner.cmxa"])
let build_cmxs srcs =
run_cmd ([ocamlopt ()] @ common srcs @ ["-shared"; "-o"; "cmdliner.cmxs"])
let clean () = rmdir build_dir
let in_build_dir f =
let srcs = ml_srcs src_dir in
let cp src = cp (fpath ~dir:src_dir src) (fpath ~dir:build_dir src) in
mkdir build_dir;
List.iter cp srcs;
Sys.chdir build_dir; f srcs; Sys.chdir root_dir
let main () = match Array.to_list Sys.argv with
| _ :: [ "cma" ] -> in_build_dir build_cma
| _ :: [ "cmxa" ] -> in_build_dir build_cmxa
| _ :: [ "cmxs" ] -> in_build_dir build_cmxs
| _ :: [ "clean" ] -> clean ()
| [] | [_] -> err "Missing argument: cma, cmxa, cmxs or clean\n";
| cmd :: args ->
err "%s: Unknown argument(s): %s\n" cmd @@ String.concat " " args
let () = main ()