2 * Copyright (C) 2009-2010 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 (* Please read generator/README first. *)
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
32 (* Generate the OCaml bindings interface. *)
33 let rec generate_ocaml_mli () =
34 generate_header OCamlStyle LGPLv2plus;
37 (** For API documentation you should refer to the C API
38 in the guestfs(3) manual page. The OCaml API uses almost
39 exactly the same calls. *)
42 (** A [guestfs_h] handle. *)
44 exception Error of string
45 (** This exception is raised when there is an error. *)
47 exception Handle_closed of string
48 (** This exception is raised if you use a {!Guestfs.t} handle
49 after calling {!close} on it. The string is the name of
52 val create : unit -> t
53 (** Create a {!Guestfs.t} handle. *)
56 (** Close the {!Guestfs.t} handle and free up all resources used
59 Handles are closed by the garbage collector when they become
60 unreferenced, but callers can call this in order to provide
61 predictable cleanup. *)
63 type progress_cb = int -> int -> int64 -> int64 -> unit
65 val set_progress_callback : t -> progress_cb -> unit
66 (** [set_progress_callback g f] sets [f] as the progress callback function.
67 For some long-running functions, [f] will be called repeatedly
68 during the function with progress updates.
70 The callback is [f proc_nr serial position total]. See
71 the description of [guestfs_set_progress_callback] in guestfs(3)
72 for the meaning of these four numbers.
74 Note that if the closure captures a reference to the handle,
75 this reference will prevent the handle from being
76 automatically closed by the garbage collector. There are
77 three ways to avoid this: be careful not to capture the handle
78 in the closure, or use a weak reference, or call
79 {!Guestfs.clear_progress_callback} to remove the reference. *)
81 val clear_progress_callback : t -> unit
82 (** [clear_progress_callback g] removes any progress callback function
83 associated with the handle. See {!Guestfs.set_progress_callback}. *)
86 generate_ocaml_structure_decls ();
90 fun (name, style, _, _, _, shortdesc, _) ->
91 generate_ocaml_prototype name style;
92 pr "(** %s *)\n" shortdesc;
94 ) all_functions_sorted
96 (* Generate the OCaml bindings implementation. *)
97 and generate_ocaml_ml () =
98 generate_header OCamlStyle LGPLv2plus;
103 exception Error of string
104 exception Handle_closed of string
106 external create : unit -> t = \"ocaml_guestfs_create\"
107 external close : t -> unit = \"ocaml_guestfs_close\"
109 type progress_cb = int -> int -> int64 -> int64 -> unit
111 external set_progress_callback : t -> progress_cb -> unit
112 = \"ocaml_guestfs_set_progress_callback\"
113 external clear_progress_callback : t -> unit
114 = \"ocaml_guestfs_clear_progress_callback\"
116 (* Give the exceptions names, so they can be raised from the C code. *)
118 Callback.register_exception \"ocaml_guestfs_error\" (Error \"\");
119 Callback.register_exception \"ocaml_guestfs_closed\" (Handle_closed \"\")
123 generate_ocaml_structure_decls ();
127 fun (name, style, _, _, _, shortdesc, _) ->
128 generate_ocaml_prototype ~is_external:true name style;
129 ) all_functions_sorted
131 (* Generate the OCaml bindings C implementation. *)
132 and generate_ocaml_c () =
133 generate_header CStyle LGPLv2plus;
140 #include <caml/config.h>
141 #include <caml/alloc.h>
142 #include <caml/callback.h>
143 #include <caml/fail.h>
144 #include <caml/memory.h>
145 #include <caml/mlvalues.h>
146 #include <caml/signals.h>
148 #include \"guestfs.h\"
150 #include \"guestfs_c.h\"
152 /* Copy a hashtable of string pairs into an assoc-list. We return
153 * the list in reverse order, but hashtables aren't supposed to be
156 static CAMLprim value
157 copy_table (char * const * argv)
160 CAMLlocal5 (rv, pairv, kv, vv, cons);
164 for (i = 0; argv[i] != NULL; i += 2) {
165 kv = caml_copy_string (argv[i]);
166 vv = caml_copy_string (argv[i+1]);
167 pairv = caml_alloc (2, 0);
168 Store_field (pairv, 0, kv);
169 Store_field (pairv, 1, vv);
170 cons = caml_alloc (2, 0);
171 Store_field (cons, 1, rv);
173 Store_field (cons, 0, pairv);
181 (* Struct copy functions. *)
183 let emit_ocaml_copy_list_function typ =
184 pr "static CAMLprim value\n";
185 pr "copy_%s_list (const struct guestfs_%s_list *%ss)\n" typ typ typ;
187 pr " CAMLparam0 ();\n";
188 pr " CAMLlocal2 (rv, v);\n";
189 pr " unsigned int i;\n";
191 pr " if (%ss->len == 0)\n" typ;
192 pr " CAMLreturn (Atom (0));\n";
194 pr " rv = caml_alloc (%ss->len, 0);\n" typ;
195 pr " for (i = 0; i < %ss->len; ++i) {\n" typ;
196 pr " v = copy_%s (&%ss->val[i]);\n" typ typ;
197 pr " caml_modify (&Field (rv, i), v);\n";
199 pr " CAMLreturn (rv);\n";
207 let has_optpercent_col =
208 List.exists (function (_, FOptPercent) -> true | _ -> false) cols in
210 pr "static CAMLprim value\n";
211 pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ;
213 pr " CAMLparam0 ();\n";
214 if has_optpercent_col then
215 pr " CAMLlocal3 (rv, v, v2);\n"
217 pr " CAMLlocal2 (rv, v);\n";
219 pr " rv = caml_alloc (%d, 0);\n" (List.length cols);
224 pr " v = caml_copy_string (%s->%s);\n" typ name
226 pr " v = caml_alloc_string (%s->%s_len);\n" typ name;
227 pr " memcpy (String_val (v), %s->%s, %s->%s_len);\n"
230 pr " v = caml_alloc_string (32);\n";
231 pr " memcpy (String_val (v), %s->%s, 32);\n" typ name
232 | name, (FBytes|FInt64|FUInt64) ->
233 pr " v = caml_copy_int64 (%s->%s);\n" typ name
234 | name, (FInt32|FUInt32) ->
235 pr " v = caml_copy_int32 (%s->%s);\n" typ name
236 | name, FOptPercent ->
237 pr " if (%s->%s >= 0) { /* Some %s */\n" typ name name;
238 pr " v2 = caml_copy_double (%s->%s);\n" typ name;
239 pr " v = caml_alloc (1, 0);\n";
240 pr " Store_field (v, 0, v2);\n";
241 pr " } else /* None */\n";
242 pr " v = Val_int (0);\n";
244 pr " v = Val_int (%s->%s);\n" typ name
246 pr " Store_field (rv, %d, v);\n" i
248 pr " CAMLreturn (rv);\n";
253 (* Emit a copy_TYPE_list function definition only if that function is used. *)
256 | typ, (RStructListOnly | RStructAndList) ->
257 (* generate the function for typ *)
258 emit_ocaml_copy_list_function typ
259 | typ, _ -> () (* empty *)
260 ) (rstructs_used_by all_functions);
264 fun (name, style, _, _, _, _, _) ->
265 pr "/* Automatically generated wrapper for function\n";
267 generate_ocaml_prototype name style;
272 "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
275 match fst style with RConstOptString _ -> true | _ -> false in
277 pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
278 pr "CAMLprim value ocaml_guestfs_%s (value %s" name (List.hd params);
279 List.iter (pr ", value %s") (List.tl params); pr ");\n";
282 pr "CAMLprim value\n";
283 pr "ocaml_guestfs_%s (value %s" name (List.hd params);
284 List.iter (pr ", value %s") (List.tl params);
289 | [p1; p2; p3; p4; p5] ->
290 pr " CAMLparam5 (%s);\n" (String.concat ", " params)
291 | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
292 pr " CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
293 pr " CAMLxparam%d (%s);\n"
294 (List.length rest) (String.concat ", " rest)
296 pr " CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
298 if not needs_extra_vs then
299 pr " CAMLlocal1 (rv);\n"
301 pr " CAMLlocal3 (rv, v, v2);\n";
304 pr " guestfs_h *g = Guestfs_val (gv);\n";
305 pr " if (g == NULL)\n";
306 pr " ocaml_guestfs_raise_closed (\"%s\");\n" name;
312 | Device n | Dev_or_Path n
317 (* Copy strings in case the GC moves them: RHBZ#604691 *)
318 pr " char *%s = guestfs_safe_strdup (g, String_val (%sv));\n" n n
320 pr " char *%s =\n" n;
321 pr " %sv != Val_int (0) ?" n;
322 pr " guestfs_safe_strdup (g, String_val (Field (%sv, 0))) : NULL;\n" n
324 pr " size_t %s_size = caml_string_length (%sv);\n" n n;
325 pr " char *%s = guestfs_safe_memdup (g, String_val (%sv), %s_size);\n" n n n
326 | StringList n | DeviceList n ->
327 pr " char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n
329 pr " int %s = Bool_val (%sv);\n" n n
331 pr " int %s = Int_val (%sv);\n" n n
333 pr " int64_t %s = Int64_val (%sv);\n" n n
337 | RErr -> pr " int r;\n"; "-1"
338 | RInt _ -> pr " int r;\n"; "-1"
339 | RInt64 _ -> pr " int64_t r;\n"; "-1"
340 | RBool _ -> pr " int r;\n"; "-1"
341 | RConstString _ | RConstOptString _ ->
342 pr " const char *r;\n"; "NULL"
343 | RString _ -> pr " char *r;\n"; "NULL"
348 | RStruct (_, typ) ->
349 pr " struct guestfs_%s *r;\n" typ; "NULL"
350 | RStructList (_, typ) ->
351 pr " struct guestfs_%s_list *r;\n" typ; "NULL"
358 pr " size_t size;\n";
362 pr " caml_enter_blocking_section ();\n";
363 pr " r = guestfs_%s " name;
364 generate_c_call_args ~handle:"g" style;
366 pr " caml_leave_blocking_section ();\n";
368 (* Free strings if we copied them above. *)
371 | Pathname n | Device n | Dev_or_Path n | String n | OptString n
372 | FileIn n | FileOut n | BufferIn n | Key n ->
374 | StringList n | DeviceList n ->
375 pr " ocaml_guestfs_free_strings (%s);\n" n;
376 | Bool _ | Int _ | Int64 _ -> ()
379 pr " if (r == %s)\n" error_code;
380 pr " ocaml_guestfs_raise_error (g, \"%s\");\n" name;
383 (match fst style with
384 | RErr -> pr " rv = Val_unit;\n"
385 | RInt _ -> pr " rv = Val_int (r);\n"
387 pr " rv = caml_copy_int64 (r);\n"
388 | RBool _ -> pr " rv = Val_bool (r);\n"
390 pr " rv = caml_copy_string (r);\n"
391 | RConstOptString _ ->
392 pr " if (r) { /* Some string */\n";
393 pr " v = caml_alloc (1, 0);\n";
394 pr " v2 = caml_copy_string (r);\n";
395 pr " Store_field (v, 0, v2);\n";
396 pr " } else /* None */\n";
397 pr " v = Val_int (0);\n";
399 pr " rv = caml_copy_string (r);\n";
402 pr " rv = caml_copy_string_array ((const char **) r);\n";
403 pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
405 | RStruct (_, typ) ->
406 pr " rv = copy_%s (r);\n" typ;
407 pr " guestfs_free_%s (r);\n" typ;
408 | RStructList (_, typ) ->
409 pr " rv = copy_%s_list (r);\n" typ;
410 pr " guestfs_free_%s_list (r);\n" typ;
412 pr " rv = copy_table (r);\n";
413 pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
416 pr " rv = caml_alloc_string (size);\n";
417 pr " memcpy (String_val (rv), r, size);\n";
420 pr " CAMLreturn (rv);\n";
424 if List.length params > 5 then (
425 pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
426 pr "CAMLprim value ";
427 pr "ocaml_guestfs_%s_byte (value *argv, int argn);\n" name;
428 pr "CAMLprim value\n";
429 pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
431 pr " return ocaml_guestfs_%s (argv[0]" name;
432 iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
437 ) all_functions_sorted
439 and generate_ocaml_structure_decls () =
442 pr "type %s = {\n" typ;
445 | name, FString -> pr " %s : string;\n" name
446 | name, FBuffer -> pr " %s : string;\n" name
447 | name, FUUID -> pr " %s : string;\n" name
448 | name, (FBytes|FInt64|FUInt64) -> pr " %s : int64;\n" name
449 | name, (FInt32|FUInt32) -> pr " %s : int32;\n" name
450 | name, FChar -> pr " %s : char;\n" name
451 | name, FOptPercent -> pr " %s : float option;\n" name
457 and generate_ocaml_prototype ?(is_external = false) name style =
458 if is_external then pr "external " else pr "val ";
459 pr "%s : t -> " name;
462 | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _
463 | BufferIn _ | Key _ -> pr "string -> "
464 | OptString _ -> pr "string option -> "
465 | StringList _ | DeviceList _ -> pr "string array -> "
466 | Bool _ -> pr "bool -> "
467 | Int _ -> pr "int -> "
468 | Int64 _ -> pr "int64 -> "
470 (match fst style with
471 | RErr -> pr "unit" (* all errors are turned into exceptions *)
473 | RInt64 _ -> pr "int64"
474 | RBool _ -> pr "bool"
475 | RConstString _ -> pr "string"
476 | RConstOptString _ -> pr "string option"
477 | RString _ | RBufferOut _ -> pr "string"
478 | RStringList _ -> pr "string array"
479 | RStruct (_, typ) -> pr "%s" typ
480 | RStructList (_, typ) -> pr "%s array" typ
481 | RHashtable _ -> pr "(string * string) list"
483 if is_external then (
485 if List.length (snd style) + 1 > 5 then
486 pr "\"ocaml_guestfs_%s_byte\" " name;
487 pr "\"ocaml_guestfs_%s\"" name