diff options
author | Matthew Booth <mbooth@redhat.com> | 2012-01-04 09:42:41 +0000 |
---|---|---|
committer | Matthew Booth <mbooth@redhat.com> | 2012-01-20 18:12:38 +0000 |
commit | 5c5babb40b06256d251425b10ca5cfff97907462 (patch) | |
tree | 38192ee448d04be5f38a4e710c707b97a1f6f17a | |
parent | eb58fa9978b71448c01348c151cc002c21983c71 (diff) | |
download | libguestfs-5c5babb40b06256d251425b10ca5cfff97907462.tar.gz libguestfs-5c5babb40b06256d251425b10ca5cfff97907462.tar.xz libguestfs-5c5babb40b06256d251425b10ca5cfff97907462.zip |
gobject: Add GObject bindings
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | configure.ac | 37 | ||||
-rw-r--r-- | generator/Makefile.am | 1 | ||||
-rw-r--r-- | generator/generator_gobject.ml | 878 | ||||
-rw-r--r-- | generator/generator_main.ml | 3 | ||||
-rw-r--r-- | gobject/Makefile.am | 58 | ||||
-rw-r--r-- | m4/introspection.m4 | 94 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
9 files changed, 1079 insertions, 0 deletions
@@ -120,6 +120,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 f9255294..839379ae 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 ce0f6e82..2ca0c475 100644 --- a/configure.ac +++ b/configure.ac @@ -996,6 +996,38 @@ AS_IF([test "x$PERL" != "xno"], AM_CONDITIONAL([HAVE_TOOLS], [test "x$PERL" != "xno" && test "x$missing_perl_modules" != "xyes"]) +dnl gobject library +AC_ARG_ENABLE([gobject], + AS_HELP_STRING([--disable-gobject], [Disable GObject bindings]), + [], + [enable_gobject=yes]) +AS_IF( + [test "x$enable_gobject" != "xno"], + [ + 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 The above check automatically sets HAVE_INTROSPECTION, but we want this to +dnl be conditional on gobject also being available. We can't move the above +dnl check inside the gobject if block above or HAVE_INTROSPECTION ends up +dnl undefined, so we recheck it here. +AM_CONDITIONAL([HAVE_INTROSPECTION], + [test "x$HAVE_INTROSPECTION_TRUE" = "x" && + test "x$HAVE_GOBJECT_TRUE" = "x"]) + dnl Library versioning. MAX_PROC_NR=`cat $srcdir/src/MAX_PROC_NR` AC_SUBST(MAX_PROC_NR) @@ -1033,6 +1065,7 @@ AC_CONFIG_FILES([Makefile generator/Makefile gnulib/lib/Makefile gnulib/tests/Makefile + gobject/Makefile haskell/Makefile inspector/Makefile java/Makefile @@ -1105,6 +1138,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_TRUE" = "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 c556322e..fd37f673 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 00000000..4779f233 --- /dev/null +++ b/generator/generator_gobject.ml @@ -0,0 +1,878 @@ +(* 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 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) + +let returns_error = function + | RConstOptString _ -> false + | _ -> true + +let 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 ")" + +let 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()) + +" + +let generate_gobject_header_static_footer () = + pr " + +G_END_DECLS + +#endif /* GUESTFS_GOBJECT_H__ */ +" + +let 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 " GByteArray *%s;\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 + +let iter_optargs f = + List.iter ( + function + | name, (_, _, (_::_ as optargs)), _, flags,_, _, _ -> + f name optargs flags + | _ -> () + ) + +let 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" + +let generate_gobject_header_optargs () = + pr "/* Optional arguments */\n\n"; + iter_optargs ( + fun name optargs flags -> + generate_gobject_header_optarg name optargs flags + ) all_functions + +let generate_gobject_header_methods () = + pr "/* Generated methods */\n"; + List.iter ( + fun (name, style, _, flags, _, _, _) -> + generate_gobject_proto name style flags; + pr ";\n"; + ) all_functions + +let 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); +} + +" + +let 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 + +let 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" + +let generate_gobject_c_optargs () = + pr "/* Optarg objects */\n\n"; + + iter_optargs ( + fun name optargs flags -> + generate_gobject_c_optarg name optargs flags + ) all_functions + +let 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 = + 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 "%sif (%s%s) memcpy(%s%s, %s%s, sizeof(%s%s));\n" + indent src n dst n src n dst n + | n, FString -> + pr "%sif (%s%s) %s%s = g_strdup(%s%s);\n" + indent src n dst n src n + | n, FBuffer -> + pr "%sif (%s%s) {\n" indent src n; + pr "%s %s%s = g_byte_array_sized_new(%s%s_len);\n" + indent dst n src n; + pr "%s g_byte_array_append(%s%s, %s%s, %s%s_len);\n" + indent dst n src n src n; + pr "%s}\n" indent + ) (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) -> + let struct_name = "Guestfs" ^ camel_name_of_struct typ in + pr " %s *s = g_slice_new0(%s);\n" struct_name struct_name; + 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"; + pr " l[i] = g_slice_new0(%s);\n" struct_name; + gen_copy_struct " " "ret->val[i]." "l[i]->" typ; + pr " }\n"; + pr " guestfs_free_%s_list(ret);\n" typ; + pr " l[i] = NULL;\n"; + pr " return l;\n"; + ); + + pr "}\n\n"; + ) all_functions + +let 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 () + +let 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 669a2720..c7ee041a 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 00000000..25441cf9 --- /dev/null +++ b/gobject/Makefile.am @@ -0,0 +1,58 @@ +# 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. + +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 00000000..bfc52be7 --- /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 84972dba..4ed598e6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -127,6 +127,7 @@ fish/virt.c format/format.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 |