2 * Copyright (C) 2009-2011 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
33 (* Generate the OCaml bindings interface. *)
34 let rec generate_ocaml_mli () =
35 generate_header OCamlStyle LGPLv2plus;
38 (** For API documentation you should refer to the C API
39 in the guestfs(3) manual page. The OCaml API uses almost
40 exactly the same calls. *)
43 (** A [guestfs_h] handle. *)
45 exception Error of string
46 (** This exception is raised when there is an error. *)
48 exception Handle_closed of string
49 (** This exception is raised if you use a {!Guestfs.t} handle
50 after calling {!close} on it. The string is the name of
53 val create : unit -> t
54 (** Create a {!Guestfs.t} handle. *)
57 (** Close the {!Guestfs.t} handle and free up all resources used
60 Handles are closed by the garbage collector when they become
61 unreferenced, but callers can call this in order to provide
62 predictable cleanup. *)
68 pr " | EVENT_%s\n" (String.uppercase name)
73 val event_all : event list
74 (** A list containing all event types. *)
77 (** The opaque event handle which can be used to delete event callbacks. *)
80 t -> event -> event_handle -> string -> int64 array -> unit
81 (** The event callback. *)
83 val set_event_callback : t -> event_callback -> event list -> event_handle
84 (** [set_event_callback g f es] sets [f] as the event callback function
85 for all events in the set [es].
87 Note that if the closure captures a reference to the handle,
88 this reference will prevent the handle from being
89 automatically closed by the garbage collector. Since the
90 handle is passed to the event callback, with careful programming
91 it should be possible to avoid capturing the handle in the closure. *)
93 val delete_event_callback : t -> event_handle -> unit
94 (** [delete_event_callback g eh] removes a previously registered
95 event callback. See {!set_event_callback}. *)
98 generate_ocaml_structure_decls ();
102 fun (name, style, _, _, _, shortdesc, _) ->
103 generate_ocaml_prototype name style;
104 pr "(** %s *)\n" shortdesc;
106 ) all_functions_sorted;
109 (** {2 Object-oriented API}
111 This is an alternate way of calling the API using an object-oriented
112 style, so you can use [g#add_drive_opts filename] instead of
113 [Guestfs.add_drive_opts g filename]. Apart from the different style,
114 it offers exactly the same functionality.
116 Calling [new guestfs ()] creates both the object and the handle.
117 The object and handle are closed either implicitly when the
118 object is garbage collected, or explicitly by calling the [g#close ()]
121 You can get the {!Guestfs.t} handle by calling [g#ocaml_handle].
123 Note that methods that take no parameters (except the implicit handle)
124 get an extra unit [()] parameter. This is so you can create a
125 closure from the method easily. For example [g#get_verbose ()]
126 calls the method, whereas [g#get_verbose] is a function. *)
128 class guestfs : unit -> object
129 method close : unit -> unit
130 method set_event_callback : event_callback -> event list -> event_handle
131 method delete_event_callback : event_handle -> unit
132 method ocaml_handle : t
137 | name, ((_, [], []) as style), _, _, _, _, _ ->
138 pr " method %s : unit -> " name;
139 generate_ocaml_function_type style;
141 | name, style, _, _, _, _, _ ->
142 pr " method %s : " name;
143 generate_ocaml_function_type style;
145 ) all_functions_sorted;
149 (* Generate the OCaml bindings implementation. *)
150 and generate_ocaml_ml () =
151 generate_header OCamlStyle LGPLv2plus;
156 exception Error of string
157 exception Handle_closed of string
159 external create : unit -> t = \"ocaml_guestfs_create\"
160 external close : t -> unit = \"ocaml_guestfs_close\"
166 pr " | EVENT_%s\n" (String.uppercase name)
175 pr " EVENT_%s;\n" (String.uppercase name)
181 type event_handle = int
183 type event_callback =
184 t -> event -> event_handle -> string -> int64 array -> unit
186 external set_event_callback : t -> event_callback -> event list -> event_handle
187 = \"ocaml_guestfs_set_event_callback\"
188 external delete_event_callback : t -> event_handle -> unit
189 = \"ocaml_guestfs_delete_event_callback\"
191 (* Give the exceptions names, so they can be raised from the C code. *)
193 Callback.register_exception \"ocaml_guestfs_error\" (Error \"\");
194 Callback.register_exception \"ocaml_guestfs_closed\" (Handle_closed \"\")
198 generate_ocaml_structure_decls ();
202 fun (name, style, _, _, _, shortdesc, _) ->
203 generate_ocaml_prototype ~is_external:true name style;
204 ) all_functions_sorted;
211 method close () = close g
212 method set_event_callback = set_event_callback g
213 method delete_event_callback = delete_event_callback g
214 method ocaml_handle = g
219 | name, (_, [], []), _, _, _, _, _ -> (* no params? add explicit unit *)
220 pr " method %s () = %s g\n" name name
221 | name, _, _, _, _, _, _ ->
222 pr " method %s = %s g\n" name name
223 ) all_functions_sorted;
227 (* Generate the OCaml bindings C implementation. *)
228 and generate_ocaml_c () =
229 generate_header CStyle LGPLv2plus;
237 #include <caml/config.h>
238 #include <caml/alloc.h>
239 #include <caml/callback.h>
240 #include <caml/fail.h>
241 #include <caml/memory.h>
242 #include <caml/mlvalues.h>
243 #include <caml/signals.h>
245 #include \"guestfs.h\"
247 #include \"guestfs_c.h\"
249 /* Copy a hashtable of string pairs into an assoc-list. We return
250 * the list in reverse order, but hashtables aren't supposed to be
253 static CAMLprim value
254 copy_table (char * const * argv)
257 CAMLlocal5 (rv, pairv, kv, vv, cons);
261 for (i = 0; argv[i] != NULL; i += 2) {
262 kv = caml_copy_string (argv[i]);
263 vv = caml_copy_string (argv[i+1]);
264 pairv = caml_alloc (2, 0);
265 Store_field (pairv, 0, kv);
266 Store_field (pairv, 1, vv);
267 cons = caml_alloc (2, 0);
268 Store_field (cons, 1, rv);
270 Store_field (cons, 0, pairv);
278 (* Struct copy functions. *)
280 let emit_ocaml_copy_list_function typ =
281 pr "static CAMLprim value\n";
282 pr "copy_%s_list (const struct guestfs_%s_list *%ss)\n" typ typ typ;
284 pr " CAMLparam0 ();\n";
285 pr " CAMLlocal2 (rv, v);\n";
286 pr " unsigned int i;\n";
288 pr " if (%ss->len == 0)\n" typ;
289 pr " CAMLreturn (Atom (0));\n";
291 pr " rv = caml_alloc (%ss->len, 0);\n" typ;
292 pr " for (i = 0; i < %ss->len; ++i) {\n" typ;
293 pr " v = copy_%s (&%ss->val[i]);\n" typ typ;
294 pr " caml_modify (&Field (rv, i), v);\n";
296 pr " CAMLreturn (rv);\n";
304 let has_optpercent_col =
305 List.exists (function (_, FOptPercent) -> true | _ -> false) cols in
307 pr "static CAMLprim value\n";
308 pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ;
310 pr " CAMLparam0 ();\n";
311 if has_optpercent_col then
312 pr " CAMLlocal3 (rv, v, v2);\n"
314 pr " CAMLlocal2 (rv, v);\n";
316 pr " rv = caml_alloc (%d, 0);\n" (List.length cols);
321 pr " v = caml_copy_string (%s->%s);\n" typ name
323 pr " v = caml_alloc_string (%s->%s_len);\n" typ name;
324 pr " memcpy (String_val (v), %s->%s, %s->%s_len);\n"
327 pr " v = caml_alloc_string (32);\n";
328 pr " memcpy (String_val (v), %s->%s, 32);\n" typ name
329 | name, (FBytes|FInt64|FUInt64) ->
330 pr " v = caml_copy_int64 (%s->%s);\n" typ name
331 | name, (FInt32|FUInt32) ->
332 pr " v = caml_copy_int32 (%s->%s);\n" typ name
333 | name, FOptPercent ->
334 pr " if (%s->%s >= 0) { /* Some %s */\n" typ name name;
335 pr " v2 = caml_copy_double (%s->%s);\n" typ name;
336 pr " v = caml_alloc (1, 0);\n";
337 pr " Store_field (v, 0, v2);\n";
338 pr " } else /* None */\n";
339 pr " v = Val_int (0);\n";
341 pr " v = Val_int (%s->%s);\n" typ name
343 pr " Store_field (rv, %d, v);\n" i
345 pr " CAMLreturn (rv);\n";
350 (* Emit a copy_TYPE_list function definition only if that function is used. *)
353 | typ, (RStructListOnly | RStructAndList) ->
354 (* generate the function for typ *)
355 emit_ocaml_copy_list_function typ
356 | typ, _ -> () (* empty *)
357 ) (rstructs_used_by all_functions);
361 fun (name, (ret, args, optargs as style), _, _, _, _, _) ->
362 pr "/* Automatically generated wrapper for function\n";
364 generate_ocaml_prototype name style;
368 (* If we run into this situation, we'll need to change the
371 if args = [] && optargs <> [] then
372 failwithf "ocaml bindings don't support args = [], optargs <> []";
376 List.map (fun arg -> name_of_argt arg ^ "v") (optargs @ args) in
379 match ret with RConstOptString _ -> true | _ -> false in
381 pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
382 pr "CAMLprim value ocaml_guestfs_%s (value %s" name (List.hd params);
383 List.iter (pr ", value %s") (List.tl params); pr ");\n";
386 pr "CAMLprim value\n";
387 pr "ocaml_guestfs_%s (value %s" name (List.hd params);
388 List.iter (pr ", value %s") (List.tl params);
393 | [p1; p2; p3; p4; p5] ->
394 pr " CAMLparam5 (%s);\n" (String.concat ", " params)
395 | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
396 pr " CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
397 pr " CAMLxparam%d (%s);\n"
398 (List.length rest) (String.concat ", " rest)
400 pr " CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
402 if not needs_extra_vs then
403 pr " CAMLlocal1 (rv);\n"
405 pr " CAMLlocal3 (rv, v, v2);\n";
408 pr " guestfs_h *g = Guestfs_val (gv);\n";
409 pr " if (g == NULL)\n";
410 pr " ocaml_guestfs_raise_closed (\"%s\");\n" name;
416 | Device n | Dev_or_Path n
421 (* Copy strings in case the GC moves them: RHBZ#604691 *)
422 pr " char *%s = guestfs_safe_strdup (g, String_val (%sv));\n" n n
424 pr " char *%s =\n" n;
425 pr " %sv != Val_int (0) ?\n" n;
426 pr " guestfs_safe_strdup (g, String_val (Field (%sv, 0))) : NULL;\n" n
428 pr " size_t %s_size = caml_string_length (%sv);\n" n n;
429 pr " char *%s = guestfs_safe_memdup (g, String_val (%sv), %s_size);\n" n n n
430 | StringList n | DeviceList n ->
431 pr " char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n
433 pr " int %s = Bool_val (%sv);\n" n n
435 pr " int %s = Int_val (%sv);\n" n n
437 pr " int64_t %s = Int64_val (%sv);\n" n n
439 pr " %s %s = (%s) (intptr_t) Int64_val (%sv);\n" t n t n
442 (* Optional arguments. *)
443 if optargs <> [] then (
444 pr " struct guestfs_%s_argv optargs_s = { .bitmask = 0 };\n" name;
445 pr " struct guestfs_%s_argv *optargs = &optargs_s;\n" name;
446 let uc_name = String.uppercase name in
449 let n = name_of_argt argt in
450 let uc_n = String.uppercase n in
451 pr " if (%sv != Val_int (0)) {\n" n;
452 pr " optargs_s.bitmask |= GUESTFS_%s_%s_BITMASK;\n" uc_name uc_n;
453 pr " optargs_s.%s = " n;
455 | Bool _ -> pr "Bool_val (Field (%sv, 0))" n
456 | Int _ -> pr "Int_val (Field (%sv, 0))" n
457 | Int64 _ -> pr "Int64_val (Field (%sv, 0))" n
459 pr "guestfs_safe_strdup (g, String_val (Field (%sv, 0)))" n
468 | RErr -> pr " int r;\n"
469 | RInt _ -> pr " int r;\n"
470 | RInt64 _ -> pr " int64_t r;\n"
471 | RBool _ -> pr " int r;\n"
472 | RConstString _ | RConstOptString _ ->
473 pr " const char *r;\n"
474 | RString _ -> pr " char *r;\n"
478 | RStruct (_, typ) ->
479 pr " struct guestfs_%s *r;\n" typ
480 | RStructList (_, typ) ->
481 pr " struct guestfs_%s_list *r;\n" typ
491 pr " caml_enter_blocking_section ();\n";
493 pr " r = guestfs_%s " name
495 pr " r = guestfs_%s_argv " name;
496 generate_c_call_args ~handle:"g" style;
498 pr " caml_leave_blocking_section ();\n";
500 (* Free strings if we copied them above. *)
503 | Pathname n | Device n | Dev_or_Path n | String n | OptString n
504 | FileIn n | FileOut n | BufferIn n | Key n ->
506 | StringList n | DeviceList n ->
507 pr " ocaml_guestfs_free_strings (%s);\n" n;
508 | Bool _ | Int _ | Int64 _ | Pointer _ -> ()
513 pr " if (%sv != Val_int (0))\n" n;
514 pr " free ((char *) optargs_s.%s);\n" n
515 | Bool _ | Int _ | Int64 _
516 | Pathname _ | Device _ | Dev_or_Path _ | OptString _
517 | FileIn _ | FileOut _ | BufferIn _ | Key _
518 | StringList _ | DeviceList _ | Pointer _ -> ()
521 (match errcode_of_ret ret with
522 | `CannotReturnError -> ()
523 | `ErrorIsMinusOne ->
524 pr " if (r == -1)\n";
525 pr " ocaml_guestfs_raise_error (g, \"%s\");\n" name;
527 pr " if (r == NULL)\n";
528 pr " ocaml_guestfs_raise_error (g, \"%s\");\n" name;
533 | RErr -> pr " rv = Val_unit;\n"
534 | RInt _ -> pr " rv = Val_int (r);\n"
536 pr " rv = caml_copy_int64 (r);\n"
537 | RBool _ -> pr " rv = Val_bool (r);\n"
539 pr " rv = caml_copy_string (r);\n"
540 | RConstOptString _ ->
541 pr " if (r) { /* Some string */\n";
542 pr " v = caml_alloc (1, 0);\n";
543 pr " v2 = caml_copy_string (r);\n";
544 pr " Store_field (v, 0, v2);\n";
545 pr " } else /* None */\n";
546 pr " v = Val_int (0);\n";
548 pr " rv = caml_copy_string (r);\n";
551 pr " rv = caml_copy_string_array ((const char **) r);\n";
552 pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
554 | RStruct (_, typ) ->
555 pr " rv = copy_%s (r);\n" typ;
556 pr " guestfs_free_%s (r);\n" typ;
557 | RStructList (_, typ) ->
558 pr " rv = copy_%s_list (r);\n" typ;
559 pr " guestfs_free_%s_list (r);\n" typ;
561 pr " rv = copy_table (r);\n";
562 pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
565 pr " rv = caml_alloc_string (size);\n";
566 pr " memcpy (String_val (rv), r, size);\n";
569 pr " CAMLreturn (rv);\n";
573 if List.length params > 5 then (
574 pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
575 pr "CAMLprim value ";
576 pr "ocaml_guestfs_%s_byte (value *argv, int argn);\n" name;
577 pr "CAMLprim value\n";
578 pr "ocaml_guestfs_%s_byte (value *argv, int argn ATTRIBUTE_UNUSED)\n"
581 pr " return ocaml_guestfs_%s (argv[0]" name;
582 iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
587 ) all_functions_sorted
589 and generate_ocaml_structure_decls () =
592 pr "type %s = {\n" typ;
595 | name, FString -> pr " %s : string;\n" name
596 | name, FBuffer -> pr " %s : string;\n" name
597 | name, FUUID -> pr " %s : string;\n" name
598 | name, (FBytes|FInt64|FUInt64) -> pr " %s : int64;\n" name
599 | name, (FInt32|FUInt32) -> pr " %s : int32;\n" name
600 | name, FChar -> pr " %s : char;\n" name
601 | name, FOptPercent -> pr " %s : float option;\n" name
607 and generate_ocaml_prototype ?(is_external = false) name style =
608 if is_external then pr "external " else pr "val ";
609 pr "%s : t -> " name;
610 generate_ocaml_function_type style;
611 if is_external then (
613 let _, args, optargs = style in
614 if List.length args + List.length optargs + 1 > 5 then
615 pr "\"ocaml_guestfs_%s_byte\" " name;
616 pr "\"ocaml_guestfs_%s\"" name
620 and generate_ocaml_function_type (ret, args, optargs) =
623 | Bool n -> pr "?%s:bool -> " n
624 | Int n -> pr "?%s:int -> " n
625 | Int64 n -> pr "?%s:int64 -> " n
626 | String n -> pr "?%s:string -> " n
631 | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _
632 | BufferIn _ | Key _ -> pr "string -> "
633 | OptString _ -> pr "string option -> "
634 | StringList _ | DeviceList _ -> pr "string array -> "
635 | Bool _ -> pr "bool -> "
636 | Int _ -> pr "int -> "
637 | Int64 _ | Pointer _ -> pr "int64 -> "
640 | RErr -> pr "unit" (* all errors are turned into exceptions *)
642 | RInt64 _ -> pr "int64"
643 | RBool _ -> pr "bool"
644 | RConstString _ -> pr "string"
645 | RConstOptString _ -> pr "string option"
646 | RString _ | RBufferOut _ -> pr "string"
647 | RStringList _ -> pr "string array"
648 | RStruct (_, typ) -> pr "%s" typ
649 | RStructList (_, typ) -> pr "%s array" typ
650 | RHashtable _ -> pr "(string * string) list"