[Libguestfs] [PATCH 1/2] gobject: Add GObject bindings

Matthew Booth mbooth at redhat.com
Tue Jan 17 15:48:42 UTC 2012


---
 .gitignore                     |    4 +
 Makefile.am                    |    3 +
 configure.ac                   |   17 +
 generator/Makefile.am          |    1 +
 generator/generator_gobject.ml |  873 ++++++++++++++++++++++++++++++++++++++++
 generator/generator_main.ml    |    3 +
 gobject/Makefile.am            |   41 ++
 m4/introspection.m4            |   94 +++++
 po/POTFILES.in                 |    1 +
 9 files changed, 1037 insertions(+), 0 deletions(-)
 create mode 100644 generator/generator_gobject.ml
 create mode 100644 gobject/Makefile.am
 create mode 100644 m4/introspection.m4

diff --git a/.gitignore b/.gitignore
index 0611975..0615dc2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,10 @@ generator/stamp-generator
 .git-module-status
 /gnulib
 /GNUmakefile
+gobject/Guestfs-1.0.gir
+gobject/Guestfs-1.0.typelib
+gobject/guestfs-gobject.c
+gobject/guestfs-gobject.h
 .guestfs-*
 guestfs.*
 guestfsd-in-wine.log
diff --git a/Makefile.am b/Makefile.am
index d14cc12..69a9ed1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -79,6 +79,9 @@ endif
 if HAVE_ERLANG
 SUBDIRS += erlang erlang/examples
 endif
+if HAVE_GOBJECT
+SUBDIRS += gobject
+endif
 
 # Unconditional because nothing is built yet.
 SUBDIRS += csharp
diff --git a/configure.ac b/configure.ac
index d8abf71..c549388 100644
--- a/configure.ac
+++ b/configure.ac
@@ -986,6 +986,18 @@ AS_IF([test "x$PERL" != "xno"],
 AM_CONDITIONAL([HAVE_TOOLS],
     [test "x$PERL" != "xno" && test "x$missing_perl_modules" != "xyes"])
 
+dnl gobject library
+PKG_CHECK_MODULES([GOBJECT], [gobject-2.0],
+        [AC_SUBST([GOBJECT_CFLAGS])
+         AC_SUBST([GOBJECT_LIBS])
+         AC_DEFINE([HAVE_GOBJECT],[1],[gobject library found at compile time.])
+        ],
+        [AC_MSG_WARN([gobject library not found, gobject binding will be disabled])])
+AM_CONDITIONAL([HAVE_GOBJECT],[test "x$GOBJECT_LIBS" != "x"])
+
+dnl gobject introspection
+GOBJECT_INTROSPECTION_CHECK([1.30.0])
+
 dnl Library versioning.
 MAX_PROC_NR=`cat $srcdir/src/MAX_PROC_NR`
 AC_SUBST(MAX_PROC_NR)
@@ -1022,6 +1034,7 @@ AC_CONFIG_FILES([Makefile
                  generator/Makefile
                  gnulib/lib/Makefile
                  gnulib/tests/Makefile
+                 gobject/Makefile
                  haskell/Makefile
                  inspector/Makefile
                  java/Makefile
@@ -1094,6 +1107,10 @@ if test "x$HAVE_TOOLS_TRUE" = "x"; then echo "yes"; else echo "no"; fi
 echo -n "virt-resize ......................... "
 if test "x$HAVE_OCAML_TRUE" = "x"; then echo "yes"; else echo "no"; fi
 echo "FUSE filesystem ..................... $enable_fuse"
+echo -n "gobject bindings .................... "
+if test "x$HAVE_GOBJECT_TRUE" = "x"; then echo "yes"; else echo "no"; fi
+echo -n "gobject introspection ............... "
+if test "x$HAVE_INTROSPECTION" = "x"; then echo "yes"; else echo "no"; fi
 echo
 echo "If any optional component is configured 'no' when you expected 'yes'"
 echo "then you should check the preceeding messages."
diff --git a/generator/Makefile.am b/generator/Makefile.am
index b01cf35..526b959 100644
--- a/generator/Makefile.am
+++ b/generator/Makefile.am
@@ -47,6 +47,7 @@ SOURCES = \
 	generator_csharp.ml \
 	generator_php.ml \
 	generator_erlang.ml \
+	generator_gobject.ml \
 	generator_bindtests.ml \
 	generator_errnostring.ml \
 	generator_main.ml
diff --git a/generator/generator_gobject.ml b/generator/generator_gobject.ml
new file mode 100644
index 0000000..58b5ce5
--- /dev/null
+++ b/generator/generator_gobject.ml
@@ -0,0 +1,873 @@
+(* libguestfs
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+(* Please read generator/README first. *)
+
+open Str
+
+open Generator_actions
+open Generator_docstrings
+open Generator_pr
+open Generator_structs
+open Generator_types
+open Generator_utils
+
+let rec camel_of_name flags name =
+  "Guestfs" ^
+  try
+    find_map (function CamelName n -> Some n | _ -> None) flags
+  with Not_found ->
+    List.fold_left (
+      fun a b ->
+        a ^ String.uppercase (Str.first_chars b 1) ^ Str.string_after b 1
+    ) "" (Str.split (regexp "_") name)
+
+and returns_error (ret:Generator_types.ret) = match ret with
+  | RConstOptString _ -> false
+  | _ -> true
+
+and generate_gobject_proto name ?(single_line = true)
+                                (ret, args, optargs) flags =
+  let spacer = if single_line then " " else "\n" in
+  let ptr_spacer = if single_line then "" else "\n" in
+  (match ret with
+   | RErr ->
+      pr "gboolean%s" spacer
+   | RInt _ ->
+      pr "gint32%s" spacer
+   | RInt64 _ ->
+      pr "gint64%s" spacer
+   | RBool _ ->
+      pr "gint8%s" spacer
+   | RConstString _
+   | RConstOptString _ ->
+      pr "const gchar *%s" ptr_spacer
+   | RString _ ->
+      pr "gchar *%s" ptr_spacer
+   | RStringList _ ->
+      pr "gchar **%s" ptr_spacer
+   | RStruct (_, typ) ->
+      let name = camel_name_of_struct typ in
+      pr "Guestfs%s *%s" name ptr_spacer
+   | RStructList (_, typ) ->
+      let name = camel_name_of_struct typ in
+      pr "Guestfs%s **%s" name ptr_spacer
+   | RHashtable _ ->
+      pr "GHashTable *%s" ptr_spacer
+   | RBufferOut _ ->
+      pr "guint8 *%s" ptr_spacer
+  );
+  pr "guestfs_session_%s(GuestfsSession *session" name;
+  List.iter (
+    fun arg ->
+      pr ", ";
+      match arg with
+      | Bool n ->
+        pr "gboolean %s" n
+      | Int n ->
+        pr "gint32 %s" n
+      | Int64 n->
+        pr "gint64 %s" n
+      | String n
+      | Device n
+      | Pathname n
+      | Dev_or_Path n
+      | OptString n
+      | Key n
+      | FileIn n
+      | FileOut n ->
+        pr "const gchar *%s" n
+      | StringList n
+      | DeviceList n ->
+        pr "gchar *const *%s" n
+      | BufferIn n ->
+        pr "const guint8 *%s, gsize %s_size" n n
+      | Pointer _ ->
+        failwith "gobject bindings do not support Pointer arguments"
+  ) args;
+  if optargs <> [] then (
+    pr ", %s *optargs" (camel_of_name flags name)
+  );
+  (match ret with
+  | RBufferOut _ ->
+    pr ", gsize *size_r"
+  | _ -> ());
+  if List.exists (function Cancellable -> true | _ -> false) flags then
+    pr ", GCancellable *cancellable";
+  if returns_error ret then pr ", GError **err";
+  pr ")";
+
+and generate_gobject_header_static () =
+  pr "
+#ifndef GUESTFS_GOBJECT_H__
+#define GUESTFS_GOBJECT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+/* Guestfs::Session object definition */
+#define GUESTFS_TYPE_SESSION             (guestfs_session_get_type())
+#define GUESTFS_SESSION(obj)             (G_TYPE_CHECK_INSTANCE_CAST ( \
+                                          (obj), \
+                                          GUESTFS_TYPE_SESSION, \
+                                          GuestfsSession))
+#define GUESTFS_SESSION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ( \
+                                          (klass), \
+                                          GUESTFS_TYPE_SESSION, \
+                                          GuestfsSessionClass))
+#define GUESTFS_IS_SESSION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ( \
+                                          (obj), \
+                                          GUESTFS_TYPE_SESSION))
+#define GUESTFS_IS_SESSION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ( \
+                                          (klass), \
+                                          GUESTFS_TYPE_SESSION))
+#define GUESTFS_SESSION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ( \
+                                          (obj), \
+                                          GUESTFS_TYPE_SESSION, \
+                                          GuestfsSessionClass))
+
+typedef struct _GuestfsSession GuestfsSession;
+typedef struct _GuestfsSessionClass GuestfsSessionClass;
+typedef struct _GuestfsSessionPrivate GuestfsSessionPrivate;
+
+struct _GuestfsSession
+{
+  GObject parent;
+  GuestfsSessionPrivate *priv;
+};
+
+struct _GuestfsSessionClass
+{
+  GObjectClass parent_class;
+};
+
+GType guestfs_session_get_type(void);
+GuestfsSession *guestfs_session_new(void);
+
+/* Guestfs::Tristate */
+typedef enum
+{
+  GUESTFS_TRISTATE_FALSE,
+  GUESTFS_TRISTATE_TRUE,
+  GUESTFS_TRISTATE_NONE
+} GuestfsTristate;
+
+GType guestfs_tristate_get_type(void);
+#define GUESTFS_TYPE_TRISTATE (guestfs_tristate_get_type())
+
+"
+
+and generate_gobject_header_static_footer () =
+  pr "
+
+G_END_DECLS
+
+#endif /* GUESTFS_GOBJECT_H__ */
+"
+
+and generate_gobject_header_structs () =
+  pr "/* Structs */\n";
+  List.iter (
+    fun (typ, cols) ->
+      let camel = camel_name_of_struct typ in
+      pr "typedef struct _Guestfs%s Guestfs%s;\n" camel camel;
+      pr "struct _Guestfs%s {\n" camel;
+      List.iter (
+        function
+        | n, FChar ->
+          pr "  gchar %s;\n" n
+        | n, FUInt32 ->
+          pr "  guint32 %s;\n" n
+        | n, FInt32 ->
+          pr "  gint32 %s;\n" n
+        | n, (FUInt64|FBytes) ->
+          pr "  guint64 %s;\n" n
+        | n, FInt64 ->
+          pr "  gint64 %s;\n" n
+        | n, FString ->
+          pr "  gchar *%s;\n" n
+        | n, FBuffer ->
+          pr "  guint8 *%s;\n" n;
+          pr "  guint32 %s_size;\n" n
+        | n, FUUID ->
+          pr "  /* The next field is NOT nul-terminated, be careful when printing it: */\n";
+          pr "  gchar %s[32];\n" n
+        | n, FOptPercent ->
+          pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
+          pr "  gfloat %s;\n" n
+      ) cols;
+      pr "};\n";
+      pr "GType guestfs_%s_get_type(void);\n\n" typ;
+  ) structs;
+
+and iter_optargs f =
+  List.iter (
+    function
+    | name, (_, _, (_::_ as optargs)), _, flags,_, _, _ ->
+      f name optargs flags
+    | _ -> ()
+  )
+
+and generate_gobject_header_optarg name optargs flags =
+  let uc_name = String.uppercase name in
+  let camel_name = camel_of_name flags name in
+  let type_define = "GUESTFS_TYPE_" ^ uc_name in
+
+  pr "/* %s */\n" camel_name;
+
+  pr "#define %s " type_define;
+  pr "(guestfs_%s_get_type())\n" name;
+
+  pr "#define GUESTFS_%s(obj) " uc_name;
+  pr "(G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))\n" type_define camel_name;
+
+  pr "#define GUESTFS_%s_CLASS(klass) " uc_name;
+  pr "(G_TYPE_CHECK_CLASS_CAST((klass), %s, %sClass))\n" type_define camel_name;
+
+  pr "#define GUESTFS_IS_%s(obj) " uc_name;
+  pr "(G_TYPE_CHECK_INSTANCE_TYPE((klass), %s))\n" type_define;
+
+  pr "#define GUESTFS_IS_%s_CLASS(klass) " uc_name;
+  pr "(G_TYPE_CHECK_CLASS_TYPE((klass), %s))\n" type_define;
+
+  pr "#define GUESTFS_%s_GET_CLASS(obj) " uc_name;
+  pr "(G_TYPE_INSTANCE_GET_CLASS((obj), %s, %sClass))\n" type_define camel_name;
+
+  pr "\n";
+
+  List.iter (
+    fun suffix ->
+      let name = camel_name ^ suffix in
+      pr "typedef struct _%s %s;\n" name name;
+  ) [ ""; "Private"; "Class" ];
+
+  pr "\n";
+
+  pr "struct _%s {\n" camel_name;
+  pr "  GObject parent;\n";
+  pr "  %sPrivate *priv;\n" camel_name;
+  pr "};\n\n";
+
+  pr "struct _%sClass {\n" camel_name;
+  pr "  GObjectClass parent_class;\n";
+  pr "};\n\n";
+
+  pr "GType guestfs_%s_get_type(void);\n" name;
+  pr "%s *guestfs_%s_new(void);\n" camel_name name;
+
+  pr "\n";
+
+and generate_gobject_header_optargs () =
+  pr "/* Optional arguments */\n\n";
+  iter_optargs (
+    fun name optargs flags ->
+      generate_gobject_header_optarg name optargs flags
+  ) all_functions;
+
+and generate_gobject_header_methods () =
+  pr "/* Generated methods */\n";
+  List.iter (
+    fun (name, style, _, flags, _, _, _) ->
+      generate_gobject_proto name style flags;
+      pr ";\n";
+  ) all_functions;
+
+and generate_gobject_c_static () =
+  pr "
+#include <glib.h>
+#include <glib-object.h>
+#include <guestfs.h>
+#include <string.h>
+
+#include <stdio.h>
+
+#include \"guestfs-gobject.h\"
+
+/**
+ * SECTION: guestfs-session
+ * @short_description: Libguestfs session
+ * @include: guestfs-gobject.h
+ *
+ * A libguestfs session which can be used to inspect and modify virtual disk
+ * images.
+ */
+
+#define GUESTFS_SESSION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ( \
+                                            (obj), \
+                                            GUESTFS_TYPE_SESSION, \
+                                            GuestfsSessionPrivate))
+
+struct _GuestfsSessionPrivate
+{
+  guestfs_h *g;
+};
+
+G_DEFINE_TYPE(GuestfsSession, guestfs_session, G_TYPE_OBJECT);
+
+static void
+guestfs_session_finalize(GObject *object)
+{
+  GuestfsSession *session = GUESTFS_SESSION(object);
+  GuestfsSessionPrivate *priv = session->priv;
+
+  if (priv->g) guestfs_close(priv->g);
+
+  G_OBJECT_CLASS(guestfs_session_parent_class)->finalize(object);
+}
+
+static void
+guestfs_session_class_init(GuestfsSessionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+  object_class->finalize = guestfs_session_finalize;
+
+  g_type_class_add_private(klass, sizeof(GuestfsSessionPrivate));
+}
+
+static void
+guestfs_session_init(GuestfsSession *session)
+{
+  session->priv = GUESTFS_SESSION_GET_PRIVATE(session);
+  session->priv->g = guestfs_create();
+}
+
+/**
+ * guestfs_session_new:
+ *
+ * Create a new libguestfs session.
+ *
+ * Returns: (transfer full): a new guestfs session object
+ */
+GuestfsSession *
+guestfs_session_new(void)
+{
+  return GUESTFS_SESSION(g_object_new(GUESTFS_TYPE_SESSION, NULL));
+}
+
+/* Guestfs::Tristate */
+GType
+guestfs_tristate_get_type(void)
+{
+  static GType etype = 0;
+  if (etype == 0) {
+    static const GEnumValue values[] = {
+      { GUESTFS_TRISTATE_FALSE, \"GUESTFS_TRISTATE_FALSE\", \"false\" },
+      { GUESTFS_TRISTATE_TRUE,  \"GUESTFS_TRISTATE_TRUE\",  \"true\" },
+      { GUESTFS_TRISTATE_NONE,  \"GUESTFS_TRISTATE_NONE\",  \"none\" },
+      { 0, NULL, NULL }
+    };
+    etype = g_enum_register_static(\"GuestfsTristate\", values);
+  }
+  return etype;
+}
+
+/* Error quark */
+
+#define GUESTFS_ERROR guestfs_error_quark()
+
+static GQuark
+guestfs_error_quark(void)
+{
+  return g_quark_from_static_string(\"guestfs\");
+}
+
+/* Cancellation handler */
+static void
+cancelled_handler(gpointer data)
+{
+  guestfs_h *g = (guestfs_h *)data;
+  guestfs_user_cancel(g);
+}
+
+"
+
+and generate_gobject_c_structs () =
+  pr "/* Structs */\n\n";
+  List.iter (
+    fun (typ, cols) ->
+      let name = "guestfs_" ^ typ in
+      let camel_name = "Guestfs" ^ camel_name_of_struct typ in
+      pr "/* %s */\n" camel_name;
+
+      pr "static %s *\n" camel_name;
+      pr "%s_copy(%s *src)\n" name camel_name;
+      pr "{\n";
+      pr "  return g_slice_dup(%s, src);\n" camel_name;
+      pr "}\n\n";
+
+      pr "static void\n";
+      pr "%s_free(%s *src)\n" name camel_name;
+      pr "{\n";
+      pr "  g_slice_free(%s, src);\n" camel_name;
+      pr "}\n\n";
+
+      pr "G_DEFINE_BOXED_TYPE(%s, %s, %s_copy, %s_free)\n\n"
+         camel_name name name name;
+  ) structs
+
+and generate_gobject_c_optarg name optargs flags =
+  let uc_name = String.uppercase name in
+  let camel_name = camel_of_name flags name in
+  let type_define = "GUESTFS_TYPE_" ^ uc_name in
+
+  pr "/* %s */\n" camel_name;
+  pr "#define GUESTFS_%s_GET_PRIVATE(obj) " uc_name;
+  pr "(G_TYPE_INSTANCE_GET_PRIVATE((obj), %s, %sPrivate))\n\n"
+    type_define camel_name;
+
+  pr "struct _%sPrivate {\n" camel_name;
+  List.iter (
+    fun optargt ->
+      let name = name_of_optargt optargt in
+      let typ = match optargt with
+      | OBool n   -> "GuestfsTristate "
+      | OInt n    -> "gint "
+      | OInt64 n  -> "gint64 "
+      | OString n -> "gchar *" in
+      pr "  %s%s;\n" typ name;
+  ) optargs;
+  pr "};\n\n";
+
+  pr "G_DEFINE_TYPE(%s, guestfs_%s, G_TYPE_OBJECT);\n\n" camel_name name;
+
+  pr "enum {\n";
+  pr "PROP_GUESTFS_%s_PROP0" uc_name;
+  List.iter (
+    fun optargt ->
+      let uc_optname = String.uppercase (name_of_optargt optargt) in
+      pr ",\n  PROP_GUESTFS_%s_%s" uc_name uc_optname;
+  ) optargs;
+  pr "\n};\n\n";
+
+  pr "static void\nguestfs_%s_set_property" name;
+  pr "(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)\n";
+  pr "{\n";
+  pr "  %s *self = GUESTFS_%s(object);\n" camel_name uc_name;
+  pr "  %sPrivate *priv = self->priv;\n\n" camel_name;
+
+  pr "  switch (property_id) {\n";
+  List.iter (
+    fun optargt ->
+      let optname = name_of_optargt optargt in
+      let uc_optname = String.uppercase optname in
+      pr "    case PROP_GUESTFS_%s_%s:\n" uc_name uc_optname;
+      (match optargt with
+      | OString n ->
+        pr "      g_free(priv->%s);\n" n;
+      | OBool _ | OInt _ | OInt64 _ -> ());
+      let set_value_func = match optargt with
+      | OBool _   -> "g_value_get_enum"
+      | OInt _    -> "g_value_get_int"
+      | OInt64 _  -> "g_value_get_int64"
+      | OString _ -> "g_value_dup_string"
+      in
+      pr "      priv->%s = %s(value);\n" optname set_value_func;
+      pr "      break;\n\n";
+  ) optargs;
+  pr "    default:\n";
+  pr "      /* Invalid property */\n";
+  pr "      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);\n";
+  pr "  }\n";
+  pr "}\n\n";
+
+  pr "static void\nguestfs_%s_get_property" name;
+  pr "(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)\n";
+  pr "{\n";
+  pr "  %s *self = GUESTFS_%s(object);\n" camel_name uc_name;
+  pr "  %sPrivate *priv = self->priv;\n\n" camel_name;
+
+  pr "  switch (property_id) {\n";
+  List.iter (
+    fun optargt ->
+      let optname = name_of_optargt optargt in
+      let uc_optname = String.uppercase optname in
+      pr "    case PROP_GUESTFS_%s_%s:\n" uc_name uc_optname;
+      let set_value_func = match optargt with
+      | OBool _   -> "enum"
+      | OInt _    -> "int"
+      | OInt64 _  -> "int64"
+      | OString _ -> "string"
+      in
+      pr "      g_value_set_%s(value, priv->%s);\n" set_value_func optname;
+      pr "      break;\n\n";
+  ) optargs;
+  pr "    default:\n";
+  pr "      /* Invalid property */\n";
+  pr "      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);\n";
+  pr "  }\n";
+  pr "}\n\n";
+
+  pr "static void\nguestfs_%s_finalize(GObject *object)\n" name;
+  pr "{\n";
+  pr "  %s *self = GUESTFS_%s(object);\n" camel_name uc_name;
+  pr "  %sPrivate *priv = self->priv;\n\n" camel_name;
+
+  List.iter (
+    function
+    | OString n ->
+      pr "  g_free(priv->%s);\n" n
+    | OBool _ | OInt _ | OInt64 _ -> ()
+  ) optargs;
+  pr "\n";
+
+  pr "  G_OBJECT_CLASS(guestfs_%s_parent_class)->finalize(object);\n" name;
+  pr "}\n\n";
+
+  pr "static void\nguestfs_%s_class_init(%sClass *klass)\n" name camel_name;
+  pr "{\n";
+  pr "  GObjectClass *object_class = G_OBJECT_CLASS(klass);\n";
+  pr "  GParamSpec *pspec;\n\n";
+
+  pr "  object_class->set_property = guestfs_%s_set_property;\n" name;
+  pr "  object_class->get_property = guestfs_%s_get_property;\n\n" name;
+
+  List.iter (
+    fun optargt ->
+      let optname = name_of_optargt optargt in
+      let uc_optname = String.uppercase optname in
+      pr "  pspec = ";
+      (match optargt with
+      | OBool n ->
+        pr "g_param_spec_enum(\"%s\", \"%s\", NULL, " optname optname;
+        pr "GUESTFS_TYPE_TRISTATE, GUESTFS_TRISTATE_NONE, ";
+      | OInt n ->
+        pr "g_param_spec_int(\"%s\", \"%s\", NULL, " optname optname;
+        pr "G_MININT32, G_MAXINT32, -1, ";
+      | OInt64 n ->
+        pr "g_param_spec_int64(\"%s\", \"%s\", NULL, " optname optname;
+        pr "G_MININT64, G_MAXINT64, -1, ";
+      | OString n ->
+        pr "g_param_spec_string(\"%s\", \"%s\", NULL, " optname optname;
+        pr "NULL, ");
+      pr "G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);\n";
+      pr "  g_object_class_install_property(object_class, ";
+      pr "PROP_GUESTFS_%s_%s, pspec);\n\n" uc_name uc_optname;
+  ) optargs;
+
+  pr "  object_class->finalize = guestfs_%s_finalize;\n" name;
+  pr "  g_type_class_add_private(klass, sizeof(%sPrivate));\n" camel_name;
+  pr "}\n\n";
+
+  pr "static void\nguestfs_%s_init(%s *o)\n" name camel_name;
+  pr "{\n";
+  pr "  o->priv = GUESTFS_%s_GET_PRIVATE(o);\n" uc_name;
+  pr "  /* XXX: Find out if gobject already zeroes private structs */\n";
+  pr "  memset(o->priv, 0, sizeof(%sPrivate));\n" camel_name;
+  pr "}\n\n";
+
+  pr "/**\n";
+  pr " * guestfs_%s_new:\n" name;
+  pr " *\n";
+  pr " * Create a new %s object\n" camel_name;
+  pr " *\n";
+  pr " * Returns: (transfer full): a new %s object\n" camel_name;
+  pr " */\n";
+  pr "%s *\n" camel_name;
+  pr "guestfs_%s_new(void)\n" name;
+  pr "{\n";
+  pr "  return GUESTFS_%s(g_object_new(%s, NULL));\n" uc_name type_define;
+  pr "}\n\n";
+
+and generate_gobject_c_optargs () =
+  pr "/* Optarg objects */\n\n";
+
+  iter_optargs (
+    fun name optargs flags ->
+      generate_gobject_c_optarg name optargs flags
+  ) all_functions;
+
+and generate_gobject_c_methods () =
+  pr "/* Generated methods */\n\n";
+
+  List.iter (
+    fun (name, (ret, args, optargs as style), _, flags, _, shortdesc, longdesc) ->
+      let doc = pod2text ~width:60 name longdesc in
+      let doc = String.concat "\n * " doc in
+      let camel_name = camel_of_name flags name in
+      let is_RBufferOut = match ret with RBufferOut _ -> true | _ -> false in
+      let error_return = match ret with
+      | RErr ->
+        "FALSE"
+      | RInt _ | RInt64 _ | RBool _ ->
+        "-1"
+      | RConstString _ | RString _ | RStringList _ | RHashtable _
+      | RBufferOut _ | RStruct _ | RStructList _ ->
+        "NULL"
+      | RConstOptString _ -> ""
+      in
+
+      (* The comment header, including GI annotations for arguments and the
+      return value *)
+
+      pr "/**\n";
+      pr " * guestfs_session_%s:\n" name;
+
+      List.iter (
+        fun argt ->
+          pr " * @%s:" (name_of_argt argt);
+          (match argt with
+          | Bool _ | Int _ | Int64 _ -> ()
+          | String _ | Key _ ->
+            pr " (transfer none) (type utf8):"
+          | OptString _ ->
+            pr " (transfer none) (type utf8) (allow-none):"
+          | Device _ | Pathname _ | Dev_or_Path _ | FileIn _ | FileOut _ ->
+            pr " (transfer none) (type filename):"
+          | StringList _ ->
+            pr " (transfer none) (array zero-terminated=1) (element-type utf8): an array of strings"
+          | DeviceList _ ->
+            pr " (transfer none) (array zero-terminated=1) (element-type filename): an array of strings"
+          | BufferIn n ->
+            pr " (transfer none) (array length=%s_size) (element-type guint8): an array of binary data" n
+          | Pointer _ ->
+            failwith "gobject bindings do not support Pointer arguments"
+          );
+          pr "\n";
+      ) args;
+      if optargs <> [] then
+        pr " * @optargs: (transfer none) (allow-none): a %s containing optional arguments\n" camel_name;
+      pr " *\n";
+
+      pr " * %s\n" shortdesc;
+      pr " *\n";
+      pr " * %s\n" doc;
+
+      pr " * Returns: ";
+      (match ret with
+      | RErr ->
+        pr "true on success, false on error"
+      | RInt _ | RInt64 _ | RBool _ ->
+        pr "the returned value, or -1 on error"
+      | RConstString _ ->
+        pr "(transfer none): the returned string, or NULL on error"
+      | RConstOptString _ ->
+        pr "(transfer none): the returned string. Note that NULL does not indicate error"
+      | RString _ ->
+        pr "(transfer full): the returned string, or NULL on error"
+      | RStringList _ ->
+        pr "(transfer full) (array zero-terminated=1) (element-type utf8): an array of returned strings, or NULL on error"
+      | RHashtable _ ->
+        pr "(transfer full) (element-type utf8 utf8): a GHashTable of results, or NULL on error"
+      | RBufferOut _ ->
+        pr "(transfer full) (array length=size_r) (element-type guint8): an array of binary data, or NULL on error"
+      | RStruct (_, typ) ->
+         let name = camel_name_of_struct typ in
+         pr "(transfer full): a %s object, or NULL on error" name
+      | RStructList (_, typ) ->
+         let name = camel_name_of_struct typ in
+         pr "(transfer full) (array zero-terminated=1) (element-type Guestfs%s): an array of %s objects, or NULL on error" name name
+      );
+      pr "\n";
+      pr " */\n";
+
+      (* The function body *)
+
+      generate_gobject_proto ~single_line:false name style flags;
+      pr "\n{\n";
+
+      let cancellable =
+        List.exists (function Cancellable -> true | _ -> false) flags
+      in
+      if cancellable then (
+        pr "  /* Check we haven't already been cancelled */\n";
+        pr "  if (g_cancellable_set_error_if_cancelled (cancellable, err))\n";
+        pr "    return %s;\n\n" error_return;
+      );
+
+      pr "  guestfs_h *g = session->priv->g;\n";
+
+      (* Optargs *)
+
+      if optargs <> [] then (
+        pr "  struct guestfs_%s_argv argv;\n" name;
+        pr "  struct guestfs_%s_argv *argvp = NULL;\n\n" name;
+
+        pr "  if (optargs) {\n";
+        let uc_prefix = "GUESTFS_" ^ String.uppercase name in
+        pr "    argv.bitmask = 0;\n\n";
+        let set_property name typ v_typ get_typ unset =
+          let uc_name = String.uppercase name in
+          pr "    GValue %s_v = {0, };\n" name;
+          pr "    g_value_init(&%s_v, %s);\n" name v_typ;
+          pr "    g_object_get_property(G_OBJECT(optargs), \"%s\", &%s_v);\n" name name;
+          pr "    %s%s = g_value_get_%s(&%s_v);\n" typ name get_typ name;
+          pr "    if (%s != %s) {\n" name unset;
+          pr "      argv.bitmask |= %s_%s_BITMASK;\n" uc_prefix uc_name;
+          pr "      argv.%s = %s;\n" name name;
+          pr "    }\n"
+        in
+        List.iter (
+          function
+          | OBool n ->
+            set_property n "GuestfsTristate " "GUESTFS_TYPE_TRISTATE" "enum" "GUESTFS_TRISTATE_NONE"
+          | OInt n ->
+            set_property n "gint32 " "G_TYPE_INT" "int" "-1"
+          | OInt64 n ->
+            set_property n "gint64 " "G_TYPE_INT64" "int64" "-1"
+          | OString n ->
+            set_property n "const gchar *" "G_TYPE_STRING" "string" "NULL"
+        ) optargs;
+        pr "    argvp = &argv;\n";
+        pr "  }\n"
+      );
+
+      (* libguestfs call *)
+
+      if cancellable then (
+        pr "  gulong id = 0;\n";
+        pr "  if (cancellable) {\n";
+        pr "    id = g_cancellable_connect(cancellable,\n";
+        pr "                               G_CALLBACK(cancelled_handler),\n";
+        pr "                               g, NULL);\n";
+        pr "  }\n\n";
+      );
+
+      pr "  ";
+      (match ret with
+      | RErr | RInt _ | RBool _ ->
+        pr "int "
+      | RInt64 _ ->
+        pr "int64_t "
+      | RConstString _ | RConstOptString _ ->
+        pr "const char *"
+      | RString _ | RBufferOut _ ->
+        pr "char *"
+      | RStringList _ | RHashtable _ ->
+        pr "char **"
+      | RStruct (_, typ) ->
+        pr "struct guestfs_%s *" typ
+      | RStructList (_, typ) ->
+        pr "struct guestfs_%s_list *" typ
+      );
+      let suffix = if optargs <> [] then "_argv" else "" in
+      pr "ret = guestfs_%s%s(g" name suffix;
+      List.iter (
+        fun argt ->
+          pr ", ";
+          match argt with
+          | BufferIn n ->
+            pr "%s, %s_size" n n
+          | Bool n | Int n | Int64 n | String n | Device n | Pathname n
+          | Dev_or_Path n | OptString n | StringList n | DeviceList n
+          | Key n | FileIn n | FileOut n ->
+            pr "%s" n
+          | Pointer _ ->
+            failwith "gobject bindings do not support Pointer arguments"
+      ) args;
+      if is_RBufferOut then pr ", size_r";
+      if optargs <> [] then pr ", argvp";
+      pr ");\n";
+
+      if cancellable then
+        pr "  g_cancellable_disconnect(cancellable, id);\n";
+
+      (* Check return, throw error if necessary, marshall return value *)
+
+      if returns_error ret then (
+        pr "  if (ret == %s) {\n"
+          (match ret with
+          | RErr | RInt _ | RInt64 _ | RBool _ ->
+            "-1"
+          | RConstString _ | RString _ | RStringList _ | RHashtable _
+          | RBufferOut _ | RStruct _ | RStructList _ ->
+            "NULL"
+          | RConstOptString _ ->
+            assert false;
+          );
+        pr "    g_set_error_literal(err, GUESTFS_ERROR, 0, guestfs_last_error(g));\n";
+        pr "    return %s;\n" error_return;
+        pr "  }\n";
+      );
+      pr "\n";
+
+      let gen_copy_struct indent src dst typ =
+        let struct_name = "Guestfs" ^ camel_name_of_struct typ in
+        pr "%s%s *%s = g_slice_new(%s);\n" indent struct_name dst struct_name;
+        List.iter (
+          function
+          | n, (FChar|FUInt32|FInt32|FUInt64|FBytes|FInt64|FOptPercent) ->
+            pr "%s%s->%s = %s%s;\n" indent dst n src n 
+          | n, FUUID ->
+            pr "%smemcpy(%s->%s, %s%s, sizeof(%s->%s));\n"
+              indent dst n src n dst n
+          | n, FString ->
+            pr "%s%s->%s = g_strdup(%s%s);\n" indent dst n src n
+          | n, FBuffer ->
+            pr "%s%s->%s = %s%s;\n" indent dst n src n;
+            pr "%s%s->%s_size = %s%s_len;\n" indent dst n src n
+        ) (cols_of_struct typ)
+      in
+      (match ret with
+      | RErr ->
+        pr "  return TRUE;\n"
+
+      | RInt _ | RInt64 _ | RBool _
+      | RConstString _ | RConstOptString _
+      | RString _ | RStringList _
+      | RBufferOut _ ->
+        pr "  return ret;\n"
+
+      | RHashtable _ ->
+        pr "  GHashTable *h = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);\n";
+        pr "  char **i = ret;\n";
+        pr "  while (*i) {\n";
+        pr "    char *key = *i; i++;\n";
+        pr "    char *value = *i; i++;\n";
+        pr "    g_hash_table_insert(h, key, value);\n";
+        pr "  };\n";
+        pr "  g_free(ret);\n";
+        pr "  return h;\n"
+
+      | RStruct (_, typ) ->
+        gen_copy_struct "  " "ret->" "s" typ;
+        pr "  guestfs_free_%s(ret);\n" typ;
+        pr "  return s;\n";
+
+      | RStructList (_, typ) ->
+        let struct_name = "Guestfs" ^ camel_name_of_struct typ in
+        pr "  %s **l = g_malloc(sizeof(%s*) * (ret->len + 1));\n" struct_name struct_name;
+        pr "  gsize i;\n";
+        pr "  for(i = 0; i < ret->len; i++) {\n";
+        gen_copy_struct "    " "ret->val[i]." "s" typ;
+        pr "    l[i] = s;\n";
+        pr "  }\n";
+        pr "  guestfs_free_%s_list(ret);\n" typ;
+        pr "  l[i] = NULL;\n";
+        pr "  return l;\n";
+      );
+
+      pr "}\n\n";
+  ) all_functions;
+
+and generate_gobject_header () =
+  generate_header CStyle GPLv2plus;
+  generate_gobject_header_static ();
+  generate_gobject_header_structs ();
+  generate_gobject_header_optargs ();
+  generate_gobject_header_methods ();
+  generate_gobject_header_static_footer ();
+
+and generate_gobject_c () =
+  generate_header CStyle GPLv2plus;
+  generate_gobject_c_static ();
+  generate_gobject_c_structs ();
+  generate_gobject_c_optargs ();
+  generate_gobject_c_methods ();
diff --git a/generator/generator_main.ml b/generator/generator_main.ml
index 53a0f29..f1412a9 100644
--- a/generator/generator_main.ml
+++ b/generator/generator_main.ml
@@ -39,6 +39,7 @@ open Generator_haskell
 open Generator_csharp
 open Generator_php
 open Generator_erlang
+open Generator_gobject
 open Generator_bindtests
 open Generator_errnostring
 
@@ -136,6 +137,8 @@ Run it from the top source directory using the command
   output_to "php/extension/guestfs_php.c" generate_php_c;
   output_to "erlang/guestfs.erl" generate_erlang_erl;
   output_to "erlang/erl-guestfs.c" generate_erlang_c;
+  output_to "gobject/guestfs-gobject.h" generate_gobject_header;
+  output_to "gobject/guestfs-gobject.c" generate_gobject_c;
 
   (* Generate the list of files generated -- last. *)
   printf "generated %d lines of code\n" (get_lines_generated ());
diff --git a/gobject/Makefile.am b/gobject/Makefile.am
new file mode 100644
index 0000000..6543ca8
--- /dev/null
+++ b/gobject/Makefile.am
@@ -0,0 +1,41 @@
+BUILT_SOURCES = guestfs-gobject.h guestfs-gobject.c
+EXTRA_DIST = $(BUILT_SOURCES)
+
+libname = libguestfs-gobject-1.0.la
+
+lib_LTLIBRARIES = $(libname)
+
+libguestfs_gobject_1_0_ladir = $(includedir)
+
+libguestfs_gobject_1_0_la_HEADERS = guestfs-gobject.h
+libguestfs_gobject_1_0_la_SOURCES = guestfs-gobject.c
+libguestfs_gobject_1_0_la_CFLAGS = -I$(top_srcdir)/src $(GOBJECT_CFLAGS)
+libguestfs_gobject_1_0_la_LIBS = $(GOBJECT_LIBS)
+libguestfs_gobject_1_0_la_LDFLAGS = $(LDFLAGS) -L$(top_builddir)/src
+libguestfs_gobject_1_0_la_LIBADD = -lguestfs
+
+-include $(INTROSPECTION_MAKEFILE)
+INTROSPECTION_GIRS =
+INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) --warn-all
+INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
+
+if HAVE_INTROSPECTION
+introspection_sources = \
+  $(libguestfs_gobject_1_0_la_HEADERS) \
+  $(libguestfs_gobject_1_0_la_SOURCES)
+
+Guestfs-1.0.gir: $(libname)
+Guestfs_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0
+Guestfs_1_0_gir_CFLAGS = $(INCLUDES)
+Guestfs_1_0_gir_LIBS = $(libname)
+Guestfs_1_0_gir_FILES = $(introspection_sources)
+INTROSPECTION_GIRS += Guestfs-1.0.gir
+
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(libdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES = $(gir_DATA) $(typelib_DATA)
+endif
diff --git a/m4/introspection.m4 b/m4/introspection.m4
new file mode 100644
index 0000000..bfc52be
--- /dev/null
+++ b/m4/introspection.m4
@@ -0,0 +1,94 @@
+dnl -*- mode: autoconf -*-
+dnl Copyright 2009 Johan Dahlin
+dnl
+dnl This file is free software; the author(s) gives unlimited
+dnl permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+
+# serial 1
+
+m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL],
+[
+    AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
+    AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first
+    AC_BEFORE([LT_INIT],[$0])dnl setup libtool first
+
+    dnl enable/disable introspection
+    m4_if([$2], [require],
+    [dnl
+        enable_introspection=yes
+    ],[dnl
+        AC_ARG_ENABLE(introspection,
+                  AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]],
+                                 [Enable introspection for this build]),,
+                                 [enable_introspection=auto])
+    ])dnl
+
+    AC_MSG_CHECKING([for gobject-introspection])
+
+    dnl presence/version checking
+    AS_CASE([$enable_introspection],
+    [no], [dnl
+        found_introspection="no (disabled, use --enable-introspection to enable)"
+    ],dnl
+    [yes],[dnl
+        PKG_CHECK_EXISTS([gobject-introspection-1.0],,
+                         AC_MSG_ERROR([gobject-introspection-1.0 is not installed]))
+        PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1],
+                         found_introspection=yes,
+                         AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME]))
+    ],dnl
+    [auto],[dnl
+        PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no)
+    ],dnl
+    [dnl
+        AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@])
+    ])dnl
+
+    AC_MSG_RESULT([$found_introspection])
+
+    INTROSPECTION_SCANNER=
+    INTROSPECTION_COMPILER=
+    INTROSPECTION_GENERATE=
+    INTROSPECTION_GIRDIR=
+    INTROSPECTION_TYPELIBDIR=
+    if test "x$found_introspection" = "xyes"; then
+       INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
+       INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
+       INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
+       INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
+       INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
+       INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0`
+       INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0`
+       INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection
+    fi
+    AC_SUBST(INTROSPECTION_SCANNER)
+    AC_SUBST(INTROSPECTION_COMPILER)
+    AC_SUBST(INTROSPECTION_GENERATE)
+    AC_SUBST(INTROSPECTION_GIRDIR)
+    AC_SUBST(INTROSPECTION_TYPELIBDIR)
+    AC_SUBST(INTROSPECTION_CFLAGS)
+    AC_SUBST(INTROSPECTION_LIBS)
+    AC_SUBST(INTROSPECTION_MAKEFILE)
+
+    AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes")
+])
+
+
+dnl Usage:
+dnl   GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version])
+
+AC_DEFUN([GOBJECT_INTROSPECTION_CHECK],
+[
+  _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1])
+])
+
+dnl Usage:
+dnl   GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version])
+
+
+AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE],
+[
+  _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require])
+])
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 380a3c7..14e8545 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -126,6 +126,7 @@ fish/time.c
 fish/virt.c
 fuse/dircache.c
 fuse/guestmount.c
+gobject/guestfs-gobject.c
 inspector/virt-inspector.c
 java/com_redhat_et_libguestfs_GuestFS.c
 ocaml/guestfs_c.c
-- 
1.7.7.5




More information about the Libguestfs mailing list