3
# glib-ginterface-gen.py: service-side interface generator
5
# Generate dbus-glib 0.x service GInterfaces from the Telepathy specification.
6
# The master copy of this program is in the telepathy-glib repository -
7
# please make any changes there.
9
# Copyright (C) 2006, 2007 Collabora Limited
11
# This library is free software; you can redistribute it and/or
12
# modify it under the terms of the GNU Lesser General Public
13
# License as published by the Free Software Foundation; either
14
# version 2.1 of the License, or (at your option) any later version.
16
# This library is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
# Lesser General Public License for more details.
21
# You should have received a copy of the GNU Lesser General Public
22
# License along with this library; if not, write to the Free Software
23
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
import xml.dom.minidom
29
from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
30
camelcase_to_lower, NS_TP, dbus_gutils_wincaps_to_uscore, \
31
signal_to_marshal_name, method_to_glue_marshal_name
34
NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
36
class Generator(object):
38
def __init__(self, dom, prefix, basename, signal_marshal_prefix,
39
headers, end_headers, not_implemented_func,
45
assert prefix.endswith('_')
46
assert not signal_marshal_prefix.endswith('_')
48
# The main_prefix, sub_prefix thing is to get:
50
# FOO_SVC_ -> (FOO_, _SVC_)
52
# FOO_BAR/ -> (FOO_BAR_, _)
53
# FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_)
56
main_prefix, sub_prefix = prefix.upper().split('/', 1)
57
prefix = prefix.replace('/', '_')
59
main_prefix, sub_prefix = prefix.upper().split('_', 1)
61
self.MAIN_PREFIX_ = main_prefix + '_'
62
self._SUB_PREFIX_ = '_' + sub_prefix
65
self.Prefix = prefix.replace('_', '')
66
self.prefix_ = prefix.lower()
67
self.PREFIX_ = prefix.upper()
69
self.signal_marshal_prefix = signal_marshal_prefix
70
self.headers = headers
71
self.end_headers = end_headers
72
self.not_implemented_func = not_implemented_func
73
self.allow_havoc = allow_havoc
76
self.__header.append(s)
81
def do_node(self, node):
82
node_name = node.getAttribute('name').replace('/', '')
83
node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
84
node_name_lc = self.node_name_lc = node_name.lower()
85
node_name_uc = self.node_name_uc = node_name.upper()
87
interfaces = node.getElementsByTagName('interface')
88
assert len(interfaces) == 1, interfaces
89
interface = interfaces[0]
90
self.iface_name = interface.getAttribute('name')
92
tmp = interface.getAttribute('tp:implement-service')
96
tmp = interface.getAttribute('tp:causes-havoc')
97
if tmp and not self.allow_havoc:
98
raise AssertionError('%s is %s' % (self.iface_name, tmp))
100
self.b('static const DBusGObjectInfo _%s%s_object_info;'
101
% (self.prefix_, node_name_lc))
104
methods = interface.getElementsByTagName('method')
105
signals = interface.getElementsByTagName('signal')
106
properties = interface.getElementsByTagName('property')
107
# Don't put properties in dbus-glib glue
110
self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
111
self.b(' GTypeInterface parent_class;')
112
for method in methods:
113
self.b(' %s %s;' % self.get_method_impl_names(method))
119
for signal in signals:
120
self.b(' %s,' % self.get_signal_const_entry(signal))
121
self.b(' N_%s_SIGNALS' % node_name_uc)
123
self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
124
% (node_name_lc, node_name_uc))
127
self.b('static void %s%s_base_init (gpointer klass);'
128
% (self.prefix_, node_name_lc))
132
self.b('%s%s_get_type (void)'
133
% (self.prefix_, node_name_lc))
135
self.b(' static GType type = 0;')
137
self.b(' if (G_UNLIKELY (type == 0))')
139
self.b(' static const GTypeInfo info = {')
140
self.b(' sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
141
self.b(' %s%s_base_init, /* base_init */'
142
% (self.prefix_, node_name_lc))
143
self.b(' NULL, /* base_finalize */')
144
self.b(' NULL, /* class_init */')
145
self.b(' NULL, /* class_finalize */')
146
self.b(' NULL, /* class_data */')
148
self.b(' 0, /* n_preallocs */')
149
self.b(' NULL /* instance_init */')
152
self.b(' type = g_type_register_static (G_TYPE_INTERFACE,')
153
self.b(' "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
156
self.b(' return type;')
161
self.h(' * %s%s:' % (self.Prefix, node_name_mixed))
163
self.h(' * Dummy typedef representing any implementation of this '
166
self.h('typedef struct _%s%s %s%s;'
167
% (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
170
self.h(' * %s%sClass:' % (self.Prefix, node_name_mixed))
172
self.h(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
174
self.h('typedef struct _%s%sClass %s%sClass;'
175
% (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
177
self.h('GType %s%s_get_type (void);'
178
% (self.prefix_, node_name_lc))
180
gtype = self.current_gtype = \
181
self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
182
classname = self.Prefix + node_name_mixed
184
self.h('#define %s \\\n (%s%s_get_type ())'
185
% (gtype, self.prefix_, node_name_lc))
186
self.h('#define %s%s(obj) \\\n'
187
' (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
188
% (self.PREFIX_, node_name_uc, gtype, classname))
189
self.h('#define %sIS%s%s(obj) \\\n'
190
' (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
191
% (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
192
self.h('#define %s%s_GET_CLASS(obj) \\\n'
193
' (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
194
% (self.PREFIX_, node_name_uc, gtype, classname))
200
for method in methods:
201
self.do_method(method)
203
for signal in signals:
204
base_init_code.extend(self.do_signal(signal))
206
self.b('static inline void')
207
self.b('%s%s_base_init_once (gpointer klass)'
208
% (self.prefix_, node_name_lc))
210
self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {'
211
% (len(properties) + 1))
214
access = m.getAttribute('access')
215
assert access in ('read', 'write', 'readwrite')
218
flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
219
elif access == 'write':
220
flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
222
flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
223
'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
225
self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
226
% (flags, m.getAttribute('type'), m.getAttribute('name')))
228
self.b(' { 0, 0, NULL, 0, NULL, NULL }')
230
self.b(' static TpDBusPropertiesMixinIfaceInfo interface =')
231
self.b(' { 0, properties, NULL, NULL };')
233
self.b(' interface.dbus_interface = g_quark_from_static_string '
234
'("%s");' % self.iface_name)
236
for i, m in enumerate(properties):
237
self.b(' properties[%d].name = g_quark_from_static_string ("%s");'
238
% (i, m.getAttribute('name')))
239
self.b(' properties[%d].type = %s;'
240
% (i, type_to_gtype(m.getAttribute('type'))[1]))
242
self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);'
243
% self.current_gtype)
246
for s in base_init_code:
248
self.b(' dbus_g_object_type_install_info (%s%s_get_type (),'
249
% (self.prefix_, node_name_lc))
250
self.b(' &_%s%s_object_info);'
251
% (self.prefix_, node_name_lc))
254
self.b('static void')
255
self.b('%s%s_base_init (gpointer klass)'
256
% (self.prefix_, node_name_lc))
258
self.b(' static gboolean initialized = FALSE;')
260
self.b(' if (!initialized)')
262
self.b(' initialized = TRUE;')
263
self.b(' %s%s_base_init_once (klass);'
264
% (self.prefix_, node_name_lc))
266
# insert anything we need to do per implementation here
271
self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
272
% (self.prefix_, node_name_lc))
274
method_blob, offsets = self.get_method_glue(methods)
276
for method, offset in zip(methods, offsets):
277
self.do_method_glue(method, offset)
282
self.b('static const DBusGObjectInfo _%s%s_object_info = {'
283
% (self.prefix_, node_name_lc))
284
self.b(' 0,') # version
285
self.b(' _%s%s_methods,' % (self.prefix_, node_name_lc))
286
self.b(' %d,' % len(methods))
287
self.b('"' + method_blob.replace('\0', '\\0') + '",')
288
self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
290
self.get_property_glue(glue_properties).replace('\0', '\\0') +
295
self.node_name_mixed = None
296
self.node_name_lc = None
297
self.node_name_uc = None
299
def get_method_glue(self, methods):
303
for method in methods:
304
offsets.append(len(''.join(info)))
306
info.append(self.iface_name + '\0')
307
info.append(method.getAttribute('name') + '\0')
309
info.append('A\0') # async
312
for arg in method.getElementsByTagName('arg'):
313
out = arg.getAttribute('direction') == 'out'
315
name = arg.getAttribute('name')
318
name = 'arg%u' % counter
321
info.append(name + '\0')
329
info.append('F\0') # not const
330
info.append('N\0') # not error or return
331
info.append(arg.getAttribute('type') + '\0')
335
return ''.join(info) + '\0', offsets
337
def do_method_glue(self, method, offset):
338
lc_name = camelcase_to_lower(method.getAttribute('name'))
340
marshaller = method_to_glue_marshal_name(method,
341
self.signal_marshal_prefix)
342
wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
344
self.b(" { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
346
def get_signal_glue(self, signals):
349
for signal in signals:
350
info.append(self.iface_name)
351
info.append(signal.getAttribute('name'))
353
return '\0'.join(info) + '\0\0'
355
# the implementation can be the same
356
get_property_glue = get_signal_glue
358
def get_method_impl_names(self, method):
359
dbus_method_name = method.getAttribute('name')
360
class_member_name = camelcase_to_lower(dbus_method_name)
361
stub_name = (self.prefix_ + self.node_name_lc + '_' +
363
return (stub_name + '_impl', class_member_name)
365
def do_method(self, method):
366
assert self.node_name_mixed is not None
370
# Examples refer to Thing.DoStuff (su) -> ii
373
dbus_method_name = method.getAttribute('name')
375
class_member_name = camelcase_to_lower(dbus_method_name)
376
# void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
377
# DBusGMethodInvocation *);
378
stub_name = (self.prefix_ + self.node_name_lc + '_' +
380
# typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *,
381
# const char *, guint, DBusGMethodInvocation);
382
impl_name = stub_name + '_impl'
383
# void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *,
385
ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' +
391
for i in method.getElementsByTagName('arg'):
392
name = i.getAttribute('name')
393
direction = i.getAttribute('direction') or 'in'
394
dtype = i.getAttribute('type')
396
assert direction in ('in', 'out')
399
name = direction + '_' + name
400
elif direction == 'in':
401
name = direction + str(len(in_args))
403
name = direction + str(len(out_args))
405
ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
408
ctype = 'const ' + ctype
410
struct = (ctype, name)
412
if direction == 'in':
413
in_args.append(struct)
415
out_args.append(struct)
417
# Implementation type declaration (in header, docs in body)
419
self.b(' * %s:' % impl_name)
420
self.b(' * @self: The object implementing this interface')
421
for (ctype, name) in in_args:
422
self.b(' * @%s: %s (FIXME, generate documentation)'
424
self.b(' * @context: Used to return values or throw an error')
426
self.b(' * The signature of an implementation of the D-Bus method')
427
self.b(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
429
self.h('typedef void (*%s) (%s%s *self,'
430
% (impl_name, self.Prefix, self.node_name_mixed))
431
for (ctype, name) in in_args:
432
self.h(' %s%s,' % (ctype, name))
433
self.h(' DBusGMethodInvocation *context);')
435
# Class member (in class definition)
436
in_class.append(' %s %s;' % (impl_name, class_member_name))
438
# Stub definition (in body only - it's static)
439
self.b('static void')
440
self.b('%s (%s%s *self,'
441
% (stub_name, self.Prefix, self.node_name_mixed))
442
for (ctype, name) in in_args:
443
self.b(' %s%s,' % (ctype, name))
444
self.b(' DBusGMethodInvocation *context)')
446
self.b(' %s impl = (%s%s_GET_CLASS (self)->%s);'
447
% (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
449
self.b(' if (impl != NULL)')
450
tmp = ['self'] + [name for (ctype, name) in in_args] + ['context']
452
self.b(' (impl) (%s);' % ',\n '.join(tmp))
456
if self.not_implemented_func:
457
self.b(' %s (context);' % self.not_implemented_func)
459
self.b(' GError e = { DBUS_GERROR, ')
460
self.b(' DBUS_GERROR_UNKNOWN_METHOD,')
461
self.b(' "Method not implemented" };')
463
self.b(' dbus_g_method_return_error (context, &e);')
468
# Implementation registration (in both header and body)
469
self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);'
470
% (self.prefix_, self.node_name_lc, class_member_name,
471
self.Prefix, self.node_name_mixed, impl_name))
474
self.b(' * %s%s_implement_%s:'
475
% (self.prefix_, self.node_name_lc, class_member_name))
476
self.b(' * @klass: A class whose instances implement this interface')
477
self.b(' * @impl: A callback used to implement the %s D-Bus method'
480
self.b(' * Register an implementation for the %s method in the vtable'
482
self.b(' * of an implementation of this interface. To be called from')
483
self.b(' * the interface init function.')
486
self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
487
% (self.prefix_, self.node_name_lc, class_member_name,
488
self.Prefix, self.node_name_mixed, impl_name))
490
self.b(' klass->%s = impl;' % class_member_name)
494
# Return convenience function (static inline, in header)
496
self.h(' * %s:' % ret_name)
497
self.h(' * @context: The D-Bus method invocation context')
498
for (ctype, name) in out_args:
499
self.h(' * @%s: %s (FIXME, generate documentation)'
502
self.h(' * Return successfully by calling dbus_g_method_return().')
503
self.h(' * This inline function exists only to provide type-safety.')
505
tmp = (['DBusGMethodInvocation *context'] +
506
[ctype + name for (ctype, name) in out_args])
507
self.h('static inline')
508
self.h('/* this comment is to stop gtkdoc realising this is static */')
509
self.h(('void %s (' % ret_name) + (',\n '.join(tmp)) + ');')
510
self.h('static inline void')
511
self.h(('%s (' % ret_name) + (',\n '.join(tmp)) + ')')
513
tmp = ['context'] + [name for (ctype, name) in out_args]
514
self.h(' dbus_g_method_return (' + ',\n '.join(tmp) + ');')
520
def get_signal_const_entry(self, signal):
521
assert self.node_name_uc is not None
522
return ('SIGNAL_%s_%s'
523
% (self.node_name_uc, signal.getAttribute('name')))
525
def do_signal(self, signal):
526
assert self.node_name_mixed is not None
530
# for signal: Thing::StuffHappened (s, u)
532
# void tp_svc_thing_emit_stuff_happened (gpointer instance,
533
# const char *arg0, guint arg1);
535
dbus_name = signal.getAttribute('name')
536
stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
537
camelcase_to_lower(dbus_name))
538
const_name = self.get_signal_const_entry(signal)
542
for i in signal.getElementsByTagName('arg'):
543
name = i.getAttribute('name')
544
dtype = i.getAttribute('type')
545
tp_type = i.getAttribute('tp:type')
550
name = 'arg' + str(len(args))
552
ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
555
ctype = 'const ' + ctype
557
struct = (ctype, name, gtype)
560
tmp = (['gpointer instance'] +
561
[ctype + name for (ctype, name, gtype) in args])
563
self.h(('void %s (' % stub_name) + (',\n '.join(tmp)) + ');')
568
self.b(' * %s:' % stub_name)
569
self.b(' * @instance: The object implementing this interface')
570
for (ctype, name, gtype) in args:
571
self.b(' * @%s: %s (FIXME, generate documentation)'
574
self.b(' * Type-safe wrapper around g_signal_emit to emit the')
575
self.b(' * %s signal on interface %s.'
576
% (dbus_name, self.iface_name))
580
self.b(('%s (' % stub_name) + (',\n '.join(tmp)) + ')')
582
self.b(' g_assert (instance != NULL);')
583
self.b(' g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));'
584
% (self.current_gtype))
585
tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name),
586
'0'] + [name for (ctype, name, gtype) in args])
587
self.b(' g_signal_emit (' + ',\n '.join(tmp) + ');')
591
in_base_init.append(' %s_signals[%s] ='
592
% (self.node_name_lc, const_name))
593
in_base_init.append(' g_signal_new ("%s",'
594
% (dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', '-')))
595
in_base_init.append(' G_OBJECT_CLASS_TYPE (klass),')
596
in_base_init.append(' G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
597
in_base_init.append(' 0,')
598
in_base_init.append(' NULL, NULL,')
599
in_base_init.append(' %s,'
600
% signal_to_marshal_name(signal, self.signal_marshal_prefix))
601
in_base_init.append(' G_TYPE_NONE,')
602
tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
603
in_base_init.append(' %s);' % ',\n '.join(tmp))
604
in_base_init.append('')
609
self.h('#include <glib-object.h>')
610
self.h('#include <dbus/dbus-glib.h>')
611
self.h('#include <telepathy-glib/dbus-properties-mixin.h>')
613
self.h('G_BEGIN_DECLS')
616
self.b('#include "%s.h"' % basename)
618
for header in self.headers:
619
self.b('#include %s' % header)
622
nodes = self.dom.getElementsByTagName('node')
623
nodes.sort(cmp_by_name)
629
self.h('G_END_DECLS')
632
for header in self.end_headers:
633
self.b('#include %s' % header)
637
open(basename + '.h', 'w').write('\n'.join(self.__header))
638
open(basename + '.c', 'w').write('\n'.join(self.__body))
644
gen-ginterface [OPTIONS] xmlfile Prefix_
646
--include='<header.h>' (may be repeated)
647
--include='"header.h"' (ditto)
648
--include-end='"header.h"' (ditto)
649
Include extra headers in the generated .c file
650
--signal-marshal-prefix='prefix'
651
Use the given prefix on generated signal marshallers (default is
653
--filename='BASENAME'
654
Set the basename for the output files (default is prefix.lower()
656
--not-implemented-func='symbol'
657
Set action when methods not implemented in the interface vtable are
658
called. symbol must have signature
659
void symbol (DBusGMethodInvocation *context)
660
and return some sort of "not implemented" error via
661
dbus_g_method_return_error (context, ...)
666
if __name__ == '__main__':
667
from getopt import gnu_getopt
669
options, argv = gnu_getopt(sys.argv[1:], '',
670
['filename=', 'signal-marshal-prefix=',
671
'include=', 'include-end=',
673
'not-implemented-func='])
680
basename = prefix.lower() + 'ginterfaces'
681
signal_marshal_prefix = prefix.lower().rstrip('_')
684
not_implemented_func = ''
687
for option, value in options:
688
if option == '--filename':
690
elif option == '--signal-marshal-prefix':
691
signal_marshal_prefix = value
692
elif option == '--include':
693
if value[0] not in '<"':
694
value = '"%s"' % value
695
headers.append(value)
696
elif option == '--include-end':
697
if value[0] not in '<"':
698
value = '"%s"' % value
699
end_headers.append(value)
700
elif option == '--not-implemented-func':
701
not_implemented_func = value
702
elif option == '--allow-unstable':
706
dom = xml.dom.minidom.parse(argv[0])
710
Generator(dom, prefix, basename, signal_marshal_prefix, headers,
711
end_headers, not_implemented_func, allow_havoc)()