]> git.0d.be Git - empathy.git/blobdiff - tools/glib-client-gen.py
Merge branch 'gnome-3-6'
[empathy.git] / tools / glib-client-gen.py
index 701fcafeb24cba428d8d2502add470fc96ddd615..e68e1a575f6260db90487f44f60708bd6b34aa28 100644 (file)
@@ -27,8 +27,9 @@ import os.path
 import xml.dom.minidom
 from getopt import gnu_getopt
 
-from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
-        camelcase_to_lower, get_docstring, xml_escape
+from libtpcodegen import file_set_contents, key_by_name, u
+from libglibcodegen import Signature, type_to_gtype, \
+        get_docstring, xml_escape, get_deprecated
 
 
 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
@@ -39,6 +40,7 @@ class Generator(object):
         self.dom = dom
         self.__header = []
         self.__body = []
+        self.__docs = []
 
         self.prefix_lc = prefix.lower()
         self.prefix_uc = prefix.upper()
@@ -56,16 +58,30 @@ class Generator(object):
         if self.proxy_arg == 'void *':
             self.proxy_arg = 'gpointer '
 
+        self.reentrant_symbols = set()
+        try:
+            filename = opts['--generate-reentrant']
+            with open(filename, 'r') as f:
+                for line in f.readlines():
+                    self.reentrant_symbols.add(line.strip())
+        except KeyError:
+            pass
+
+        self.deprecate_reentrant = opts.get('--deprecate-reentrant', None)
+        self.deprecation_attribute = opts.get('--deprecation-attribute',
+                'G_GNUC_DEPRECATED')
+
+        self.guard = opts.get('--guard', None)
+
     def h(self, s):
-        if isinstance(s, unicode):
-            s = s.encode('utf-8')
         self.__header.append(s)
 
     def b(self, s):
-        if isinstance(s, unicode):
-            s = s.encode('utf-8')
         self.__body.append(s)
 
+    def d(self, s):
+        self.__docs.append(s)
+
     def get_iface_quark(self):
         assert self.iface_dbus is not None
         assert self.iface_uc is not None
@@ -78,7 +94,11 @@ class Generator(object):
         iface_lc = iface.lower()
 
         member = signal.getAttribute('name')
-        member_lc = camelcase_to_lower(member)
+        member_lc = signal.getAttribute('tp:name-for-bindings')
+        if member != member_lc.replace('_', ''):
+            raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
+                    'not match' % (member, member_lc))
+        member_lc = member_lc.lower()
         member_uc = member_lc.upper()
 
         arg_count = 0
@@ -114,25 +134,31 @@ class Generator(object):
         #   guint arg_handle, gboolean arg_suppress_handler,
         #   gpointer user_data, GObject *weak_object);
 
-        self.b('/**')
-        self.b(' * %s:' % callback_name)
-        self.b(' * @proxy: The proxy on which %s_%s_connect_to_%s ()'
+        self.d('/**')
+        self.d(' * %s:' % callback_name)
+        self.d(' * @proxy: The proxy on which %s_%s_connect_to_%s ()'
                % (self.prefix_lc, iface_lc, member_lc))
-        self.b(' *  was called')
+        self.d(' *  was called')
 
         for arg in args:
             name, info, tp_type, elt = arg
             ctype, gtype, marshaller, pointer = info
 
-            self.b(' * @%s: %s' % (name,
-                xml_escape(get_docstring(elt) or '(Undocumented)')))
+            docs = get_docstring(elt) or '(Undocumented)'
+
+            if ctype == 'guint ' and tp_type != '':
+                docs +=  ' (#%s)' % ('Tp' + tp_type.replace('_', ''))
 
-        self.b(' * @user_data: User-supplied data')
-        self.b(' * @weak_object: User-supplied weakly referenced object')
-        self.b(' *')
-        self.b(' * Represents the signature of a callback for the signal %s.'
+            self.d(' * @%s: %s' % (name, xml_escape(docs)))
+
+        self.d(' * @user_data: User-supplied data')
+        self.d(' * @weak_object: User-supplied weakly referenced object')
+        self.d(' *')
+        self.d(' * Represents the signature of a callback for the signal %s.'
                % member)
-        self.b(' */')
+        self.d(' */')
+        self.d('')
+
         self.h('typedef void (*%s) (%sproxy,'
                % (callback_name, self.proxy_cls))
 
@@ -281,31 +307,33 @@ class Generator(object):
         # emitted the 'invalidated' signal, or because the weakly referenced
         # object has gone away.
 
-        self.b('/**')
-        self.b(' * %s_%s_connect_to_%s:'
+        self.d('/**')
+        self.d(' * %s_%s_connect_to_%s:'
                % (self.prefix_lc, iface_lc, member_lc))
-        self.b(' * @proxy: %s' % self.proxy_doc)
-        self.b(' * @callback: Callback to be called when the signal is')
-        self.b(' *   received')
-        self.b(' * @user_data: User-supplied data for the callback')
-        self.b(' * @destroy: Destructor for the user-supplied data, which')
-        self.b(' *   will be called when this signal is disconnected, or')
-        self.b(' *   before this function returns %NULL')
-        self.b(' * @weak_object: A #GObject which will be weakly referenced; ')
-        self.b(' *   if it is destroyed, this callback will automatically be')
-        self.b(' *   disconnected')
-        self.b(' * @error: If not %NULL, used to raise an error if %NULL is')
-        self.b(' *   returned')
-        self.b(' *')
-        self.b(' * Connect a handler to the signal %s.' % member)
-        self.b(' *')
-        self.b(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)'))
-        self.b(' *')
-        self.b(' * Returns: a #TpProxySignalConnection containing all of the')
-        self.b(' * above, which can be used to disconnect the signal; or')
-        self.b(' * %NULL if the proxy does not have the desired interface')
-        self.b(' * or has become invalid.')
-        self.b(' */')
+        self.d(' * @proxy: %s' % self.proxy_doc)
+        self.d(' * @callback: Callback to be called when the signal is')
+        self.d(' *   received')
+        self.d(' * @user_data: User-supplied data for the callback')
+        self.d(' * @destroy: Destructor for the user-supplied data, which')
+        self.d(' *   will be called when this signal is disconnected, or')
+        self.d(' *   before this function returns %NULL')
+        self.d(' * @weak_object: A #GObject which will be weakly referenced; ')
+        self.d(' *   if it is destroyed, this callback will automatically be')
+        self.d(' *   disconnected')
+        self.d(' * @error: If not %NULL, used to raise an error if %NULL is')
+        self.d(' *   returned')
+        self.d(' *')
+        self.d(' * Connect a handler to the signal %s.' % member)
+        self.d(' *')
+        self.d(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)'))
+        self.d(' *')
+        self.d(' * Returns: a #TpProxySignalConnection containing all of the')
+        self.d(' * above, which can be used to disconnect the signal; or')
+        self.d(' * %NULL if the proxy does not have the desired interface')
+        self.d(' * or has become invalid.')
+        self.d(' */')
+        self.d('')
+
         self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,'
                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
         self.h('    %s callback,' % callback_name)
@@ -313,6 +341,7 @@ class Generator(object):
         self.h('    GDestroyNotify destroy,')
         self.h('    GObject *weak_object,')
         self.h('    GError **error);')
+        self.h('')
 
         self.b('TpProxySignalConnection *')
         self.b('%s_%s_connect_to_%s (%sproxy,'
@@ -352,13 +381,15 @@ class Generator(object):
         self.b('}')
         self.b('')
 
-        self.h('')
-
     def do_method(self, iface, method):
         iface_lc = iface.lower()
 
         member = method.getAttribute('name')
-        member_lc = camelcase_to_lower(member)
+        member_lc = method.getAttribute('tp:name-for-bindings')
+        if member != member_lc.replace('_', ''):
+            raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+                    'not match' % (member, member_lc))
+        member_lc = member_lc.lower()
         member_uc = member_lc.upper()
 
         in_count = 0
@@ -401,27 +432,40 @@ class Generator(object):
         #       gpointer user_data,
         #       GObject *weak_object);
 
-        self.b('/**')
-        self.b(' * %s_%s_callback_for_%s:'
+        self.d('/**')
+        self.d(' * %s_%s_callback_for_%s:'
                % (self.prefix_lc, iface_lc, member_lc))
-        self.b(' * @proxy: the proxy on which the call was made')
+        self.d(' * @proxy: the proxy on which the call was made')
 
         for arg in out_args:
             name, info, tp_type, elt = arg
             ctype, gtype, marshaller, pointer = info
 
-            self.b(' * @%s: Used to return an \'out\' argument if @error is '
+            docs = xml_escape(get_docstring(elt) or '(Undocumented)')
+
+            if ctype == 'guint ' and tp_type != '':
+                docs +=  ' (#%s)' % ('Tp' + tp_type.replace('_', ''))
+
+            self.d(' * @%s: Used to return an \'out\' argument if @error is '
                    '%%NULL: %s'
-                   % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
+                   % (name, docs))
 
-        self.b(' * @error: %NULL on success, or an error on failure')
-        self.b(' * @user_data: user-supplied data')
-        self.b(' * @weak_object: user-supplied object')
-        self.b(' *')
-        self.b(' * Signature of the callback called when a %s method call'
+        self.d(' * @error: %NULL on success, or an error on failure')
+        self.d(' * @user_data: user-supplied data')
+        self.d(' * @weak_object: user-supplied object')
+        self.d(' *')
+        self.d(' * Signature of the callback called when a %s method call'
                % member)
-        self.b(' * succeeds or fails.')
-        self.b(' */')
+        self.d(' * succeeds or fails.')
+
+        deprecated = method.getElementsByTagName('tp:deprecated')
+        if deprecated:
+            d = deprecated[0]
+            self.d(' *')
+            self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d)))
+
+        self.d(' */')
+        self.d('')
 
         callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc,
                                                    member_lc)
@@ -646,42 +690,56 @@ class Generator(object):
                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
         self.h('    gint timeout_ms,')
 
-        self.b('/**')
-        self.b(' * %s_%s_call_%s:'
+        self.d('/**')
+        self.d(' * %s_%s_call_%s:'
                % (self.prefix_lc, iface_lc, member_lc))
-        self.b(' * @proxy: the #TpProxy')
-        self.b(' * @timeout_ms: the timeout in milliseconds, or -1 to use the')
-        self.b(' *   default')
+        self.d(' * @proxy: the #TpProxy')
+        self.d(' * @timeout_ms: the timeout in milliseconds, or -1 to use the')
+        self.d(' *   default')
 
         for arg in in_args:
             name, info, tp_type, elt = arg
             ctype, gtype, marshaller, pointer = info
 
-            self.b(' * @%s: Used to pass an \'in\' argument: %s'
-                   % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
+            docs = xml_escape(get_docstring(elt) or '(Undocumented)')
+
+            if ctype == 'guint ' and tp_type != '':
+                docs +=  ' (#%s)' % ('Tp' + tp_type.replace('_', ''))
+
+            self.d(' * @%s: Used to pass an \'in\' argument: %s'
+                   % (name, docs))
+
+        self.d(' * @callback: called when the method call succeeds or fails;')
+        self.d(' *   may be %NULL to make a "fire and forget" call with no ')
+        self.d(' *   reply tracking')
+        self.d(' * @user_data: user-supplied data passed to the callback;')
+        self.d(' *   must be %NULL if @callback is %NULL')
+        self.d(' * @destroy: called with the user_data as argument, after the')
+        self.d(' *   call has succeeded, failed or been cancelled;')
+        self.d(' *   must be %NULL if @callback is %NULL')
+        self.d(' * @weak_object: If not %NULL, a #GObject which will be ')
+        self.d(' *   weakly referenced; if it is destroyed, this call ')
+        self.d(' *   will automatically be cancelled. Must be %NULL if ')
+        self.d(' *   @callback is %NULL')
+        self.d(' *')
+        self.d(' * Start a %s method call.' % member)
+        self.d(' *')
+        self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
+        self.d(' *')
+        self.d(' * Returns: a #TpProxyPendingCall representing the call in')
+        self.d(' *  progress. It is borrowed from the object, and will become')
+        self.d(' *  invalid when the callback is called, the call is')
+        self.d(' *  cancelled or the #TpProxy becomes invalid.')
+
+        deprecated = method.getElementsByTagName('tp:deprecated')
+        if deprecated:
+            d = deprecated[0]
+            self.d(' *')
+            self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d)))
+
+        self.d(' */')
+        self.d('')
 
-        self.b(' * @callback: called when the method call succeeds or fails;')
-        self.b(' *   may be %NULL to make a "fire and forget" call with no ')
-        self.b(' *   reply tracking')
-        self.b(' * @user_data: user-supplied data passed to the callback;')
-        self.b(' *   must be %NULL if @callback is %NULL')
-        self.b(' * @destroy: called with the user_data as argument, after the')
-        self.b(' *   call has succeeded, failed or been cancelled;')
-        self.b(' *   must be %NULL if @callback is %NULL')
-        self.b(' * @weak_object: If not %NULL, a #GObject which will be ')
-        self.b(' *   weakly referenced; if it is destroyed, this call ')
-        self.b(' *   will automatically be cancelled. Must be %NULL if ')
-        self.b(' *   @callback is %NULL')
-        self.b(' *')
-        self.b(' * Start a %s method call.' % member)
-        self.b(' *')
-        self.b(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
-        self.b(' *')
-        self.b(' * Returns: a #TpProxyPendingCall representing the call in')
-        self.b(' *  progress. It is borrowed from the object, and will become')
-        self.b(' *  invalid when the callback is called, the call is')
-        self.b(' *  cancelled or the #TpProxy becomes invalid.')
-        self.b(' */')
         self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,'
                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
         self.b('    gint timeout_ms,')
@@ -719,9 +777,11 @@ class Generator(object):
         self.b('  g_return_val_if_fail (callback != NULL || '
                'weak_object == NULL, NULL);')
         self.b('')
+        self.b('  G_GNUC_BEGIN_IGNORE_DEPRECATIONS')
         self.b('  iface = tp_proxy_borrow_interface_by_id (')
         self.b('      (TpProxy *) proxy,')
         self.b('      interface, &error);')
+        self.b('  G_GNUC_END_IGNORE_DEPRECATIONS')
         self.b('')
         self.b('  if (iface == NULL)')
         self.b('    {')
@@ -793,6 +853,16 @@ class Generator(object):
         self.b('}')
         self.b('')
 
+        self.do_method_reentrant(method, iface_lc, member, member_lc,
+                                 in_args, out_args, collect_callback)
+
+        # leave a gap for the end of the method
+        self.d('')
+        self.b('')
+        self.h('')
+
+    def do_method_reentrant(self, method, iface_lc, member, member_lc, in_args,
+            out_args, collect_callback):
         # Reentrant blocking calls
         # Example:
         # gboolean tp_cli_properties_interface_run_get_properties
@@ -803,6 +873,10 @@ class Generator(object):
         #       GError **error,
         #       GMainLoop **loop);
 
+        run_method_name = '%s_%s_run_%s' % (self.prefix_lc, iface_lc, member_lc)
+        if run_method_name not in self.reentrant_symbols:
+            return
+
         self.b('typedef struct {')
         self.b('    GMainLoop *loop;')
         self.b('    GError **error;')
@@ -877,50 +951,67 @@ class Generator(object):
         self.b('}')
         self.b('')
 
-        self.h('gboolean %s_%s_run_%s (%sproxy,'
-               % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
+        if self.deprecate_reentrant:
+            self.h('#ifndef %s' % self.deprecate_reentrant)
+
+        self.h('gboolean %s (%sproxy,'
+               % (run_method_name, self.proxy_arg))
         self.h('    gint timeout_ms,')
 
-        self.b('/**')
-        self.b(' * %s_%s_run_%s:' % (self.prefix_lc, iface_lc, member_lc))
-        self.b(' * @proxy: %s' % self.proxy_doc)
-        self.b(' * @timeout_ms: Timeout in milliseconds, or -1 for default')
+        self.d('/**')
+        self.d(' * %s:' % run_method_name)
+        self.d(' * @proxy: %s' % self.proxy_doc)
+        self.d(' * @timeout_ms: Timeout in milliseconds, or -1 for default')
 
         for arg in in_args:
             name, info, tp_type, elt = arg
             ctype, gtype, marshaller, pointer = info
 
-            self.b(' * @%s: Used to pass an \'in\' argument: %s'
-                   % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
+            docs = xml_escape(get_docstring(elt) or '(Undocumented)')
+
+            if ctype == 'guint ' and tp_type != '':
+                docs +=  ' (#%s)' % ('Tp' + tp_type.replace('_', ''))
+
+            self.d(' * @%s: Used to pass an \'in\' argument: %s'
+                   % (name, docs))
 
         for arg in out_args:
             name, info, tp_type, elt = arg
             ctype, gtype, marshaller, pointer = info
 
-            self.b(' * @%s: Used to return an \'out\' argument if %%TRUE is '
+            self.d(' * @%s: Used to return an \'out\' argument if %%TRUE is '
                    'returned: %s'
                    % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
 
-        self.b(' * @error: If not %NULL, used to return errors if %FALSE ')
-        self.b(' *  is returned')
-        self.b(' * @loop: If not %NULL, set before re-entering ')
-        self.b(' *  the main loop, to point to a #GMainLoop ')
-        self.b(' *  which can be used to cancel this call with ')
-        self.b(' *  g_main_loop_quit(), causing a return of ')
-        self.b(' *  %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED')
-        self.b(' *')
-        self.b(' * Call the method %s and run the main loop' % member)
-        self.b(' * until it returns. Before calling this method, you must')
-        self.b(' * add a reference to any borrowed objects you need to keep,')
-        self.b(' * and generally ensure that everything is in a consistent')
-        self.b(' * state.')
-        self.b(' *')
-        self.b(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
-        self.b(' *')
-        self.b(' * Returns: TRUE on success, FALSE and sets @error on error')
-        self.b(' */')
-        self.b('gboolean\n%s_%s_run_%s (%sproxy,'
-               % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
+        self.d(' * @error: If not %NULL, used to return errors if %FALSE ')
+        self.d(' *  is returned')
+        self.d(' * @loop: If not %NULL, set before re-entering ')
+        self.d(' *  the main loop, to point to a #GMainLoop ')
+        self.d(' *  which can be used to cancel this call with ')
+        self.d(' *  g_main_loop_quit(), causing a return of ')
+        self.d(' *  %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED')
+        self.d(' *')
+        self.d(' * Call the method %s and run the main loop' % member)
+        self.d(' * until it returns. Before calling this method, you must')
+        self.d(' * add a reference to any borrowed objects you need to keep,')
+        self.d(' * and generally ensure that everything is in a consistent')
+        self.d(' * state.')
+        self.d(' *')
+        self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
+        self.d(' *')
+        self.d(' * Returns: TRUE on success, FALSE and sets @error on error')
+
+        deprecated = method.getElementsByTagName('tp:deprecated')
+        if deprecated:
+            d = deprecated[0]
+            self.d(' *')
+            self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d)))
+
+        self.d(' */')
+        self.d('')
+
+        self.b('gboolean\n%s (%sproxy,'
+               % (run_method_name, self.proxy_arg))
         self.b('    gint timeout_ms,')
 
         for arg in in_args:
@@ -940,7 +1031,13 @@ class Generator(object):
             self.b('    %s*%s,' % (ctype, name))
 
         self.h('    GError **error,')
-        self.h('    GMainLoop **loop);')
+
+        if self.deprecate_reentrant:
+            self.h('    GMainLoop **loop) %s;' % self.deprecation_attribute)
+            self.h('#endif /* not %s */' % self.deprecate_reentrant)
+        else:
+            self.h('    GMainLoop **loop);')
+
         self.h('')
 
         self.b('    GError **error,')
@@ -963,8 +1060,10 @@ class Generator(object):
         self.b('  g_return_val_if_fail (%s (proxy), FALSE);'
                % self.proxy_assert)
         self.b('')
+        self.b('  G_GNUC_BEGIN_IGNORE_DEPRECATIONS')
         self.b('  iface = tp_proxy_borrow_interface_by_id')
         self.b('       ((TpProxy *) proxy, interface, error);')
+        self.b('  G_GNUC_END_IGNORE_DEPRECATIONS')
         self.b('')
         self.b('  if (iface == NULL)')
         self.b('    return FALSE;')
@@ -1012,10 +1111,6 @@ class Generator(object):
         self.b('}')
         self.b('')
 
-        # leave a gap for the end of the method
-        self.b('')
-        self.h('')
-
     def do_signal_add(self, signal):
         marshaller_items = []
         gtypes = []
@@ -1076,6 +1171,11 @@ class Generator(object):
 
     def __call__(self):
 
+        if self.guard is not None:
+            self.h('#ifndef %s' % self.guard)
+            self.h('#define %s' % self.guard)
+            self.h('')
+
         self.h('G_BEGIN_DECLS')
         self.h('')
 
@@ -1085,7 +1185,7 @@ class Generator(object):
         self.b('')
 
         nodes = self.dom.getElementsByTagName('node')
-        nodes.sort(cmp_by_name)
+        nodes.sort(key=key_by_name)
 
         for node in nodes:
             self.do_interface(node)
@@ -1134,9 +1234,13 @@ class Generator(object):
         self.h('G_END_DECLS')
         self.h('')
 
-        open(self.basename + '.h', 'w').write('\n'.join(self.__header))
-        open(self.basename + '-body.h', 'w').write('\n'.join(self.__body))
+        if self.guard is not None:
+            self.h('#endif /* defined (%s) */' % self.guard)
+            self.h('')
 
+        file_set_contents(self.basename + '.h', u('\n').join(self.__header).encode('utf-8'))
+        file_set_contents(self.basename + '-body.h', u('\n').join(self.__body).encode('utf-8'))
+        file_set_contents(self.basename + '-gtk-doc.h', u('\n').join(self.__docs).encode('utf-8'))
 
 def types_to_gtypes(types):
     return [type_to_gtype(t)[1] for t in types]
@@ -1145,7 +1249,9 @@ def types_to_gtypes(types):
 if __name__ == '__main__':
     options, argv = gnu_getopt(sys.argv[1:], '',
                                ['group=', 'subclass=', 'subclass-assert=',
-                                'iface-quark-prefix=', 'tp-proxy-api='])
+                                'iface-quark-prefix=', 'tp-proxy-api=',
+                                'generate-reentrant=', 'deprecate-reentrant=',
+                                'deprecation-attribute=', 'guard='])
 
     opts = {}