summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extensions/Makefile.am16
-rw-r--r--extensions/OLPC_Activity_Properties.xml9
-rw-r--r--extensions/OLPC_Buddy_Info.xml50
-rw-r--r--extensions/all.xml2
-rw-r--r--tools/Makefile.am3
-rw-r--r--tools/glib-ginterface-gen.py331
-rw-r--r--tools/libglibcodegen.py180
-rw-r--r--tools/libtpcodegen.py247
8 files changed, 532 insertions, 306 deletions
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index 8e2e7679..7cb835bf 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -15,9 +15,6 @@ libsalut_extensions_la_SOURCES = \
extensions.h
nodist_libsalut_extensions_la_SOURCES = \
- _gen/signals-marshal.c \
- _gen/signals-marshal.h \
- _gen/signals-marshal.list \
_gen/enums.h \
_gen/gtypes.h \
_gen/gtypes-body.h \
@@ -57,17 +54,6 @@ extensions.html: _gen/all.xml $(tools_dir)/doc-generator.xsl
$(tools_dir)/doc-generator.xsl \
$< > $@
-_gen/signals-marshal.list: _gen/all.xml \
- $(tools_dir)/glib-signals-marshal-gen.py
- $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py $< > $@
-
-_gen/signals-marshal.h: _gen/signals-marshal.list Makefile.am
- $(AM_V_GEN)$(GLIB_GENMARSHAL) --header --prefix=_salut_ext_marshal $< > $@
-
-_gen/signals-marshal.c: _gen/signals-marshal.list Makefile.am
- $(AM_V_GEN){ echo '#include "_gen/signals-marshal.h"' && \
- $(GLIB_GENMARSHAL) --body --prefix=_salut_ext_marshal $< ; } > $@
-
_gen/register-dbus-glib-marshallers-body.h: _gen/all.xml \
$(tools_dir)/glib-client-marshaller-gen.py
$(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< \
@@ -103,9 +89,7 @@ _gen/interfaces-body.h: _gen/all.xml \
_gen/svc.c _gen/svc.h: _gen/all.xml $(tools_dir)/glib-ginterface-gen.py
$(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \
--filename=_gen/svc \
- --signal-marshal-prefix=_salut_ext \
--include='<telepathy-glib/dbus.h>' \
- --include='"_gen/signals-marshal.h"' \
--not-implemented-func='tp_dbus_g_method_return_not_implemented' \
--allow-unstable \
$< Salut_Svc_
diff --git a/extensions/OLPC_Activity_Properties.xml b/extensions/OLPC_Activity_Properties.xml
index 1976fc63..d1ea2afa 100644
--- a/extensions/OLPC_Activity_Properties.xml
+++ b/extensions/OLPC_Activity_Properties.xml
@@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
<interface name="org.laptop.Telepathy.ActivityProperties">
<tp:requires interface="org.freedesktop.Telepathy.Connection"/>
- <method name="SetProperties">
+ <method name="SetProperties" tp:name-for-bindings="Set_Properties">
<arg direction="in" name="room" type="u">
<tp:docstring>
An integer handle representing the room of the activity
@@ -41,7 +41,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <method name="GetProperties">
+ <method name="GetProperties" tp:name-for-bindings="Get_Properties">
<arg direction="in" name="room" type="u">
<tp:docstring>
An integer handle for the activity's room to request his properties for
@@ -61,7 +61,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <method name="GetActivity">
+ <method name="GetActivity" tp:name-for-bindings="Get_Activity">
<arg direction="in" name="activity_id" type="s">
<tp:docstring>
An activity id
@@ -85,7 +85,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <signal name="ActivityPropertiesChanged">
+ <signal name="ActivityPropertiesChanged"
+ tp:name-for-bindings="Activity_Properties_Changed">
<arg name="room" type="u">
<tp:docstring>
An integer handle representing the room of the activity
diff --git a/extensions/OLPC_Buddy_Info.xml b/extensions/OLPC_Buddy_Info.xml
index d7ed2ccd..7caf35c0 100644
--- a/extensions/OLPC_Buddy_Info.xml
+++ b/extensions/OLPC_Buddy_Info.xml
@@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
<interface name="org.laptop.Telepathy.BuddyInfo">
<tp:requires interface="org.freedesktop.Telepathy.Connection"/>
- <method name="SetProperties">
+ <method name="SetProperties" tp:name-for-bindings="Set_Properties">
<arg direction="in" name="properties" type="a{sv}">
<tp:docstring>
A dictionary mapping information names to the desired values.
@@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <method name="GetProperties">
+ <method name="GetProperties" tp:name-for-bindings="Get_Properties">
<arg direction="in" name="contact" type="u">
<tp:docstring>
An integer handle for the contact to request his properties for
@@ -59,7 +59,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <signal name="PropertiesChanged">
+ <signal name="PropertiesChanged" tp:name-for-bindings="Properties_Changed">
<arg name="contact" type="u">
<tp:docstring>
An integer handle representing the contact
@@ -67,7 +67,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</arg>
<arg name="properties" type="a{sv}">
<tp:docstring>
- A dictionary mapping information names to their new values
+ A dictionary mapping property names to their new values. All
+ properties are included, not just those that have changed.
</tp:docstring>
</arg>
<tp:docstring>
@@ -76,9 +77,21 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:docstring>
</signal>
- <method name="SetActivities">
- <arg direction="in" name="activities" type="a(su)">
- <tp:docstring>
+
+ <tp:struct name="Activity" array-name="Activity_List">
+ <tp:docstring>A struct containing:
+ <ul>
+ <li>the identifier of the activity</li>
+ <li>the room handle of the activity channel</li>
+ </ul>
+ </tp:docstring>
+ <tp:member type="s" name="id"/>
+ <tp:member type="u" tp:type="Room_Handle" name="room"/>
+ </tp:struct>
+
+ <method name="SetActivities" tp:name-for-bindings="Set_Activities">
+ <arg direction="in" name="activities" type="a(su)" tp:type="Activity[]">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
An array of structs containing:
<ul>
<li>the identifier of the activity</li>
@@ -95,7 +108,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <method name="AddActivity">
+ <method name="AddActivity" tp:name-for-bindings="Add_Activity">
<arg direction="in" name="id" type="s">
<tp:docstring>
An activity id
@@ -120,14 +133,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <method name="GetActivities">
+ <method name="GetActivities" tp:name-for-bindings="Get_Activities">
<arg direction="in" name="contact" type="u">
<tp:docstring>
An integer handle for the contact whose activities are to be returned
</tp:docstring>
</arg>
- <arg direction="out" name="activities" type="a(su)">
- <tp:docstring>
+ <arg direction="out" name="activities" type="a(su)" tp:type="Activity[]">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
An array of structs containing:
<ul>
<li>the identifier of the activity</li>
@@ -144,14 +157,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <signal name="ActivitiesChanged">
+ <signal name="ActivitiesChanged" tp:name-for-bindings="Activities_Changed">
<arg name="contact" type="u">
<tp:docstring>
An integer handle representing the contact
</tp:docstring>
</arg>
- <arg name="activities" type="a(su)">
- <tp:docstring>
+ <arg name="activities" type="a(su)" tp:type="Activity[]">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
An array of structs containing:
<ul>
<li>the identifier of the activity</li>
@@ -165,7 +178,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:docstring>
</signal>
- <method name="SetCurrentActivity">
+ <method name="SetCurrentActivity"
+ tp:name-for-bindings="Set_Current_Activity">
<arg direction="in" name="activity" type="s">
<tp:docstring>
The identifier of the activity, or the empty string if there is no
@@ -187,7 +201,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <method name="GetCurrentActivity">
+ <method name="GetCurrentActivity"
+ tp:name-for-bindings="Get_Current_Activity">
<arg direction="in" name="contact" type="u">
<tp:docstring>
An integer handle for the contact whose current activity is to be
@@ -213,7 +228,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:possible-errors>
</method>
- <signal name="CurrentActivityChanged">
+ <signal name="CurrentActivityChanged"
+ tp:name-for-bindings="Current_Activity_Changed">
<arg name="contact" type="u">
<tp:docstring>
An integer handle representing the contact
diff --git a/extensions/all.xml b/extensions/all.xml
index ee496336..436272cd 100644
--- a/extensions/all.xml
+++ b/extensions/all.xml
@@ -29,6 +29,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
<tp:generic-types>
<tp:external-type name="Contact_Handle" type="u"
from="Telepathy specification"/>
+ <tp:external-type name="Room_Handle" type="u"
+ from="Telepathy specification"/>
<tp:external-type name="Socket_Address_Type" type="u"
from="Telepathy specification"/>
<tp:external-type name="DBus_Interface" type="s"
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 691ac753..6a5a2990 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -16,10 +16,13 @@ EXTRA_DIST = \
glib-signals-marshal-gen.py \
identity.xsl \
libglibcodegen.py \
+ libtpcodegen.py \
make-release-mail.py \
telepathy.am \
xep.xsl
+libglibcodegen.py: libtpcodegen.py
+ $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@
glib-client-marshaller-gen.py: libglibcodegen.py
$(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@
glib-ginterface-gen.py: libglibcodegen.py
diff --git a/tools/glib-ginterface-gen.py b/tools/glib-ginterface-gen.py
index 36f3242b..c0ce20dd 100644
--- a/tools/glib-ginterface-gen.py
+++ b/tools/glib-ginterface-gen.py
@@ -26,13 +26,23 @@ import sys
import os.path
import xml.dom.minidom
-from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
- camelcase_to_lower, NS_TP, dbus_gutils_wincaps_to_uscore, \
- signal_to_marshal_name, method_to_glue_marshal_name
+from libtpcodegen import file_set_contents, key_by_name, u
+from libglibcodegen import Signature, type_to_gtype, \
+ NS_TP, dbus_gutils_wincaps_to_uscore
NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+def get_emits_changed(node):
+ try:
+ return [
+ annotation.getAttribute('value')
+ for annotation in node.getElementsByTagName('annotation')
+ if annotation.getAttribute('name') == 'org.freedesktop.DBus.Property.EmitsChangedSignal'
+ ][0]
+ except IndexError:
+ return None
+
class Generator(object):
def __init__(self, dom, prefix, basename, signal_marshal_prefix,
@@ -41,6 +51,7 @@ class Generator(object):
self.dom = dom
self.__header = []
self.__body = []
+ self.__docs = []
assert prefix.endswith('_')
assert not signal_marshal_prefix.endswith('_')
@@ -66,6 +77,7 @@ class Generator(object):
self.prefix_ = prefix.lower()
self.PREFIX_ = prefix.upper()
+ self.basename = basename
self.signal_marshal_prefix = signal_marshal_prefix
self.headers = headers
self.end_headers = end_headers
@@ -78,6 +90,9 @@ class Generator(object):
def b(self, s):
self.__body.append(s)
+ def d(self, s):
+ self.__docs.append(s)
+
def do_node(self, node):
node_name = node.getAttribute('name').replace('/', '')
node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
@@ -97,6 +112,8 @@ class Generator(object):
if tmp and not self.allow_havoc:
raise AssertionError('%s is %s' % (self.iface_name, tmp))
+ iface_emits_changed = get_emits_changed(interface)
+
self.b('static const DBusGObjectInfo _%s%s_object_info;'
% (self.prefix_, node_name_lc))
self.b('')
@@ -157,20 +174,55 @@ class Generator(object):
self.b('}')
self.b('')
- self.h('/**')
- self.h(' * %s%s:' % (self.Prefix, node_name_mixed))
- self.h(' *')
- self.h(' * Dummy typedef representing any implementation of this '
+ self.d('/**')
+ self.d(' * %s%s:' % (self.Prefix, node_name_mixed))
+ self.d(' *')
+ self.d(' * Dummy typedef representing any implementation of this '
'interface.')
- self.h(' */')
+ self.d(' */')
+
self.h('typedef struct _%s%s %s%s;'
% (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
self.h('')
- self.h('/**')
- self.h(' * %s%sClass:' % (self.Prefix, node_name_mixed))
- self.h(' *')
- self.h(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
- self.h(' */')
+
+ self.d('/**')
+ self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed))
+ self.d(' *')
+ self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
+
+ if methods:
+ self.d(' *')
+ self.d(' * In a full implementation of this interface (i.e. all')
+ self.d(' * methods implemented), the interface initialization')
+ self.d(' * function used in G_IMPLEMENT_INTERFACE() would')
+ self.d(' * typically look like this:')
+ self.d(' *')
+ self.d(' * <programlisting>')
+ self.d(' * static void')
+ self.d(' * implement_%s (gpointer klass,' % self.node_name_lc)
+ self.d(' * gpointer unused G_GNUC_UNUSED)')
+ self.d(' * {')
+ self.d(' * #define IMPLEMENT(x) %s%s_implement_&num;&num;x (\\'
+ % (self.prefix_, self.node_name_lc))
+ self.d(' * klass, my_object_&num;&num;x)')
+
+ for method in methods:
+ class_member_name = method.getAttribute('tp:name-for-bindings')
+ class_member_name = class_member_name.lower()
+ self.d(' * IMPLEMENT (%s);' % class_member_name)
+
+ self.d(' * #undef IMPLEMENT')
+ self.d(' * }')
+ self.d(' * </programlisting>')
+ else:
+ self.d(' * This interface has no D-Bus methods, so an')
+ self.d(' * implementation can typically pass %NULL to')
+ self.d(' * G_IMPLEMENT_INTERFACE() as the interface')
+ self.d(' * initialization function.')
+
+ self.d(' */')
+ self.d('')
+
self.h('typedef struct _%s%sClass %s%sClass;'
% (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
self.h('')
@@ -207,48 +259,66 @@ class Generator(object):
self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)'
% (self.prefix_, node_name_lc))
self.b('{')
- self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {'
- % (len(properties) + 1))
- for m in properties:
- access = m.getAttribute('access')
- assert access in ('read', 'write', 'readwrite')
+ if properties:
+ self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {'
+ % (len(properties) + 1))
- if access == 'read':
- flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
- elif access == 'write':
- flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
- else:
- flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
- 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
+ for m in properties:
+ access = m.getAttribute('access')
+ assert access in ('read', 'write', 'readwrite')
- self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
- % (flags, m.getAttribute('type'), m.getAttribute('name')))
+ if access == 'read':
+ flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
+ elif access == 'write':
+ flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
+ else:
+ flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
+ 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
- self.b(' { 0, 0, NULL, 0, NULL, NULL }')
- self.b(' };')
- self.b(' static TpDBusPropertiesMixinIfaceInfo interface =')
- self.b(' { 0, properties, NULL, NULL };')
- self.b('')
- self.b(' interface.dbus_interface = g_quark_from_static_string '
- '("%s");' % self.iface_name)
+ prop_emits_changed = get_emits_changed(m)
- for i, m in enumerate(properties):
- self.b(' properties[%d].name = g_quark_from_static_string ("%s");'
- % (i, m.getAttribute('name')))
- self.b(' properties[%d].type = %s;'
- % (i, type_to_gtype(m.getAttribute('type'))[1]))
+ if prop_emits_changed is None:
+ prop_emits_changed = iface_emits_changed
+
+ if prop_emits_changed == 'true':
+ flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED'
+ elif prop_emits_changed == 'invalidates':
+ flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED'
+
+ self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
+ % (flags, m.getAttribute('type'), m.getAttribute('name')))
+
+ self.b(' { 0, 0, NULL, 0, NULL, NULL }')
+ self.b(' };')
+ self.b(' static TpDBusPropertiesMixinIfaceInfo interface =')
+ self.b(' { 0, properties, NULL, NULL };')
+ self.b('')
- self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);'
- % self.current_gtype)
- self.b('')
- for s in base_init_code:
- self.b(s)
self.b(' dbus_g_object_type_install_info (%s%s_get_type (),'
% (self.prefix_, node_name_lc))
self.b(' &_%s%s_object_info);'
% (self.prefix_, node_name_lc))
+ self.b('')
+
+ if properties:
+ self.b(' interface.dbus_interface = g_quark_from_static_string '
+ '("%s");' % self.iface_name)
+
+ for i, m in enumerate(properties):
+ self.b(' properties[%d].name = g_quark_from_static_string ("%s");'
+ % (i, m.getAttribute('name')))
+ self.b(' properties[%d].type = %s;'
+ % (i, type_to_gtype(m.getAttribute('type'))[1]))
+
+ self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);'
+ % self.current_gtype)
+
+ self.b('')
+
+ for s in base_init_code:
+ self.b(s)
self.b('}')
self.b('static void')
@@ -276,6 +346,10 @@ class Generator(object):
for method, offset in zip(methods, offsets):
self.do_method_glue(method, offset)
+ if len(methods) == 0:
+ # empty arrays are a gcc extension, so put in a dummy member
+ self.b(" { NULL, NULL, 0 }")
+
self.b('};')
self.b('')
@@ -335,10 +409,13 @@ class Generator(object):
return ''.join(info) + '\0', offsets
def do_method_glue(self, method, offset):
- lc_name = camelcase_to_lower(method.getAttribute('name'))
+ lc_name = method.getAttribute('tp:name-for-bindings')
+ if method.getAttribute('name') != lc_name.replace('_', ''):
+ raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+ 'not match' % (method.getAttribute('name'), lc_name))
+ lc_name = lc_name.lower()
- marshaller = method_to_glue_marshal_name(method,
- self.signal_marshal_prefix)
+ marshaller = 'g_cclosure_marshal_generic'
wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
self.b(" { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
@@ -357,10 +434,16 @@ class Generator(object):
def get_method_impl_names(self, method):
dbus_method_name = method.getAttribute('name')
- class_member_name = camelcase_to_lower(dbus_method_name)
+
+ class_member_name = method.getAttribute('tp:name-for-bindings')
+ if dbus_method_name != class_member_name.replace('_', ''):
+ raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+ 'not match' % (dbus_method_name, class_member_name))
+ class_member_name = class_member_name.lower()
+
stub_name = (self.prefix_ + self.node_name_lc + '_' +
class_member_name)
- return (stub_name + '_impl', class_member_name)
+ return (stub_name + '_impl', class_member_name + '_cb')
def do_method(self, method):
assert self.node_name_mixed is not None
@@ -372,7 +455,12 @@ class Generator(object):
# DoStuff
dbus_method_name = method.getAttribute('name')
# do_stuff
- class_member_name = camelcase_to_lower(dbus_method_name)
+ class_member_name = method.getAttribute('tp:name-for-bindings')
+ if dbus_method_name != class_member_name.replace('_', ''):
+ raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+ 'not match' % (dbus_method_name, class_member_name))
+ class_member_name = class_member_name.lower()
+
# void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
# DBusGMethodInvocation *);
stub_name = (self.prefix_ + self.node_name_lc + '_' +
@@ -414,18 +502,19 @@ class Generator(object):
else:
out_args.append(struct)
- # Implementation type declaration (in header, docs in body)
- self.b('/**')
- self.b(' * %s:' % impl_name)
- self.b(' * @self: The object implementing this interface')
+ # Implementation type declaration (in header, docs separated)
+ self.d('/**')
+ self.d(' * %s:' % impl_name)
+ self.d(' * @self: The object implementing this interface')
for (ctype, name) in in_args:
- self.b(' * @%s: %s (FIXME, generate documentation)'
+ self.d(' * @%s: %s (FIXME, generate documentation)'
% (name, ctype))
- self.b(' * @context: Used to return values or throw an error')
- self.b(' *')
- self.b(' * The signature of an implementation of the D-Bus method')
- self.b(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
- self.b(' */')
+ self.d(' * @context: Used to return values or throw an error')
+ self.d(' *')
+ self.d(' * The signature of an implementation of the D-Bus method')
+ self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
+ self.d(' */')
+
self.h('typedef void (*%s) (%s%s *self,'
% (impl_name, self.Prefix, self.node_name_mixed))
for (ctype, name) in in_args:
@@ -443,7 +532,7 @@ class Generator(object):
self.b(' %s%s,' % (ctype, name))
self.b(' DBusGMethodInvocation *context)')
self.b('{')
- self.b(' %s impl = (%s%s_GET_CLASS (self)->%s);'
+ self.b(' %s impl = (%s%s_GET_CLASS (self)->%s_cb);'
% (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
self.b('')
self.b(' if (impl != NULL)')
@@ -470,38 +559,41 @@ class Generator(object):
% (self.prefix_, self.node_name_lc, class_member_name,
self.Prefix, self.node_name_mixed, impl_name))
- self.b('/**')
- self.b(' * %s%s_implement_%s:'
+ self.d('/**')
+ self.d(' * %s%s_implement_%s:'
% (self.prefix_, self.node_name_lc, class_member_name))
- self.b(' * @klass: A class whose instances implement this interface')
- self.b(' * @impl: A callback used to implement the %s D-Bus method'
+ self.d(' * @klass: A class whose instances implement this interface')
+ self.d(' * @impl: A callback used to implement the %s D-Bus method'
% dbus_method_name)
- self.b(' *')
- self.b(' * Register an implementation for the %s method in the vtable'
+ self.d(' *')
+ self.d(' * Register an implementation for the %s method in the vtable'
% dbus_method_name)
- self.b(' * of an implementation of this interface. To be called from')
- self.b(' * the interface init function.')
- self.b(' */')
+ self.d(' * of an implementation of this interface. To be called from')
+ self.d(' * the interface init function.')
+ self.d(' */')
+
self.b('void')
self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
% (self.prefix_, self.node_name_lc, class_member_name,
self.Prefix, self.node_name_mixed, impl_name))
self.b('{')
- self.b(' klass->%s = impl;' % class_member_name)
+ self.b(' klass->%s_cb = impl;' % class_member_name)
self.b('}')
self.b('')
# Return convenience function (static inline, in header)
- self.h('/**')
- self.h(' * %s:' % ret_name)
- self.h(' * @context: The D-Bus method invocation context')
+ self.d('/**')
+ self.d(' * %s:' % ret_name)
+ self.d(' * @context: The D-Bus method invocation context')
for (ctype, name) in out_args:
- self.h(' * @%s: %s (FIXME, generate documentation)'
+ self.d(' * @%s: %s (FIXME, generate documentation)'
% (name, ctype))
- self.h(' *')
- self.h(' * Return successfully by calling dbus_g_method_return().')
- self.h(' * This inline function exists only to provide type-safety.')
- self.h(' */')
+ self.d(' *')
+ self.d(' * Return successfully by calling dbus_g_method_return().')
+ self.d(' * This inline function exists only to provide type-safety.')
+ self.d(' */')
+ self.d('')
+
tmp = (['DBusGMethodInvocation *context'] +
[ctype + name for (ctype, name) in out_args])
self.h('static inline')
@@ -533,8 +625,15 @@ class Generator(object):
# const char *arg0, guint arg1);
dbus_name = signal.getAttribute('name')
+
+ ugly_name = signal.getAttribute('tp:name-for-bindings')
+ if dbus_name != ugly_name.replace('_', ''):
+ raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
+ 'not match' % (dbus_name, ugly_name))
+
stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
- camelcase_to_lower(dbus_name))
+ ugly_name.lower())
+
const_name = self.get_signal_const_entry(signal)
# Gather arguments
@@ -564,17 +663,17 @@ class Generator(object):
# FIXME: emit docs
- self.b('/**')
- self.b(' * %s:' % stub_name)
- self.b(' * @instance: The object implementing this interface')
+ self.d('/**')
+ self.d(' * %s:' % stub_name)
+ self.d(' * @instance: The object implementing this interface')
for (ctype, name, gtype) in args:
- self.b(' * @%s: %s (FIXME, generate documentation)'
+ self.d(' * @%s: %s (FIXME, generate documentation)'
% (name, ctype))
- self.b(' *')
- self.b(' * Type-safe wrapper around g_signal_emit to emit the')
- self.b(' * %s signal on interface %s.'
+ self.d(' *')
+ self.d(' * Type-safe wrapper around g_signal_emit to emit the')
+ self.d(' * %s signal on interface %s.'
% (dbus_name, self.iface_name))
- self.b(' */')
+ self.d(' */')
self.b('void')
self.b(('%s (' % stub_name) + (',\n '.join(tmp)) + ')')
@@ -588,16 +687,30 @@ class Generator(object):
self.b('}')
self.b('')
+ signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_',
+ '-')
+
+ self.d('/**')
+ self.d(' * %s%s::%s:'
+ % (self.Prefix, self.node_name_mixed, signal_name))
+ self.d(' * @self: an object')
+ for (ctype, name, gtype) in args:
+ self.d(' * @%s: %s (FIXME, generate documentation)'
+ % (name, ctype))
+ self.d(' *')
+ self.d(' * The %s D-Bus signal is emitted whenever '
+ 'this GObject signal is.' % dbus_name)
+ self.d(' */')
+ self.d('')
+
in_base_init.append(' %s_signals[%s] ='
% (self.node_name_lc, const_name))
- in_base_init.append(' g_signal_new ("%s",'
- % (dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', '-')))
+ in_base_init.append(' g_signal_new ("%s",' % signal_name)
in_base_init.append(' G_OBJECT_CLASS_TYPE (klass),')
in_base_init.append(' G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
in_base_init.append(' 0,')
in_base_init.append(' NULL, NULL,')
- in_base_init.append(' %s,'
- % signal_to_marshal_name(signal, self.signal_marshal_prefix))
+ in_base_init.append(' g_cclosure_marshal_generic,')
in_base_init.append(' G_TYPE_NONE,')
tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
in_base_init.append(' %s);' % ',\n '.join(tmp))
@@ -605,23 +718,31 @@ class Generator(object):
return in_base_init
+ def have_properties(self, nodes):
+ for node in nodes:
+ interface = node.getElementsByTagName('interface')[0]
+ if interface.getElementsByTagName('property'):
+ return True
+ return False
+
def __call__(self):
+ nodes = self.dom.getElementsByTagName('node')
+ nodes.sort(key=key_by_name)
+
self.h('#include <glib-object.h>')
self.h('#include <dbus/dbus-glib.h>')
- self.h('#include <telepathy-glib/dbus-properties-mixin.h>')
+
+ for header in self.headers:
+ self.h('#include %s' % header)
+ self.h('')
+
self.h('')
self.h('G_BEGIN_DECLS')
self.h('')
- self.b('#include "%s.h"' % basename)
- self.b('')
- for header in self.headers:
- self.b('#include %s' % header)
+ self.b('#include "%s.h"' % self.basename)
self.b('')
- nodes = self.dom.getElementsByTagName('node')
- nodes.sort(cmp_by_name)
-
for node in nodes:
self.do_node(node)
@@ -634,12 +755,12 @@ class Generator(object):
self.h('')
self.b('')
- open(basename + '.h', 'w').write('\n'.join(self.__header))
- open(basename + '.c', 'w').write('\n'.join(self.__body))
-
+ file_set_contents(self.basename + '.h', u('\n').join(self.__header).encode('utf-8'))
+ file_set_contents(self.basename + '.c', 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 cmdline_error():
- print """\
+ print("""\
usage:
gen-ginterface [OPTIONS] xmlfile Prefix_
options:
@@ -659,7 +780,7 @@ options:
void symbol (DBusGMethodInvocation *context)
and return some sort of "not implemented" error via
dbus_g_method_return_error (context, ...)
-"""
+""")
sys.exit(1)
diff --git a/tools/libglibcodegen.py b/tools/libglibcodegen.py
index d465b740..6cd1a627 100644
--- a/tools/libglibcodegen.py
+++ b/tools/libglibcodegen.py
@@ -4,7 +4,7 @@ The master copy of this library is in the telepathy-glib repository -
please make any changes there.
"""
-# Copyright (C) 2006, 2007 Collabora Limited
+# Copyright (C) 2006-2008 Collabora Limited
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -21,44 +21,15 @@ please make any changes there.
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-from string import ascii_letters, digits
-
-
-NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
-
-_ASCII_ALNUM = ascii_letters + digits
-
-
-def camelcase_to_lower(s):
- out ="";
- out += s[0].lower()
- last_upper=False
- if s[0].isupper():
- last_upper=True
- for i in range(1,len(s)):
- if s[i].isupper():
- if last_upper:
- if (i+1) < len(s) and s[i+1].islower():
- out += "_" + s[i].lower()
- else:
- out += s[i].lower()
- else:
- out += "_" + s[i].lower()
- last_upper=True
- else:
- out += s[i]
- last_upper=False
- return out
-
-
-def camelcase_to_upper(s):
- return camelcase_to_lower(s).upper()
-
-
-def cmp_by_name(node1, node2):
- return cmp(node1.getAttributeNode("name").nodeValue,
- node2.getAttributeNode("name").nodeValue)
-
+from libtpcodegen import NS_TP, \
+ Signature, \
+ cmp_by_name, \
+ escape_as_identifier, \
+ get_by_path, \
+ get_descendant_text, \
+ get_docstring, \
+ xml_escape, \
+ get_deprecated
def dbus_gutils_wincaps_to_uscore(s):
"""Bug-for-bug compatible Python port of _dbus_gutils_wincaps_to_uscore
@@ -77,60 +48,6 @@ def dbus_gutils_wincaps_to_uscore(s):
return ret
-def escape_as_identifier(identifier):
- """Escape the given string to be a valid D-Bus object path or service
- name component, using a reversible encoding to ensure uniqueness.
-
- The reversible encoding is as follows:
-
- * The empty string becomes '_'
- * Otherwise, each non-alphanumeric character is replaced by '_' plus
- two lower-case hex digits; the same replacement is carried out on
- the first character, if it's a digit
- """
- # '' -> '_'
- if not identifier:
- return '_'
-
- # A bit of a fast path for strings which are already OK.
- # We deliberately omit '_' because, for reversibility, that must also
- # be escaped.
- if (identifier.strip(_ASCII_ALNUM) == '' and
- identifier[0] in ascii_letters):
- return identifier
-
- # The first character may not be a digit
- if identifier[0] not in ascii_letters:
- ret = ['_%02x' % ord(identifier[0])]
- else:
- ret = [identifier[0]]
-
- # Subsequent characters may be digits or ASCII letters
- for c in identifier[1:]:
- if c in _ASCII_ALNUM:
- ret.append(c)
- else:
- ret.append('_%02x' % ord(c))
-
- return ''.join(ret)
-
-
-def get_docstring(element):
- docstring = None
- for x in element.childNodes:
- if x.namespaceURI == NS_TP and x.localName == 'docstring':
- docstring = x
- if docstring is not None:
- docstring = docstring.toxml().replace('\n', ' ').strip()
- if docstring.startswith('<tp:docstring>'):
- docstring = docstring[14:].lstrip()
- if docstring.endswith('</tp:docstring>'):
- docstring = docstring[:-15].rstrip()
- if docstring in ('<tp:docstring/>', ''):
- docstring = ''
- return docstring
-
-
def signal_to_marshal_type(signal):
"""
return a list of strings indicating the marshalling type for this signal.
@@ -183,69 +100,6 @@ def method_to_glue_marshal_name(method, prefix):
return prefix + '_marshal_VOID__' + name
-class _SignatureIter:
- """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
- can run genginterface in a limited environment with only Python
- (like Scratchbox).
- """
- def __init__(self, string):
- self.remaining = string
-
- def next(self):
- if self.remaining == '':
- raise StopIteration
-
- signature = self.remaining
- block_depth = 0
- block_type = None
- end = len(signature)
-
- for marker in range(0, end):
- cur_sig = signature[marker]
-
- if cur_sig == 'a':
- pass
- elif cur_sig == '{' or cur_sig == '(':
- if block_type == None:
- block_type = cur_sig
-
- if block_type == cur_sig:
- block_depth = block_depth + 1
-
- elif cur_sig == '}':
- if block_type == '{':
- block_depth = block_depth - 1
-
- if block_depth == 0:
- end = marker
- break
-
- elif cur_sig == ')':
- if block_type == '(':
- block_depth = block_depth - 1
-
- if block_depth == 0:
- end = marker
- break
-
- else:
- if block_depth == 0:
- end = marker
- break
-
- end = end + 1
- self.remaining = signature[end:]
- return Signature(signature[0:end])
-
-
-class Signature(str):
- """A string, iteration over which is by D-Bus single complete types
- rather than characters.
- """
- def __iter__(self):
- return _SignatureIter(self)
-
-
def type_to_gtype(s):
if s == 'y': #byte
return ("guchar ", "G_TYPE_UCHAR","UCHAR", False)
@@ -292,12 +146,15 @@ def type_to_gtype(s):
elif s == 'ab': #boolean array
return ("GArray *", "DBUS_TYPE_G_BOOLEAN_ARRAY", "BOXED", True)
elif s == 'ao': #object path array
- return ("GPtrArray *", "DBUS_TYPE_G_OBJECT_ARRAY", "BOXED", True)
+ return ("GPtrArray *",
+ 'dbus_g_type_get_collection ("GPtrArray",'
+ ' DBUS_TYPE_G_OBJECT_PATH)',
+ "BOXED", True)
elif s == 'a{ss}': #hash table of string to string
return ("GHashTable *", "DBUS_TYPE_G_STRING_STRING_HASHTABLE", "BOXED", False)
elif s[:2] == 'a{': #some arbitrary hash tables
if s[2] not in ('y', 'b', 'n', 'q', 'i', 'u', 's', 'o', 'g'):
- raise Exception, "can't index a hashtable off non-basic type " + s
+ raise Exception("can't index a hashtable off non-basic type " + s)
first = type_to_gtype(s[2])
second = type_to_gtype(s[3:-1])
return ("GHashTable *", "(dbus_g_type_get_map (\"GHashTable\", " + first[1] + ", " + second[1] + "))", "BOXED", False)
@@ -312,9 +169,4 @@ def type_to_gtype(s):
return ("GValueArray *", gtype, "BOXED", True)
# we just don't know ..
- raise Exception, "don't know the GType for " + s
-
-
-def xml_escape(s):
- s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
- return s.replace('<', '&lt;').replace('>', '&gt;')
+ raise Exception("don't know the GType for " + s)
diff --git a/tools/libtpcodegen.py b/tools/libtpcodegen.py
new file mode 100644
index 00000000..99de6634
--- /dev/null
+++ b/tools/libtpcodegen.py
@@ -0,0 +1,247 @@
+"""Library code for language-independent D-Bus-related code generation.
+
+The master copy of this library is in the telepathy-glib repository -
+please make any changes there.
+"""
+
+# Copyright (C) 2006-2008 Collabora Limited
+#
+# 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+import sys
+from string import ascii_letters, digits
+
+
+NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+
+_ASCII_ALNUM = ascii_letters + digits
+
+if sys.version_info[0] >= 3:
+ def u(s):
+ """Return s, which must be a str literal with no non-ASCII characters.
+ This is like a more restricted form of the Python 2 u'' syntax.
+ """
+ return s.encode('ascii').decode('ascii')
+else:
+ def u(s):
+ """Return a Unicode version of s, which must be a str literal
+ (a bytestring) in which each byte is an ASCII character.
+ This is like a more restricted form of the u'' syntax.
+ """
+ return s.decode('ascii')
+
+def file_set_contents(filename, contents):
+ try:
+ os.remove(filename)
+ except OSError:
+ pass
+ try:
+ os.remove(filename + '.tmp')
+ except OSError:
+ pass
+
+ open(filename + '.tmp', 'wb').write(contents)
+ os.rename(filename + '.tmp', filename)
+
+def cmp_by_name(node1, node2):
+ return cmp(node1.getAttributeNode("name").nodeValue,
+ node2.getAttributeNode("name").nodeValue)
+
+def key_by_name(node):
+ return node.getAttributeNode("name").nodeValue
+
+def escape_as_identifier(identifier):
+ """Escape the given string to be a valid D-Bus object path or service
+ name component, using a reversible encoding to ensure uniqueness.
+
+ The reversible encoding is as follows:
+
+ * The empty string becomes '_'
+ * Otherwise, each non-alphanumeric character is replaced by '_' plus
+ two lower-case hex digits; the same replacement is carried out on
+ the first character, if it's a digit
+ """
+ # '' -> '_'
+ if not identifier:
+ return '_'
+
+ # A bit of a fast path for strings which are already OK.
+ # We deliberately omit '_' because, for reversibility, that must also
+ # be escaped.
+ if (identifier.strip(_ASCII_ALNUM) == '' and
+ identifier[0] in ascii_letters):
+ return identifier
+
+ # The first character may not be a digit
+ if identifier[0] not in ascii_letters:
+ ret = ['_%02x' % ord(identifier[0])]
+ else:
+ ret = [identifier[0]]
+
+ # Subsequent characters may be digits or ASCII letters
+ for c in identifier[1:]:
+ if c in _ASCII_ALNUM:
+ ret.append(c)
+ else:
+ ret.append('_%02x' % ord(c))
+
+ return ''.join(ret)
+
+
+def get_by_path(element, path):
+ branches = path.split('/')
+ branch = branches[0]
+
+ # Is the current branch an attribute, if so, return the attribute value
+ if branch[0] == '@':
+ return element.getAttribute(branch[1:])
+
+ # Find matching children for the branch
+ children = []
+ if branch == '..':
+ children.append(element.parentNode)
+ else:
+ for x in element.childNodes:
+ if x.localName == branch:
+ children.append(x)
+
+ ret = []
+ # If this is not the last path element, recursively gather results from
+ # children
+ if len(branches) > 1:
+ for x in children:
+ add = get_by_path(x, '/'.join(branches[1:]))
+ if isinstance(add, list):
+ ret += add
+ else:
+ return add
+ else:
+ ret = children
+
+ return ret
+
+
+def get_docstring(element):
+ docstring = None
+ for x in element.childNodes:
+ if x.namespaceURI == NS_TP and x.localName == 'docstring':
+ docstring = x
+ if docstring is not None:
+ docstring = docstring.toxml().replace('\n', ' ').strip()
+ if docstring.startswith('<tp:docstring>'):
+ docstring = docstring[14:].lstrip()
+ if docstring.endswith('</tp:docstring>'):
+ docstring = docstring[:-15].rstrip()
+ if docstring in ('<tp:docstring/>', ''):
+ docstring = ''
+ return docstring
+
+def get_deprecated(element):
+ text = []
+ for x in element.childNodes:
+ if hasattr(x, 'data'):
+ text.append(x.data.replace('\n', ' ').strip())
+ else:
+ # This caters for tp:dbus-ref elements, but little else.
+ if x.childNodes and hasattr(x.childNodes[0], 'data'):
+ text.append(x.childNodes[0].data.replace('\n', ' ').strip())
+ return ' '.join(text)
+
+def get_descendant_text(element_or_elements):
+ if not element_or_elements:
+ return ''
+ if isinstance(element_or_elements, list):
+ return ''.join(map(get_descendant_text, element_or_elements))
+ parts = []
+ for x in element_or_elements.childNodes:
+ if x.nodeType == x.TEXT_NODE:
+ parts.append(x.nodeValue)
+ elif x.nodeType == x.ELEMENT_NODE:
+ parts.append(get_descendant_text(x))
+ else:
+ pass
+ return ''.join(parts)
+
+
+class _SignatureIter:
+ """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
+ can run genginterface in a limited environment with only Python
+ (like Scratchbox).
+ """
+ def __init__(self, string):
+ self.remaining = string
+
+ def next(self):
+ return self.__next__()
+
+ def __next__(self):
+ if self.remaining == '':
+ raise StopIteration
+
+ signature = self.remaining
+ block_depth = 0
+ block_type = None
+ end = len(signature)
+
+ for marker in range(0, end):
+ cur_sig = signature[marker]
+
+ if cur_sig == 'a':
+ pass
+ elif cur_sig == '{' or cur_sig == '(':
+ if block_type == None:
+ block_type = cur_sig
+
+ if block_type == cur_sig:
+ block_depth = block_depth + 1
+
+ elif cur_sig == '}':
+ if block_type == '{':
+ block_depth = block_depth - 1
+
+ if block_depth == 0:
+ end = marker
+ break
+
+ elif cur_sig == ')':
+ if block_type == '(':
+ block_depth = block_depth - 1
+
+ if block_depth == 0:
+ end = marker
+ break
+
+ else:
+ if block_depth == 0:
+ end = marker
+ break
+
+ end = end + 1
+ self.remaining = signature[end:]
+ return Signature(signature[0:end])
+
+
+class Signature(str):
+ """A string, iteration over which is by D-Bus single complete types
+ rather than characters.
+ """
+ def __iter__(self):
+ return _SignatureIter(self)
+
+
+def xml_escape(s):
+ s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
+ return s.replace('<', '&lt;').replace('>', '&gt;')