diff options
-rw-r--r-- | extensions/Makefile.am | 16 | ||||
-rw-r--r-- | extensions/OLPC_Activity_Properties.xml | 9 | ||||
-rw-r--r-- | extensions/OLPC_Buddy_Info.xml | 50 | ||||
-rw-r--r-- | extensions/all.xml | 2 | ||||
-rw-r--r-- | tools/Makefile.am | 3 | ||||
-rw-r--r-- | tools/glib-ginterface-gen.py | 331 | ||||
-rw-r--r-- | tools/libglibcodegen.py | 180 | ||||
-rw-r--r-- | tools/libtpcodegen.py | 247 |
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_##x (\\' + % (self.prefix_, self.node_name_lc)) + self.d(' * klass, my_object_##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('&', '&').replace("'", ''').replace('"', '"') - return s.replace('<', '<').replace('>', '>') + 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('&', '&').replace("'", ''').replace('"', '"') + return s.replace('<', '<').replace('>', '>') |