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;
97 (** {2 Object-oriented API}
99 This is an alternate way of calling the API using an object-oriented
100 style, so you can use [g#add_drive filename] instead of
101 [Guestfs.add_drive g filename]. Apart from the different style,
102 it offers exactly the same functionality.
104 Calling [new guestfs ()] creates both the object and the handle.
106 Note that methods that take no parameters (except the implicit handle)
107 get an extra unit [()] parameter. This is so you can create a
108 closure from the method easily. For example [g#get_verbose ()]
109 calls the method, whereas [g#get_verbose] is a function. *)
111 class guestfs : unit -> object
112 method close : unit -> unit
113 method set_progress_callback : progress_cb -> unit
114 method clear_progress_callback : unit -> unit
119 | name, ((_, []) as style), _, _, _, _, _ ->
120 pr " method %s : unit -> " name;
121 generate_ocaml_function_type style;
123 | name, style, _, _, _, _, _ ->
124 pr " method %s : " name;
125 generate_ocaml_function_type style;
127 ) all_functions_sorted;
131 (* Generate the OCaml bindings implementation. *)
132 and generate_ocaml_ml () =
133 generate_header OCamlStyle LGPLv2plus;
138 exception Error of string
139 exception Handle_closed of string
141 external create : unit -> t = \"ocaml_guestfs_create\"
142 external close : t -> unit = \"ocaml_guestfs_close\"
144 type progress_cb = int -> int -> int64 -> int64 -> unit
146 external set_progress_callback : t -> progress_cb -> unit
147 = \"ocaml_guestfs_set_progress_callback\"
148 external clear_progress_callback : t -> unit
149 = \"ocaml_guestfs_clear_progress_callback\"
151 (* Give the exceptions names, so they can be raised from the C code. *)
153 Callback.register_exception \"ocaml_guestfs_error\" (Error \"\");
154 Callback.register_exception \"ocaml_guestfs_closed\" (Handle_closed \"\")
158 generate_ocaml_structure_decls ();
162 fun (name, style, _, _, _, shortdesc, _) ->
163 generate_ocaml_prototype ~is_external:true name style;
164 ) all_functions_sorted;
171 method close () = close g
172 method set_progress_callback = set_progress_callback g
173 method clear_progress_callback () = clear_progress_callback g
178 | name, (_, []), _, _, _, _, _ -> (* no params? add explicit unit *)
179 pr " method %s () = %s g\n" name name
180 | name, _, _, _, _, _, _ ->
181 pr " method %s = %s g\n" name name
182 ) all_functions_sorted;
186 (* Generate the OCaml bindings C implementation. *)
187 and generate_ocaml_c () =
188 generate_header CStyle LGPLv2plus;
195 #include <caml/config.h>
196 #include <caml/alloc.h>
197 #include <caml/callback.h>
198 #include <caml/fail.h>
199 #include <caml/memory.h>
200 #include <caml/mlvalues.h>
201 #include <caml/signals.h>
203 #include \"guestfs.h\"
205 #include \"guestfs_c.h\"
207 /* Copy a hashtable of string pairs into an assoc-list. We return
208 * the list in reverse order, but hashtables aren't supposed to be
211 static CAMLprim value
212 copy_table (char * const * argv)
215 CAMLlocal5 (rv, pairv, kv, vv, cons);
219 for (i = 0; argv[i] != NULL; i += 2) {
220 kv = caml_copy_string (argv[i]);
221 vv = caml_copy_string (argv[i+1]);
222 pairv = caml_alloc (2, 0);
223 Store_field (pairv, 0, kv);
224 Store_field (pairv, 1, vv);
225 cons = caml_alloc (2, 0);
226 Store_field (cons, 1, rv);
228 Store_field (cons, 0, pairv);
236 (* Struct copy functions. *)
238 let emit_ocaml_copy_list_function typ =
239 pr "static CAMLprim value\n";
240 pr "copy_%s_list (const struct guestfs_%s_list *%ss)\n" typ typ typ;
242 pr " CAMLparam0 ();\n";
243 pr " CAMLlocal2 (rv, v);\n";
244 pr " unsigned int i;\n";
246 pr " if (%ss->len == 0)\n" typ;
247 pr " CAMLreturn (Atom (0));\n";
249 pr " rv = caml_alloc (%ss->len, 0);\n" typ;
250 pr " for (i = 0; i < %ss->len; ++i) {\n" typ;
251 pr " v = copy_%s (&%ss->val[i]);\n" typ typ;
252 pr " caml_modify (&Field (rv, i), v);\n";
254 pr " CAMLreturn (rv);\n";
262 let has_optpercent_col =
263 List.exists (function (_, FOptPercent) -> true | _ -> false) cols in
265 pr "static CAMLprim value\n";
266 pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ;
268 pr " CAMLparam0 ();\n";
269 if has_optpercent_col then
270 pr " CAMLlocal3 (rv, v, v2);\n"
272 pr " CAMLlocal2 (rv, v);\n";
274 pr " rv = caml_alloc (%d, 0);\n" (List.length cols);
279 pr " v = caml_copy_string (%s->%s);\n" typ name
281 pr " v = caml_alloc_string (%s->%s_len);\n" typ name;
282 pr " memcpy (String_val (v), %s->%s, %s->%s_len);\n"
285 pr " v = caml_alloc_string (32);\n";
286 pr " memcpy (String_val (v), %s->%s, 32);\n" typ name
287 | name, (FBytes|FInt64|FUInt64) ->
288 pr " v = caml_copy_int64 (%s->%s);\n" typ name
289 | name, (FInt32|FUInt32) ->
290 pr " v = caml_copy_int32 (%s->%s);\n" typ name
291 | name, FOptPercent ->
292 pr " if (%s->%s >= 0) { /* Some %s */\n" typ name name;
293 pr " v2 = caml_copy_double (%s->%s);\n" typ name;
294 pr " v = caml_alloc (1, 0);\n";
295 pr " Store_field (v, 0, v2);\n";
296 pr " } else /* None */\n";
297 pr " v = Val_int (0);\n";
299 pr " v = Val_int (%s->%s);\n" typ name
301 pr " Store_field (rv, %d, v);\n" i
303 pr " CAMLreturn (rv);\n";
308 (* Emit a copy_TYPE_list function definition only if that function is used. *)
311 | typ, (RStructListOnly | RStructAndList) ->
312 (* generate the function for typ *)
313 emit_ocaml_copy_list_function typ
314 | typ, _ -> () (* empty *)
315 ) (rstructs_used_by all_functions);
319 fun (name, style, _, _, _, _, _) ->
320 pr "/* Automatically generated wrapper for function\n";
322 generate_ocaml_prototype name style;
327 "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
330 match fst style with RConstOptString _ -> true | _ -> false in
332 pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
333 pr "CAMLprim value ocaml_guestfs_%s (value %s" name (List.hd params);
334 List.iter (pr ", value %s") (List.tl params); pr ");\n";
337 pr "CAMLprim value\n";
338 pr "ocaml_guestfs_%s (value %s" name (List.hd params);
339 List.iter (pr ", value %s") (List.tl params);
344 | [p1; p2; p3; p4; p5] ->
345 pr " CAMLparam5 (%s);\n" (String.concat ", " params)
346 | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
347 pr " CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
348 pr " CAMLxparam%d (%s);\n"
349 (List.length rest) (String.concat ", " rest)
351 pr " CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
353 if not needs_extra_vs then
354 pr " CAMLlocal1 (rv);\n"
356 pr " CAMLlocal3 (rv, v, v2);\n";
359 pr " guestfs_h *g = Guestfs_val (gv);\n";
360 pr " if (g == NULL)\n";
361 pr " ocaml_guestfs_raise_closed (\"%s\");\n" name;
367 | Device n | Dev_or_Path n
372 (* Copy strings in case the GC moves them: RHBZ#604691 *)
373 pr " char *%s = guestfs_safe_strdup (g, String_val (%sv));\n" n n
375 pr " char *%s =\n" n;
376 pr " %sv != Val_int (0) ?" n;
377 pr " guestfs_safe_strdup (g, String_val (Field (%sv, 0))) : NULL;\n" n
379 pr " size_t %s_size = caml_string_length (%sv);\n" n n;
380 pr " char *%s = guestfs_safe_memdup (g, String_val (%sv), %s_size);\n" n n n
381 | StringList n | DeviceList n ->
382 pr " char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n
384 pr " int %s = Bool_val (%sv);\n" n n
386 pr " int %s = Int_val (%sv);\n" n n
388 pr " int64_t %s = Int64_val (%sv);\n" n n
392 | RErr -> pr " int r;\n"; "-1"
393 | RInt _ -> pr " int r;\n"; "-1"
394 | RInt64 _ -> pr " int64_t r;\n"; "-1"
395 | RBool _ -> pr " int r;\n"; "-1"
396 | RConstString _ | RConstOptString _ ->
397 pr " const char *r;\n"; "NULL"
398 | RString _ -> pr " char *r;\n"; "NULL"
403 | RStruct (_, typ) ->
404 pr " struct guestfs_%s *r;\n" typ; "NULL"
405 | RStructList (_, typ) ->
406 pr " struct guestfs_%s_list *r;\n" typ; "NULL"
413 pr " size_t size;\n";
417 pr " caml_enter_blocking_section ();\n";
418 pr " r = guestfs_%s " name;
419 generate_c_call_args ~handle:"g" style;
421 pr " caml_leave_blocking_section ();\n";
423 (* Free strings if we copied them above. *)
426 | Pathname n | Device n | Dev_or_Path n | String n | OptString n
427 | FileIn n | FileOut n | BufferIn n | Key n ->
429 | StringList n | DeviceList n ->
430 pr " ocaml_guestfs_free_strings (%s);\n" n;
431 | Bool _ | Int _ | Int64 _ -> ()
434 pr " if (r == %s)\n" error_code;
435 pr " ocaml_guestfs_raise_error (g, \"%s\");\n" name;
438 (match fst style with
439 | RErr -> pr " rv = Val_unit;\n"
440 | RInt _ -> pr " rv = Val_int (r);\n"
442 pr " rv = caml_copy_int64 (r);\n"
443 | RBool _ -> pr " rv = Val_bool (r);\n"
445 pr " rv = caml_copy_string (r);\n"
446 | RConstOptString _ ->
447 pr " if (r) { /* Some string */\n";
448 pr " v = caml_alloc (1, 0);\n";
449 pr " v2 = caml_copy_string (r);\n";
450 pr " Store_field (v, 0, v2);\n";
451 pr " } else /* None */\n";
452 pr " v = Val_int (0);\n";
454 pr " rv = caml_copy_string (r);\n";
457 pr " rv = caml_copy_string_array ((const char **) r);\n";
458 pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
460 | RStruct (_, typ) ->
461 pr " rv = copy_%s (r);\n" typ;
462 pr " guestfs_free_%s (r);\n" typ;
463 | RStructList (_, typ) ->
464 pr " rv = copy_%s_list (r);\n" typ;
465 pr " guestfs_free_%s_list (r);\n" typ;
467 pr " rv = copy_table (r);\n";
468 pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
471 pr " rv = caml_alloc_string (size);\n";
472 pr " memcpy (String_val (rv), r, size);\n";
475 pr " CAMLreturn (rv);\n";
479 if List.length params > 5 then (
480 pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
481 pr "CAMLprim value ";
482 pr "ocaml_guestfs_%s_byte (value *argv, int argn);\n" name;
483 pr "CAMLprim value\n";
484 pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
486 pr " return ocaml_guestfs_%s (argv[0]" name;
487 iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
492 ) all_functions_sorted
494 and generate_ocaml_structure_decls () =
497 pr "type %s = {\n" typ;
500 | name, FString -> pr " %s : string;\n" name
501 | name, FBuffer -> pr " %s : string;\n" name
502 | name, FUUID -> pr " %s : string;\n" name
503 | name, (FBytes|FInt64|FUInt64) -> pr " %s : int64;\n" name
504 | name, (FInt32|FUInt32) -> pr " %s : int32;\n" name
505 | name, FChar -> pr " %s : char;\n" name
506 | name, FOptPercent -> pr " %s : float option;\n" name
512 and generate_ocaml_prototype ?(is_external = false) name style =
513 if is_external then pr "external " else pr "val ";
514 pr "%s : t -> " name;
515 generate_ocaml_function_type style;
516 if is_external then (
518 if List.length (snd style) + 1 > 5 then
519 pr "\"ocaml_guestfs_%s_byte\" " name;
520 pr "\"ocaml_guestfs_%s\"" name
524 and generate_ocaml_function_type style =
527 | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _
528 | BufferIn _ | Key _ -> pr "string -> "
529 | OptString _ -> pr "string option -> "
530 | StringList _ | DeviceList _ -> pr "string array -> "
531 | Bool _ -> pr "bool -> "
532 | Int _ -> pr "int -> "
533 | Int64 _ -> pr "int64 -> "
535 (match fst style with
536 | RErr -> pr "unit" (* all errors are turned into exceptions *)
538 | RInt64 _ -> pr "int64"
539 | RBool _ -> pr "bool"
540 | RConstString _ -> pr "string"
541 | RConstOptString _ -> pr "string option"
542 | RString _ | RBufferOut _ -> pr "string"
543 | RStringList _ -> pr "string array"
544 | RStruct (_, typ) -> pr "%s" typ
545 | RStructList (_, typ) -> pr "%s array" typ
546 | RHashtable _ -> pr "(string * string) list"