Add new files from last commit
authorColin Walters <walters@src.gnome.org>
Thu, 13 Nov 2008 20:13:39 +0000 (20:13 +0000)
committerColin Walters <walters@src.gnome.org>
Thu, 13 Nov 2008 20:13:39 +0000 (20:13 +0000)
svn path=/trunk/; revision=913

girepository/gdump.c [new file with mode: 0644]
giscanner/dumper.py [new file with mode: 0644]
tests/scanner/barapp.c [new file with mode: 0644]
tests/scanner/barapp.h [new file with mode: 0644]

diff --git a/girepository/gdump.c b/girepository/gdump.c
new file mode 100644 (file)
index 0000000..90ca27c
--- /dev/null
@@ -0,0 +1,386 @@
+/* GObject introspection: Dump introspection data
+ *
+ * Copyright (C) 2008 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "girepository.h"
+#include "config.h"
+
+#include <string.h>
+
+static void
+escaped_printf (GOutputStream *out, const char *fmt, ...)
+{
+  char *str;
+  va_list args;
+  gsize written;
+  GError *error = NULL;
+
+  va_start (args, fmt);
+
+  str = g_markup_vprintf_escaped (fmt, args);
+  if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
+    {
+      g_critical ("failed to write to iochannel: %s", error->message);
+      g_clear_error (&error);
+    }
+  g_free (str);
+
+  va_end (args);
+}
+
+static void
+goutput_write (GOutputStream *out, const char *str)
+{
+  gsize written;
+  GError *error = NULL;
+  if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
+    {
+      g_critical ("failed to write to iochannel: %s", error->message);
+      g_clear_error (&error);
+    }
+}
+
+typedef GType (*GetTypeFunc)(void);
+
+static GType
+invoke_get_type (GModule *self, const char *symbol, GError **error)
+{
+  GetTypeFunc sym;
+
+  if (!g_module_symbol (self, symbol, (void**)&sym))
+    {
+      g_set_error (error,
+                  G_IO_ERROR,
+                  G_IO_ERROR_FAILED,
+                  "Failed to find symbol '%s'", symbol);
+      return G_TYPE_INVALID;
+    }
+
+  return sym ();
+}
+
+static void
+dump_properties (GType type, GOutputStream *out)
+{
+  guint i;
+  guint n_properties;
+  GParamSpec **props;
+
+  if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
+    {
+      GObjectClass *klass;
+      klass = g_type_class_ref (type);
+      props = g_object_class_list_properties (klass, &n_properties);
+    }
+  else
+    {
+      void *klass;
+      klass = g_type_default_interface_ref (type);
+      props = g_object_interface_list_properties (klass, &n_properties);
+    }
+
+  for (i = 0; i < n_properties; i++)
+    {
+      GParamSpec *prop;
+
+      prop = props[i];
+      if (prop->owner_type != type)
+       continue;
+
+      escaped_printf (out, "    <property name=\"%s\" type=\"%s\" flags=\"%d\"/>\n",
+                     prop->name, g_type_name (prop->value_type), prop->flags);
+    }
+  g_free (props);
+}
+
+static void
+dump_signals (GType type, GOutputStream *out)
+{
+  guint i;
+  guint n_sigs;
+  guint *sig_ids;
+
+  sig_ids = g_signal_list_ids (type, &n_sigs);
+  for (i = 0; i < n_sigs; i++)
+    {
+      guint sigid;
+      GSignalQuery query;
+      guint j;
+
+      sigid = sig_ids[i];
+      g_signal_query (sigid, &query);
+
+      escaped_printf (out, "    <signal name=\"%s\" return=\"%s\">\n",
+                     query.signal_name, g_type_name (query.return_type));
+
+      for (j = 0; j < query.n_params; j++)
+       {
+         escaped_printf (out, "      <param type=\"%s\"/>\n",
+                         g_type_name (query.param_types[j]));
+       }
+      goutput_write (out, "    </signal>\n");
+    }
+}
+
+static void
+dump_object_type (GType type, const char *symbol, GOutputStream *out)
+{
+  guint n_interfaces;
+  guint i;
+  GType *interfaces;
+
+  escaped_printf (out, "  <class name=\"%s\" get-type=\"%s\"",
+                 g_type_name (type), symbol);
+  if (type != G_TYPE_OBJECT)
+    escaped_printf (out, " parent=\"%s\"", g_type_name (g_type_parent (type)));
+
+  if (G_TYPE_IS_ABSTRACT (type))
+    escaped_printf (out, " abstract=\"1\"");
+  goutput_write (out, ">\n");
+
+  interfaces = g_type_interfaces (type, &n_interfaces);
+  for (i = 0; i < n_interfaces; i++)
+    {
+      GType itype = interfaces[i];
+      escaped_printf (out, "    <implements name=\"%s\"/>\n",
+                     g_type_name (itype));
+    }
+  dump_properties (type, out);
+  dump_signals (type, out);
+  goutput_write (out, "  </class>\n");
+}
+
+static void
+dump_interface_type (GType type, const char *symbol, GOutputStream *out)
+{
+  guint n_interfaces;
+  guint i;
+  GType *interfaces;
+
+  escaped_printf (out, "  <interface name=\"%s\" get-type=\"%s\">\n",
+                 g_type_name (type), symbol);
+
+  interfaces = g_type_interface_prerequisites (type, &n_interfaces);
+  for (i = 0; i < n_interfaces; i++)
+    {
+      GType itype = interfaces[i];
+      escaped_printf (out, "    <extends>%s</extends>\n",
+                     g_type_name (itype));
+    }
+  dump_properties (type, out);
+  dump_signals (type, out);
+  goutput_write (out, "  </interface>\n");
+}
+
+static void
+dump_boxed_type (GType type, const char *symbol, GOutputStream *out)
+{
+  escaped_printf (out, "  <boxed name=\"%s\" get-type=\"%s\"/>\n",
+                 g_type_name (type), symbol);
+}
+
+static void
+dump_flags_type (GType type, const char *symbol, GOutputStream *out)
+{
+  guint i;
+  GFlagsClass *klass;
+
+  klass = g_type_class_ref (type);
+  escaped_printf (out, "  <flags name=\"%s\" get-type=\"%s\">\n",
+                 g_type_name (type), symbol);
+
+  for (i = 0; i < klass->n_values; i++)
+    {
+      GFlagsValue *value = &(klass->values[i]);
+
+      escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
+                     value->value_name, value->value_nick, value->value);
+    }
+  goutput_write (out, "  </flags>\n");
+}
+
+static void
+dump_enum_type (GType type, const char *symbol, GOutputStream *out)
+{
+  guint i;
+  GEnumClass *klass;
+
+  klass = g_type_class_ref (type);
+  escaped_printf (out, "  <enum name=\"%s\" get-type=\"%s\">\n",
+                 g_type_name (type), symbol);
+
+  for (i = 0; i < klass->n_values; i++)
+    {
+      GEnumValue *value = &(klass->values[i]);
+
+      escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
+                     value->value_name, value->value_nick, value->value);
+    }
+  goutput_write (out, "  </enum>");
+}
+
+static void
+dump_type (GType type, const char *symbol, GOutputStream *out)
+{
+  g_printerr ("processing %s\n", g_type_name (type));
+  switch (g_type_fundamental (type))
+    {
+    case G_TYPE_OBJECT:
+      dump_object_type (type, symbol, out);
+      break;
+    case G_TYPE_INTERFACE:
+      dump_interface_type (type, symbol, out);
+      break;
+    case G_TYPE_BOXED:
+      dump_boxed_type (type, symbol, out);
+      break;
+    case G_TYPE_FLAGS:
+      dump_flags_type (type, symbol, out);
+      break;
+    case G_TYPE_ENUM:
+      dump_enum_type (type, symbol, out);
+      break;
+    case G_TYPE_POINTER:
+      /* GValue, etc.  Just skip them. */
+      break;
+    default:
+      g_warning ("unhandled gtype %s", g_type_name (type));
+    }
+}
+
+/**
+ * g_irepository_dump:
+ * @arg: Comma-separated pair of input and output filenames
+ * @error: a %GError
+ *
+ * Argument specified is a comma-separated pair of filenames; i.e. of
+ * the form "input.txt,output.xml".  The input file should be a
+ * UTF-8 Unix-line-ending text file, with each line containing the name
+ * of a GType _get_type function.
+ *
+ * The output file should already exist, but be empty.  This function will
+ * overwrite its contents.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ */
+gboolean
+g_irepository_dump (const char *arg, GError **error)
+{
+  GHashTable *output_types;
+  char **args;
+  GFile *input_file;
+  GFile *output_file;
+  GFileInputStream *input;
+  GFileOutputStream *output;
+  GDataInputStream *in;
+  GModule *self;
+  gboolean caught_error = FALSE;
+
+  self = g_module_open (NULL, 0);
+  if (!self)
+    {
+      g_set_error (error,
+                  G_IO_ERROR,
+                  G_IO_ERROR_FAILED,
+                  "failed to open self: %s",
+                  g_module_error ());
+      return FALSE;
+    }
+
+  args = g_strsplit (arg, ",", 2);
+
+  input_file = g_file_new_for_path (args[0]);
+  output_file = g_file_new_for_path (args[1]);
+
+  input = g_file_read (input_file, NULL, error);
+  if (input == NULL)
+    return FALSE;
+
+  output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error);
+  if (output == NULL)
+    {
+      g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
+      return FALSE;
+    }
+
+  goutput_write (G_OUTPUT_STREAM (output), "<?xml version=\"1.0\"?>\n");
+  goutput_write (G_OUTPUT_STREAM (output), "<dump>\n");
+
+  output_types = g_hash_table_new (NULL, NULL);
+
+  in = g_data_input_stream_new (G_INPUT_STREAM (input));
+  g_object_unref (input);
+
+  while (TRUE)
+    {
+      gsize len;
+      char *line = g_data_input_stream_read_line (in, &len, NULL, NULL);
+      GType type;
+
+      if (line == NULL || *line == '\0')
+       {
+         g_free (line);
+         break;
+       }
+
+      g_strchomp (line);
+      type = invoke_get_type (self, line, error);
+
+      if (type == G_TYPE_INVALID)
+       {
+         caught_error = TRUE;
+         g_free (line);
+         break;
+       }
+
+      if (g_hash_table_lookup (output_types, (gpointer) type))
+       goto next;
+      g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
+
+      dump_type (type, line, G_OUTPUT_STREAM (output));
+
+    next:
+      g_free (line);
+    }
+
+  g_hash_table_destroy (output_types);
+
+  goutput_write (G_OUTPUT_STREAM (output), "</dump>\n");
+
+  {
+    GError **ioerror;
+    /* Avoid overwriting an earlier set error */
+    if (caught_error)
+      ioerror = NULL;
+    else
+      ioerror = error;
+    if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, ioerror))
+      return FALSE;
+    if (!g_output_stream_close (G_OUTPUT_STREAM (output), NULL, ioerror))
+      return FALSE;
+  }
+
+  return !caught_error;
+}
diff --git a/giscanner/dumper.py b/giscanner/dumper.py
new file mode 100644 (file)
index 0000000..066063e
--- /dev/null
@@ -0,0 +1,115 @@
+import os
+import subprocess
+import tempfile
+
+from .glibtransformer import IntrospectionBinary
+
+# bugzilla.gnome.org/558436
+# Compile a binary program which is then linked to a library
+# we want to introspect, in order to call its get_type functions.
+
+
+def mktmp(tmpdir, nsname, nsver, suffix):
+    name = '%s-%s%s' % (nsname, nsver, suffix)
+    return os.path.join(tmpdir, name)
+
+
+def compile_introspection_binary(options):
+    tmpdir = tempfile.mkdtemp('', 'tmp-introspect')
+    nsname = options.namespace_name
+    nsver = options.namespace_version
+    c_path = mktmp(tmpdir, nsname, nsver, '.c')
+    f = open(c_path, 'w')
+    f.write('''#include <glib.h>
+#include <girepository.h>
+#include <string.h>
+
+static GOptionEntry entries[] =
+{
+  { NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+  GOptionContext *context;
+  GError *error = NULL;
+
+  g_type_init ();
+  g_thread_init (NULL);
+
+  context = g_option_context_new ("");
+  g_option_context_add_main_entries (context, entries, "girepository");
+  g_option_context_add_group (context, g_irepository_get_option_group ());
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      g_printerr ("introspect failed (%d,%d): %s\\n",
+                  error->domain, error->code,
+                  error->message);
+      return 1;
+    }
+  return 0;
+}
+''')
+    f.close()
+
+    o_path = mktmp(tmpdir, nsname, nsver, '.o')
+
+    cc = os.environ.get('CC', 'gcc')
+    ld = os.environ.get('LD', cc)
+    pkgconfig = os.environ.get('PKG_CONFIG', 'pkg-config')
+    uninst_srcdir = os.environ.get('UNINSTALLED_INTROSPECTION_SRCDIR')
+    uninst_builddir = os.environ.get('UNINSTALLED_INTROSPECTION_BUILDDIR')
+
+    pkgs = ['gio-2.0 gthread-2.0']
+    if not uninst_srcdir:
+        pkgs.append('gobject-introspection-1.0')
+
+    cc_args = [cc]
+    if cc == 'gcc':
+        cc_args.append('-Wall')
+    output = subprocess.Popen([pkgconfig, '--cflags'] + pkgs,
+                              stdout=subprocess.PIPE).communicate()[0]
+    if uninst_srcdir:
+        cc_args.append('-I' + os.path.join(uninst_srcdir, 'girepository'))
+    cc_args.extend(output.split())
+    for include in options.cpp_includes:
+        cc_args.append('-I' + include)
+    cc_args.extend(['-c', '-o', o_path, c_path])
+    print "%r" % (cc_args, )
+    subprocess.check_call(cc_args)
+
+    bin_path = mktmp(tmpdir, nsname, nsver, '')
+
+    ld_args = [ld, '-o', bin_path, o_path]
+    libtool_infection = not options.nolibtool
+    if not libtool_infection:
+        try:
+            subprocess.check_call(['libtool', '--version'])
+        except subprocess.CalledProcessError, e:
+            # If libtool's not installed, assume we don't need it
+            libtool_infection = False
+    if libtool_infection:
+        ld_args.insert(0, 'libtool')
+        ld_args.insert(1, '--mode=link')
+    output = subprocess.Popen([pkgconfig, '--libs'] + pkgs,
+                              stdout=subprocess.PIPE).communicate()[0]
+    ld_args.extend(output.split())
+
+    # This hack is only for building gobject-introspection itself
+    if uninst_builddir:
+        path = os.path.join(uninst_builddir, 'girepository',
+                            'libgirepository.la')
+        ld_args.append(path)
+    # Search the current directory first
+    ld_args.append('-L.')
+
+    # We only use the first library; assume others are "custom" libraries like
+    # from gir-repository.  Right now those don't define new GTypes, so we
+    # don't need to introspect them.
+    ld_args.append('-l'+options.libraries[0])
+    print "%r" % (ld_args, )
+    subprocess.check_call(ld_args)
+
+    os.unlink(c_path)
+    return IntrospectionBinary([bin_path], tmpdir)
diff --git a/tests/scanner/barapp.c b/tests/scanner/barapp.c
new file mode 100644 (file)
index 0000000..f2b9254
--- /dev/null
@@ -0,0 +1,47 @@
+#include "barapp.h"
+
+#include <girepository.h>
+#include <string.h>
+
+G_DEFINE_TYPE(BarBaz, bar_baz, G_TYPE_OBJECT);
+
+static void
+bar_baz_class_init (BarBazClass *klass)
+{
+}
+
+static void
+bar_baz_init (BarBaz *object)
+{
+}
+
+void
+barapp_func (void)
+{
+}
+
+void
+barapp_func2 (int x, double y)
+{
+}
+
+int
+main(int argc, char **argv)
+{
+  const char *prefix = "--introspect-dump=";
+  GError *error = NULL;
+  if (!(argc == 2 && g_str_has_prefix (argv[1], prefix)))
+    {
+      g_printerr ("usage: barapp --introspect-dump=types.txt,out.xml\\n");
+      return 1;
+    }
+  g_type_init ();
+  g_thread_init (NULL);
+
+  if (!g_irepository_dump (argv[1] + strlen (prefix), &error))
+    {
+      g_printerr ("%s\n", error->message);
+      return 1;
+    }
+  return 0;
+}
diff --git a/tests/scanner/barapp.h b/tests/scanner/barapp.h
new file mode 100644 (file)
index 0000000..6e09b8a
--- /dev/null
@@ -0,0 +1,24 @@
+
+#include <glib-object.h>
+
+#define BAR_TYPE_BAZ              (bar_baz_get_type ())
+#define BAR_BAZ(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), BAR_TYPE_BAZ, BarBaz))
+#define BAR_IS_BAZ(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), BAR_TYPE_BAZ))
+
+typedef struct BarBaz          BarBaz;
+typedef struct BarBazClass     BarBazClass;
+
+struct BarBaz
+{
+  GObject parent_instance;
+};
+
+struct BarBazClass
+{
+  GObjectClass parent_class;
+};
+
+GType bar_baz_get_type          (void) G_GNUC_CONST;
+
+void barapp_func (void);
+void barapp_func2 (int x, double y);