File indexing completed on 2024-04-28 16:13:13

0001 #!/usr/bin/python
0002 
0003 # glib-ginterface-gen.py: service-side interface generator
0004 #
0005 # Generate dbus-glib 0.x service GInterfaces from the Telepathy specification.
0006 # The master copy of this program is in the telepathy-glib repository -
0007 # please make any changes there.
0008 #
0009 # Copyright (C) 2006, 2007 Collabora Limited
0010 #
0011 # This library is free software; you can redistribute it and/or
0012 # modify it under the terms of the GNU Lesser General Public
0013 # License as published by the Free Software Foundation; either
0014 # version 2.1 of the License, or (at your option) any later version.
0015 #
0016 # This library is distributed in the hope that it will be useful,
0017 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0018 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0019 # Lesser General Public License for more details.
0020 #
0021 # You should have received a copy of the GNU Lesser General Public
0022 # License along with this library; if not, write to the Free Software
0023 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0024 
0025 import sys
0026 import os.path
0027 import xml.dom.minidom
0028 
0029 from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
0030         NS_TP, dbus_gutils_wincaps_to_uscore, \
0031         signal_to_marshal_name, method_to_glue_marshal_name
0032 
0033 
0034 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
0035 
0036 class Generator(object):
0037 
0038     def __init__(self, dom, prefix, basename, signal_marshal_prefix,
0039                  headers, end_headers, not_implemented_func,
0040                  allow_havoc):
0041         self.dom = dom
0042         self.__header = []
0043         self.__body = []
0044 
0045         assert prefix.endswith('_')
0046         assert not signal_marshal_prefix.endswith('_')
0047 
0048         # The main_prefix, sub_prefix thing is to get:
0049         # FOO_ -> (FOO_, _)
0050         # FOO_SVC_ -> (FOO_, _SVC_)
0051         # but
0052         # FOO_BAR/ -> (FOO_BAR_, _)
0053         # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_)
0054 
0055         if '/' in prefix:
0056             main_prefix, sub_prefix = prefix.upper().split('/', 1)
0057             prefix = prefix.replace('/', '_')
0058         else:
0059             main_prefix, sub_prefix = prefix.upper().split('_', 1)
0060 
0061         self.MAIN_PREFIX_ = main_prefix + '_'
0062         self._SUB_PREFIX_ = '_' + sub_prefix
0063 
0064         self.Prefix_ = prefix
0065         self.Prefix = prefix.replace('_', '')
0066         self.prefix_ = prefix.lower()
0067         self.PREFIX_ = prefix.upper()
0068 
0069         self.basename = basename
0070         self.signal_marshal_prefix = signal_marshal_prefix
0071         self.headers = headers
0072         self.end_headers = end_headers
0073         self.not_implemented_func = not_implemented_func
0074         self.allow_havoc = allow_havoc
0075 
0076     def h(self, s):
0077         self.__header.append(s)
0078 
0079     def b(self, s):
0080         self.__body.append(s)
0081 
0082     def do_node(self, node):
0083         node_name = node.getAttribute('name').replace('/', '')
0084         node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
0085         node_name_lc = self.node_name_lc = node_name.lower()
0086         node_name_uc = self.node_name_uc = node_name.upper()
0087 
0088         interfaces = node.getElementsByTagName('interface')
0089         assert len(interfaces) == 1, interfaces
0090         interface = interfaces[0]
0091         self.iface_name = interface.getAttribute('name')
0092 
0093         tmp = interface.getAttribute('tp:implement-service')
0094         if tmp == "no":
0095             return
0096 
0097         tmp = interface.getAttribute('tp:causes-havoc')
0098         if tmp and not self.allow_havoc:
0099             raise AssertionError('%s is %s' % (self.iface_name, tmp))
0100 
0101         self.b('static const DBusGObjectInfo _%s%s_object_info;'
0102                % (self.prefix_, node_name_lc))
0103         self.b('')
0104 
0105         methods = interface.getElementsByTagName('method')
0106         signals = interface.getElementsByTagName('signal')
0107         properties = interface.getElementsByTagName('property')
0108         # Don't put properties in dbus-glib glue
0109         glue_properties = []
0110 
0111         self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
0112         self.b('    GTypeInterface parent_class;')
0113         for method in methods:
0114             self.b('    %s %s;' % self.get_method_impl_names(method))
0115         self.b('};')
0116         self.b('')
0117 
0118         if signals:
0119             self.b('enum {')
0120             for signal in signals:
0121                 self.b('    %s,' % self.get_signal_const_entry(signal))
0122             self.b('    N_%s_SIGNALS' % node_name_uc)
0123             self.b('};')
0124             self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
0125                    % (node_name_lc, node_name_uc))
0126             self.b('')
0127 
0128         self.b('static void %s%s_base_init (gpointer klass);'
0129                % (self.prefix_, node_name_lc))
0130         self.b('')
0131 
0132         self.b('GType')
0133         self.b('%s%s_get_type (void)'
0134                % (self.prefix_, node_name_lc))
0135         self.b('{')
0136         self.b('  static GType type = 0;')
0137         self.b('')
0138         self.b('  if (G_UNLIKELY (type == 0))')
0139         self.b('    {')
0140         self.b('      static const GTypeInfo info = {')
0141         self.b('        sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
0142         self.b('        %s%s_base_init, /* base_init */'
0143                % (self.prefix_, node_name_lc))
0144         self.b('        NULL, /* base_finalize */')
0145         self.b('        NULL, /* class_init */')
0146         self.b('        NULL, /* class_finalize */')
0147         self.b('        NULL, /* class_data */')
0148         self.b('        0,')
0149         self.b('        0, /* n_preallocs */')
0150         self.b('        NULL /* instance_init */')
0151         self.b('      };')
0152         self.b('')
0153         self.b('      type = g_type_register_static (G_TYPE_INTERFACE,')
0154         self.b('          "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
0155         self.b('    }')
0156         self.b('')
0157         self.b('  return type;')
0158         self.b('}')
0159         self.b('')
0160 
0161         self.h('/**')
0162         self.h(' * %s%s:' % (self.Prefix, node_name_mixed))
0163         self.h(' *')
0164         self.h(' * Dummy typedef representing any implementation of this '
0165                'interface.')
0166         self.h(' */')
0167         self.h('typedef struct _%s%s %s%s;'
0168                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
0169         self.h('')
0170         self.h('/**')
0171         self.h(' * %s%sClass:' % (self.Prefix, node_name_mixed))
0172         self.h(' *')
0173         self.h(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
0174 
0175         if methods:
0176             self.h(' *')
0177             self.h(' * In a full implementation of this interface (i.e. all')
0178             self.h(' * methods implemented), the interface initialization')
0179             self.h(' * function used in G_IMPLEMENT_INTERFACE() would')
0180             self.h(' * typically look like this:')
0181             self.h(' *')
0182             self.h(' * <programlisting>')
0183             self.h(' * static void')
0184             self.h(' * implement_%s (gpointer klass,' % self.node_name_lc)
0185             self.h(' *     gpointer unused G_GNUC_UNUSED)')
0186             self.h(' * {')
0187             # "#" is special to gtkdoc under some circumstances; it appears
0188             # that escaping "##" as "#<!---->#" or "&#35;&#35;" doesn't work,
0189             # but adding an extra hash symbol does. Thanks, gtkdoc :-(
0190             self.h(' * #define IMPLEMENT(x) %s%s_implement_###x (\\'
0191                    % (self.prefix_, self.node_name_lc))
0192             self.h(' *   klass, my_object_###x)')
0193 
0194             for method in methods:
0195                 class_member_name = method.getAttribute('tp:name-for-bindings')
0196                 class_member_name = class_member_name.lower()
0197                 self.h(' *   IMPLEMENT (%s);' % class_member_name)
0198 
0199             self.h(' * #undef IMPLEMENT')
0200             self.h(' * }')
0201             self.h(' * </programlisting>')
0202         else:
0203             self.h(' * This interface has no D-Bus methods, so an')
0204             self.h(' * implementation can typically pass %NULL to')
0205             self.h(' * G_IMPLEMENT_INTERFACE() as the interface')
0206             self.h(' * initialization function.')
0207 
0208         self.h(' */')
0209 
0210         self.h('typedef struct _%s%sClass %s%sClass;'
0211                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
0212         self.h('')
0213         self.h('GType %s%s_get_type (void);'
0214                % (self.prefix_, node_name_lc))
0215 
0216         gtype = self.current_gtype = \
0217                 self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
0218         classname = self.Prefix + node_name_mixed
0219 
0220         self.h('#define %s \\\n  (%s%s_get_type ())'
0221                % (gtype, self.prefix_, node_name_lc))
0222         self.h('#define %s%s(obj) \\\n'
0223                '  (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
0224                % (self.PREFIX_, node_name_uc, gtype, classname))
0225         self.h('#define %sIS%s%s(obj) \\\n'
0226                '  (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
0227                % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
0228         self.h('#define %s%s_GET_CLASS(obj) \\\n'
0229                '  (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
0230                % (self.PREFIX_, node_name_uc, gtype, classname))
0231         self.h('')
0232         self.h('')
0233 
0234         base_init_code = []
0235 
0236         for method in methods:
0237             self.do_method(method)
0238 
0239         for signal in signals:
0240             base_init_code.extend(self.do_signal(signal))
0241 
0242         self.b('static inline void')
0243         self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)'
0244                % (self.prefix_, node_name_lc))
0245         self.b('{')
0246 
0247         if properties:
0248             self.b('  static TpDBusPropertiesMixinPropInfo properties[%d] = {'
0249                    % (len(properties) + 1))
0250 
0251             for m in properties:
0252                 access = m.getAttribute('access')
0253                 assert access in ('read', 'write', 'readwrite')
0254 
0255                 if access == 'read':
0256                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
0257                 elif access == 'write':
0258                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
0259                 else:
0260                     flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
0261                              'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
0262 
0263                 self.b('      { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
0264                        % (flags, m.getAttribute('type'), m.getAttribute('name')))
0265 
0266             self.b('      { 0, 0, NULL, 0, NULL, NULL }')
0267             self.b('  };')
0268             self.b('  static TpDBusPropertiesMixinIfaceInfo interface =')
0269             self.b('      { 0, properties, NULL, NULL };')
0270             self.b('')
0271 
0272 
0273         self.b('  dbus_g_object_type_install_info (%s%s_get_type (),'
0274                % (self.prefix_, node_name_lc))
0275         self.b('      &_%s%s_object_info);'
0276                % (self.prefix_, node_name_lc))
0277         self.b('')
0278 
0279         if properties:
0280             self.b('  interface.dbus_interface = g_quark_from_static_string '
0281                    '("%s");' % self.iface_name)
0282 
0283             for i, m in enumerate(properties):
0284                 self.b('  properties[%d].name = g_quark_from_static_string ("%s");'
0285                        % (i, m.getAttribute('name')))
0286                 self.b('  properties[%d].type = %s;'
0287                            % (i, type_to_gtype(m.getAttribute('type'))[1]))
0288 
0289             self.b('  tp_svc_interface_set_dbus_properties_info (%s, &interface);'
0290                    % self.current_gtype)
0291 
0292             self.b('')
0293 
0294         for s in base_init_code:
0295             self.b(s)
0296         self.b('}')
0297 
0298         self.b('static void')
0299         self.b('%s%s_base_init (gpointer klass)'
0300                % (self.prefix_, node_name_lc))
0301         self.b('{')
0302         self.b('  static gboolean initialized = FALSE;')
0303         self.b('')
0304         self.b('  if (!initialized)')
0305         self.b('    {')
0306         self.b('      initialized = TRUE;')
0307         self.b('      %s%s_base_init_once (klass);'
0308                % (self.prefix_, node_name_lc))
0309         self.b('    }')
0310         # insert anything we need to do per implementation here
0311         self.b('}')
0312 
0313         self.h('')
0314 
0315         self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
0316                % (self.prefix_, node_name_lc))
0317 
0318         method_blob, offsets = self.get_method_glue(methods)
0319 
0320         for method, offset in zip(methods, offsets):
0321             self.do_method_glue(method, offset)
0322 
0323         if len(methods) == 0:
0324             # empty arrays are a gcc extension, so put in a dummy member
0325             self.b("  { NULL, NULL, 0 }")
0326 
0327         self.b('};')
0328         self.b('')
0329 
0330         self.b('static const DBusGObjectInfo _%s%s_object_info = {'
0331                % (self.prefix_, node_name_lc))
0332         self.b('  0,')  # version
0333         self.b('  _%s%s_methods,' % (self.prefix_, node_name_lc))
0334         self.b('  %d,' % len(methods))
0335         self.b('"' + method_blob.replace('\0', '\\0') + '",')
0336         self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
0337         self.b('"' +
0338                self.get_property_glue(glue_properties).replace('\0', '\\0') +
0339                '",')
0340         self.b('};')
0341         self.b('')
0342 
0343         self.node_name_mixed = None
0344         self.node_name_lc = None
0345         self.node_name_uc = None
0346 
0347     def get_method_glue(self, methods):
0348         info = []
0349         offsets = []
0350 
0351         for method in methods:
0352             offsets.append(len(''.join(info)))
0353 
0354             info.append(self.iface_name + '\0')
0355             info.append(method.getAttribute('name') + '\0')
0356 
0357             info.append('A\0')    # async
0358 
0359             counter = 0
0360             for arg in method.getElementsByTagName('arg'):
0361                 out = arg.getAttribute('direction') == 'out'
0362 
0363                 name = arg.getAttribute('name')
0364                 if not name:
0365                     assert out
0366                     name = 'arg%u' % counter
0367                 counter += 1
0368 
0369                 info.append(name + '\0')
0370 
0371                 if out:
0372                     info.append('O\0')
0373                 else:
0374                     info.append('I\0')
0375 
0376                 if out:
0377                     info.append('F\0')    # not const
0378                     info.append('N\0')    # not error or return
0379                 info.append(arg.getAttribute('type') + '\0')
0380 
0381             info.append('\0')
0382 
0383         return ''.join(info) + '\0', offsets
0384 
0385     def do_method_glue(self, method, offset):
0386         lc_name = method.getAttribute('tp:name-for-bindings')
0387         if method.getAttribute('name') != lc_name.replace('_', ''):
0388             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
0389                     'not match' % (method.getAttribute('name'), lc_name))
0390         lc_name = lc_name.lower()
0391 
0392         marshaller = method_to_glue_marshal_name(method,
0393                 self.signal_marshal_prefix)
0394         wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
0395 
0396         self.b("  { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
0397 
0398     def get_signal_glue(self, signals):
0399         info = []
0400 
0401         for signal in signals:
0402             info.append(self.iface_name)
0403             info.append(signal.getAttribute('name'))
0404 
0405         return '\0'.join(info) + '\0\0'
0406 
0407     # the implementation can be the same
0408     get_property_glue = get_signal_glue
0409 
0410     def get_method_impl_names(self, method):
0411         dbus_method_name = method.getAttribute('name')
0412 
0413         class_member_name = method.getAttribute('tp:name-for-bindings')
0414         if dbus_method_name != class_member_name.replace('_', ''):
0415             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
0416                     'not match' % (dbus_method_name, class_member_name))
0417         class_member_name = class_member_name.lower()
0418 
0419         stub_name = (self.prefix_ + self.node_name_lc + '_' +
0420                      class_member_name)
0421         return (stub_name + '_impl', class_member_name)
0422 
0423     def do_method(self, method):
0424         assert self.node_name_mixed is not None
0425 
0426         in_class = []
0427 
0428         # Examples refer to Thing.DoStuff (su) -> ii
0429 
0430         # DoStuff
0431         dbus_method_name = method.getAttribute('name')
0432         # do_stuff
0433         class_member_name = method.getAttribute('tp:name-for-bindings')
0434         if dbus_method_name != class_member_name.replace('_', ''):
0435             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
0436                     'not match' % (dbus_method_name, class_member_name))
0437         class_member_name = class_member_name.lower()
0438 
0439         # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
0440         #   DBusGMethodInvocation *);
0441         stub_name = (self.prefix_ + self.node_name_lc + '_' +
0442                      class_member_name)
0443         # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *,
0444         #   const char *, guint, DBusGMethodInvocation);
0445         impl_name = stub_name + '_impl'
0446         # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *,
0447         #   gint, gint);
0448         ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' +
0449                     class_member_name)
0450 
0451         # Gather arguments
0452         in_args = []
0453         out_args = []
0454         for i in method.getElementsByTagName('arg'):
0455             name = i.getAttribute('name')
0456             direction = i.getAttribute('direction') or 'in'
0457             dtype = i.getAttribute('type')
0458 
0459             assert direction in ('in', 'out')
0460 
0461             if name:
0462                 name = direction + '_' + name
0463             elif direction == 'in':
0464                 name = direction + str(len(in_args))
0465             else:
0466                 name = direction + str(len(out_args))
0467 
0468             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
0469 
0470             if pointer:
0471                 ctype = 'const ' + ctype
0472 
0473             struct = (ctype, name)
0474 
0475             if direction == 'in':
0476                 in_args.append(struct)
0477             else:
0478                 out_args.append(struct)
0479 
0480         # Implementation type declaration (in header, docs in body)
0481         self.b('/**')
0482         self.b(' * %s:' % impl_name)
0483         self.b(' * @self: The object implementing this interface')
0484         for (ctype, name) in in_args:
0485             self.b(' * @%s: %s (FIXME, generate documentation)'
0486                    % (name, ctype))
0487         self.b(' * @context: Used to return values or throw an error')
0488         self.b(' *')
0489         self.b(' * The signature of an implementation of the D-Bus method')
0490         self.b(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
0491         self.b(' */')
0492         self.h('typedef void (*%s) (%s%s *self,'
0493           % (impl_name, self.Prefix, self.node_name_mixed))
0494         for (ctype, name) in in_args:
0495             self.h('    %s%s,' % (ctype, name))
0496         self.h('    DBusGMethodInvocation *context);')
0497 
0498         # Class member (in class definition)
0499         in_class.append('    %s %s;' % (impl_name, class_member_name))
0500 
0501         # Stub definition (in body only - it's static)
0502         self.b('static void')
0503         self.b('%s (%s%s *self,'
0504            % (stub_name, self.Prefix, self.node_name_mixed))
0505         for (ctype, name) in in_args:
0506             self.b('    %s%s,' % (ctype, name))
0507         self.b('    DBusGMethodInvocation *context)')
0508         self.b('{')
0509         self.b('  %s impl = (%s%s_GET_CLASS (self)->%s);'
0510           % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
0511         self.b('')
0512         self.b('  if (impl != NULL)')
0513         tmp = ['self'] + [name for (ctype, name) in in_args] + ['context']
0514         self.b('    {')
0515         self.b('      (impl) (%s);' % ',\n        '.join(tmp))
0516         self.b('    }')
0517         self.b('  else')
0518         self.b('    {')
0519         if self.not_implemented_func:
0520             self.b('      %s (context);' % self.not_implemented_func)
0521         else:
0522             self.b('      GError e = { DBUS_GERROR, ')
0523             self.b('           DBUS_GERROR_UNKNOWN_METHOD,')
0524             self.b('           "Method not implemented" };')
0525             self.b('')
0526             self.b('      dbus_g_method_return_error (context, &e);')
0527         self.b('    }')
0528         self.b('}')
0529         self.b('')
0530 
0531         # Implementation registration (in both header and body)
0532         self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);'
0533                % (self.prefix_, self.node_name_lc, class_member_name,
0534                   self.Prefix, self.node_name_mixed, impl_name))
0535 
0536         self.b('/**')
0537         self.b(' * %s%s_implement_%s:'
0538                % (self.prefix_, self.node_name_lc, class_member_name))
0539         self.b(' * @klass: A class whose instances implement this interface')
0540         self.b(' * @impl: A callback used to implement the %s D-Bus method'
0541                % dbus_method_name)
0542         self.b(' *')
0543         self.b(' * Register an implementation for the %s method in the vtable'
0544                % dbus_method_name)
0545         self.b(' * of an implementation of this interface. To be called from')
0546         self.b(' * the interface init function.')
0547         self.b(' */')
0548         self.b('void')
0549         self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
0550                % (self.prefix_, self.node_name_lc, class_member_name,
0551                   self.Prefix, self.node_name_mixed, impl_name))
0552         self.b('{')
0553         self.b('  klass->%s = impl;' % class_member_name)
0554         self.b('}')
0555         self.b('')
0556 
0557         # Return convenience function (static inline, in header)
0558         self.h('/**')
0559         self.h(' * %s:' % ret_name)
0560         self.h(' * @context: The D-Bus method invocation context')
0561         for (ctype, name) in out_args:
0562             self.h(' * @%s: %s (FIXME, generate documentation)'
0563                    % (name, ctype))
0564         self.h(' *')
0565         self.h(' * Return successfully by calling dbus_g_method_return().')
0566         self.h(' * This inline function exists only to provide type-safety.')
0567         self.h(' */')
0568         tmp = (['DBusGMethodInvocation *context'] +
0569                [ctype + name for (ctype, name) in out_args])
0570         self.h('static inline')
0571         self.h('/* this comment is to stop gtkdoc realising this is static */')
0572         self.h(('void %s (' % ret_name) + (',\n    '.join(tmp)) + ');')
0573         self.h('static inline void')
0574         self.h(('%s (' % ret_name) + (',\n    '.join(tmp)) + ')')
0575         self.h('{')
0576         tmp = ['context'] + [name for (ctype, name) in out_args]
0577         self.h('  dbus_g_method_return (' + ',\n      '.join(tmp) + ');')
0578         self.h('}')
0579         self.h('')
0580 
0581         return in_class
0582 
0583     def get_signal_const_entry(self, signal):
0584         assert self.node_name_uc is not None
0585         return ('SIGNAL_%s_%s'
0586                 % (self.node_name_uc, signal.getAttribute('name')))
0587 
0588     def do_signal(self, signal):
0589         assert self.node_name_mixed is not None
0590 
0591         in_base_init = []
0592 
0593         # for signal: Thing::StuffHappened (s, u)
0594         # we want to emit:
0595         # void tp_svc_thing_emit_stuff_happened (gpointer instance,
0596         #    const char *arg0, guint arg1);
0597 
0598         dbus_name = signal.getAttribute('name')
0599 
0600         ugly_name = signal.getAttribute('tp:name-for-bindings')
0601         if dbus_name != ugly_name.replace('_', ''):
0602             raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
0603                     'not match' % (dbus_name, ugly_name))
0604 
0605         stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
0606                      ugly_name.lower())
0607 
0608         const_name = self.get_signal_const_entry(signal)
0609 
0610         # Gather arguments
0611         args = []
0612         for i in signal.getElementsByTagName('arg'):
0613             name = i.getAttribute('name')
0614             dtype = i.getAttribute('type')
0615             tp_type = i.getAttribute('tp:type')
0616 
0617             if name:
0618                 name = 'arg_' + name
0619             else:
0620                 name = 'arg' + str(len(args))
0621 
0622             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
0623 
0624             if pointer:
0625                 ctype = 'const ' + ctype
0626 
0627             struct = (ctype, name, gtype)
0628             args.append(struct)
0629 
0630         tmp = (['gpointer instance'] +
0631                [ctype + name for (ctype, name, gtype) in args])
0632 
0633         self.h(('void %s (' % stub_name) + (',\n    '.join(tmp)) + ');')
0634 
0635         # FIXME: emit docs
0636 
0637         self.b('/**')
0638         self.b(' * %s:' % stub_name)
0639         self.b(' * @instance: The object implementing this interface')
0640         for (ctype, name, gtype) in args:
0641             self.b(' * @%s: %s (FIXME, generate documentation)'
0642                    % (name, ctype))
0643         self.b(' *')
0644         self.b(' * Type-safe wrapper around g_signal_emit to emit the')
0645         self.b(' * %s signal on interface %s.'
0646                % (dbus_name, self.iface_name))
0647         self.b(' */')
0648 
0649         self.b('void')
0650         self.b(('%s (' % stub_name) + (',\n    '.join(tmp)) + ')')
0651         self.b('{')
0652         self.b('  g_assert (instance != NULL);')
0653         self.b('  g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));'
0654                % (self.current_gtype))
0655         tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name),
0656                 '0'] + [name for (ctype, name, gtype) in args])
0657         self.b('  g_signal_emit (' + ',\n      '.join(tmp) + ');')
0658         self.b('}')
0659         self.b('')
0660 
0661         signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_',
0662                 '-')
0663         in_base_init.append('  /**')
0664         in_base_init.append('   * %s%s::%s:'
0665                 % (self.Prefix, self.node_name_mixed, signal_name))
0666         for (ctype, name, gtype) in args:
0667             in_base_init.append('   * @%s: %s (FIXME, generate documentation)'
0668                    % (name, ctype))
0669         in_base_init.append('   *')
0670         in_base_init.append('   * The %s D-Bus signal is emitted whenever '
0671                 'this GObject signal is.' % dbus_name)
0672         in_base_init.append('   */')
0673         in_base_init.append('  %s_signals[%s] ='
0674                             % (self.node_name_lc, const_name))
0675         in_base_init.append('  g_signal_new ("%s",' % signal_name)
0676         in_base_init.append('      G_OBJECT_CLASS_TYPE (klass),')
0677         in_base_init.append('      G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
0678         in_base_init.append('      0,')
0679         in_base_init.append('      NULL, NULL,')
0680         in_base_init.append('      %s,'
0681                 % signal_to_marshal_name(signal, self.signal_marshal_prefix))
0682         in_base_init.append('      G_TYPE_NONE,')
0683         tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
0684         in_base_init.append('      %s);' % ',\n      '.join(tmp))
0685         in_base_init.append('')
0686 
0687         return in_base_init
0688 
0689     def have_properties(self, nodes):
0690         for node in nodes:
0691             interface =  node.getElementsByTagName('interface')[0]
0692             if interface.getElementsByTagName('property'):
0693                 return True
0694         return False
0695 
0696     def __call__(self):
0697         nodes = self.dom.getElementsByTagName('node')
0698         nodes.sort(cmp_by_name)
0699 
0700         self.h('#include <glib-object.h>')
0701         self.h('#include <dbus/dbus-glib.h>')
0702 
0703         if self.have_properties(nodes):
0704             self.h('#include <telepathy-glib/dbus-properties-mixin.h>')
0705 
0706         self.h('')
0707         self.h('G_BEGIN_DECLS')
0708         self.h('')
0709 
0710         self.b('#include "%s.h"' % self.basename)
0711         self.b('')
0712         for header in self.headers:
0713             self.b('#include %s' % header)
0714         self.b('')
0715 
0716         for node in nodes:
0717             self.do_node(node)
0718 
0719         self.h('')
0720         self.h('G_END_DECLS')
0721 
0722         self.b('')
0723         for header in self.end_headers:
0724             self.b('#include %s' % header)
0725 
0726         self.h('')
0727         self.b('')
0728         open(self.basename + '.h', 'w').write('\n'.join(self.__header))
0729         open(self.basename + '.c', 'w').write('\n'.join(self.__body))
0730 
0731 
0732 def cmdline_error():
0733     print """\
0734 usage:
0735     gen-ginterface [OPTIONS] xmlfile Prefix_
0736 options:
0737     --include='<header.h>' (may be repeated)
0738     --include='"header.h"' (ditto)
0739     --include-end='"header.h"' (ditto)
0740         Include extra headers in the generated .c file
0741     --signal-marshal-prefix='prefix'
0742         Use the given prefix on generated signal marshallers (default is
0743         prefix.lower()).
0744     --filename='BASENAME'
0745         Set the basename for the output files (default is prefix.lower()
0746         + 'ginterfaces')
0747     --not-implemented-func='symbol'
0748         Set action when methods not implemented in the interface vtable are
0749         called. symbol must have signature
0750             void symbol (DBusGMethodInvocation *context)
0751         and return some sort of "not implemented" error via
0752             dbus_g_method_return_error (context, ...)
0753 """
0754     sys.exit(1)
0755 
0756 
0757 if __name__ == '__main__':
0758     from getopt import gnu_getopt
0759 
0760     options, argv = gnu_getopt(sys.argv[1:], '',
0761                                ['filename=', 'signal-marshal-prefix=',
0762                                 'include=', 'include-end=',
0763                                 'allow-unstable',
0764                                 'not-implemented-func='])
0765 
0766     try:
0767         prefix = argv[1]
0768     except IndexError:
0769         cmdline_error()
0770 
0771     basename = prefix.lower() + 'ginterfaces'
0772     signal_marshal_prefix = prefix.lower().rstrip('_')
0773     headers = []
0774     end_headers = []
0775     not_implemented_func = ''
0776     allow_havoc = False
0777 
0778     for option, value in options:
0779         if option == '--filename':
0780             basename = value
0781         elif option == '--signal-marshal-prefix':
0782             signal_marshal_prefix = value
0783         elif option == '--include':
0784             if value[0] not in '<"':
0785                 value = '"%s"' % value
0786             headers.append(value)
0787         elif option == '--include-end':
0788             if value[0] not in '<"':
0789                 value = '"%s"' % value
0790             end_headers.append(value)
0791         elif option == '--not-implemented-func':
0792             not_implemented_func = value
0793         elif option == '--allow-unstable':
0794             allow_havoc = True
0795 
0796     try:
0797         dom = xml.dom.minidom.parse(argv[0])
0798     except IndexError:
0799         cmdline_error()
0800 
0801     Generator(dom, prefix, basename, signal_marshal_prefix, headers,
0802               end_headers, not_implemented_func, allow_havoc)()