Source file quill_top_unix.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
let open_temp_file prefix suffix =
let filename = Filename.temp_file prefix suffix in
let fd = Unix.openfile filename Unix.[ O_WRONLY; O_CREAT; O_TRUNC ] 0o600 in
(fd, filename)
let read_all_file filename =
try
let ic = open_in filename in
let len = in_channel_length ic in
let buf = Buffer.create len in
Buffer.add_channel buf ic len;
close_in ic;
Buffer.contents buf
with _ -> ""
let capture_separated f =
let stdout_backup = Unix.dup ~cloexec:true Unix.stdout in
let stderr_backup = Unix.dup ~cloexec:true Unix.stderr in
let fd_out, fname_out = open_temp_file "quill-out-" ".tmp" in
let fd_err, fname_err = open_temp_file "quill-err-" ".tmp" in
let ppf_out =
Format.formatter_of_out_channel (Unix.out_channel_of_descr fd_out)
in
let ppf_err =
Format.formatter_of_out_channel (Unix.out_channel_of_descr fd_err)
in
let result = ref None in
Fun.protect
(fun () ->
flush stdout;
flush stderr;
Unix.dup2 ~cloexec:false fd_out Unix.stdout;
Unix.dup2 ~cloexec:false fd_err Unix.stderr;
result := Some (f ppf_out ppf_err))
~finally:(fun () ->
Format.pp_print_flush ppf_out ();
Format.pp_print_flush ppf_err ();
flush stdout;
flush stderr;
Unix.close fd_out;
Unix.close fd_err;
Unix.dup2 ~cloexec:false stdout_backup Unix.stdout;
Unix.dup2 ~cloexec:false stderr_backup Unix.stderr;
Unix.close stdout_backup;
Unix.close stderr_backup);
let captured_output = read_all_file fname_out in
let captured_error = read_all_file fname_err in
(try Sys.remove fname_out with _ -> ());
(try Sys.remove fname_err with _ -> ());
match !result with
| None -> failwith "Capture logic failed unexpectedly"
| Some success_status ->
if success_status then
{
Quill_top.output = captured_output;
error = (if captured_error = "" then None else Some captured_error);
status = `Success;
}
else
let combined_error =
let parts =
List.filter (fun s -> s <> "") [ captured_output; captured_error ]
in
String.concat "\n" parts
in
{ Quill_top.output = ""; error = Some combined_error; status = `Error }
let initialized = ref false
let initialization_mutex = Mutex.create ()
let format_error_to_string prefix formatter_fn =
let buf = Buffer.create 512 in
let fmt = Format.formatter_of_buffer buf in
Format.fprintf fmt "%s@.@." prefix;
formatter_fn fmt;
Format.pp_print_flush fmt ();
Buffer.contents buf
let execute_directive directive =
try
let lexbuf = Lexing.from_string directive in
let phrases = !Toploop.parse_use_file lexbuf in
List.iter
(fun phrase ->
let result = Toploop.execute_phrase true Format.err_formatter phrase in
if not result then
Printf.eprintf "Failed to execute directive: %s\n%!" directive)
phrases
with ex ->
Printf.eprintf "Exception executing directive '%s': %s\n%!" directive
(Printexc.to_string ex);
raise ex
let load_plugins () =
try
let plugins_locations = Quill_sites.Sites.toplevel_libs in
let lookup_file filename =
List.find_map
(fun dir ->
let filename' = Filename.concat dir filename in
if Sys.file_exists filename' then Some filename' else None)
plugins_locations
in
let cmas =
let stdlib_cmas = [ "unix.cma" ] in
let project_cmas =
[
"nx_core.cma";
"nx_native.cma";
"nx.cma";
"nx_c.cma";
"bigarray_compat.cma";
"integers.cma";
"ctypes.cma";
"ctypes_foreign.cma";
"objc_c.cma";
"objc.cma";
"metal.cma";
"nx_metal.cma";
"zip.cma";
"npy.cma";
"stb_image.cma";
"stb_image_write.cma";
"nx_io.cma";
"curl.cma";
"csv.cma";
"nx_datasets.cma";
"re.cma";
"uutf.cma";
"uucp.cma";
"nx_text.cma";
"cairo.cma";
"usdl.cma";
"base64.cma";
"logs.cma";
"hugin.cma";
"rune_jit.cma";
"rune_jit_metal.cma";
"rune_metal.cma";
"rune.cma";
"sowilo.cma";
"kaun.cma";
"kaun_datasets.cma";
]
in
let all_cmas =
stdlib_cmas
@ List.filter_map
(fun name ->
match lookup_file name with
| Some path -> Some path
| None ->
Printf.eprintf "Warning: %s not found\n%!" name;
None)
project_cmas
in
Printf.eprintf "Found %d CMA files to load\n%!" (List.length all_cmas);
all_cmas
in
if plugins_locations = [] then (
let error_msg =
"No site directories found for 'quill.toplevel_libs'. Check \
installation."
in
Printf.eprintf "ERROR: %s\n%!" error_msg;
failwith error_msg)
else (
Printf.eprintf "Plugin locations: %s\n%!"
(String.concat ", " plugins_locations);
List.iter Topdirs.dir_directory plugins_locations;
List.iter
(fun dir ->
let kaun_dir = Filename.concat (Filename.dirname dir) "kaun" in
if Sys.file_exists kaun_dir then
let datasets_dir = Filename.concat kaun_dir "datasets" in
if Sys.file_exists datasets_dir then
Topdirs.dir_directory datasets_dir)
plugins_locations;
List.iter
(fun cma -> execute_directive (Printf.sprintf "#load %S;;" cma))
cmas;
execute_directive {|
let pp_nx fmt arr =
Nx.pp_data fmt arr;;
|};
execute_directive "#install_printer pp_nx;;";
execute_directive {|
let pp_rune fmt arr =
Rune.pp_data fmt arr;;
|};
execute_directive "#install_printer pp_rune;;";
execute_directive
{|
let pp_hugin_figure fmt figure =
let image_data = Hugin.render figure in
let base64_data = Base64.encode_string image_data in
Format.fprintf fmt "" base64_data;;
|};
execute_directive "#install_printer pp_hugin_figure;;";
execute_directive "();;";
let () =
try
execute_directive
{|
(* Set up a simple Logs reporter without ANSI codes *)
let setup_logs () =
let report src level ~over k msgf =
let k _ = over (); k () in
msgf @@ fun ?header:_ ?tags:_ fmt ->
Format.kfprintf k Format.std_formatter ("[%s] %s: " ^^ fmt ^^ "@.")
(Logs.Src.name src)
(Logs.level_to_string (Some level))
in
{ Logs.report }
;;
Logs.set_reporter (setup_logs ());;
|}
with _ -> Printf.eprintf "Warning: Could not set up Logs reporter\n%!"
in
())
with
| Env.Error e ->
failwith
(format_error_to_string "Plugin loading failed with environment error:"
(fun fmt -> Env.report_error fmt e))
| Typecore.Error (loc, env, err) ->
failwith
(format_error_to_string "Plugin loading failed with type error:"
(fun fmt ->
let report = Typecore.report_error ~loc env err in
Location.print_report fmt report))
| ex ->
Printf.eprintf "Error during plugin loading: %s\n%!"
(Printexc.to_string ex);
raise ex
let initialize_if_needed () =
Mutex.lock initialization_mutex;
Fun.protect
~finally:(fun () -> Mutex.unlock initialization_mutex)
(fun () ->
if not !initialized then (
Quill_top.initialize_toplevel ();
load_plugins ();
initialized := true))
let eval ?(print_all = true) code : Quill_top.execution_result =
try
initialize_if_needed ();
capture_separated (fun ppf_out ppf_err ->
Quill_top.execute print_all ppf_out ppf_err code)
with
| Failure msg when String.starts_with ~prefix:"Plugin loading failed" msg ->
{ Quill_top.output = ""; error = Some msg; status = `Error }
| Env.Error e ->
let error_msg =
format_error_to_string
"Toplevel initialization failed: Environment error" (fun fmt ->
Env.report_error fmt e)
in
{ Quill_top.output = ""; error = Some error_msg; status = `Error }
| Typecore.Error (loc, env, err) ->
let error_msg =
format_error_to_string "Toplevel initialization failed: Type error"
(fun fmt ->
let report = Typecore.report_error ~loc env err in
Location.print_report fmt report)
in
{ Quill_top.output = ""; error = Some error_msg; status = `Error }
| ex ->
let error_msg =
Printf.sprintf "Toplevel initialization failed: %s\nBacktrace:\n%s"
(Printexc.to_string ex)
(Printexc.get_backtrace ())
in
{ Quill_top.output = ""; error = Some error_msg; status = `Error }