diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index e5f3c41..dba0b28 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,124 +1,123 @@ # Some useful commands add_custom_command(OUTPUT FIXME.out COMMAND egrep ARGS -A 5 '[F]IXME|[T]ODO|[X]XX' ${CMAKE_SOURCE_DIR}/TelepathyQt/*.[ch]* ${CMAKE_SOURCE_DIR}/TelepathyQt/*.[ch]* > FIXME.out || true) add_custom_target(check-local DEPENDS FIXME.out) execute_process(COMMAND ${SH} tools/git-which-branch.sh misc | tr -d '\n' | tr -C "[:alnum:]" _ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_BRANCH_CURRENT) if (GIT_BRANCH_CURRENT) string(LENGTH ${GIT_BRANCH_CURRENT} HAVE_GIT_BRANCH) if (HAVE_GIT_BRANCH) string(REPLACE "\n" "" GIT_BRANCH_CURRENT ${GIT_BRANCH_CURRENT}) set(UPLOAD_BRANCH_TO people.freedesktop.org:public_html/telepathy-qt) add_custom_target(upload-branch-docs rsync -rtzvPp --chmod=a+rX doc/html/ ${UPLOAD_BRANCH_TO}-${GIT_BRANCH_CURRENT} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_dependencies(upload-branch-docs doxygen-doc) endif (HAVE_GIT_BRANCH) endif (GIT_BRANCH_CURRENT) if (PERL_FOUND) add_custom_target(maintainer-fix-qt-links-in-docs ${PERL_EXECUTABLE} doc/html/installdox -l qt.tags@http://doc.qt.nokia.com/latest/ doc/html/*.html WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_dependencies(maintainer-fix-qt-links-in-docs doxygen-doc _maintainer-upload-release-check) endif (PERL_FOUND) add_custom_target(maintainer-upload-release-docs rsync -rtOvzPp --chmod=Dg+s,ug+rwX,o=rX doc/html/ telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/doc/telepathy-qt/ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) if (PERL_FOUND) add_dependencies(maintainer-upload-release-docs maintainer-fix-qt-links-in-docs) else (PERL_FOUND) add_dependencies(maintainer-upload-release-docs doxygen-doc _maintainer-upload-release-check) endif (PERL_FOUND) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/maintainer-upload-release-check.sh " #!/bin/sh case ${PACKAGE_VERSION} in (*.*.*.*) echo \"${PACKAGE_VERSION} is not a release\" >&2; exit 2; ;; esac test -f ${CMAKE_BINARY_DIR}/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz if ! test -f ${CMAKE_BINARY_DIR}/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz.asc; then gpg --detach-sign -a ${CMAKE_BINARY_DIR}/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz; fi; gpg --verify ${CMAKE_BINARY_DIR}/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz.asc ") add_custom_target(_maintainer-upload-release-check ${SH} ${CMAKE_CURRENT_BINARY_DIR}/maintainer-upload-release-check.sh) add_custom_target(maintainer-upload-release rsync -vzP ${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/${PACKAGE_NAME}/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz COMMAND rsync -vzP ${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz.asc telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/${PACKAGE_NAME}/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz.asc WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_dependencies(maintainer-upload-release _maintainer-upload-release-check maintainer-upload-release-docs) set(toolchain_files c-constants-gen.py check-misc.sh check-whitespace.sh git-which-branch.sh glib-ginterface-gen.py glib-gtypes-generator.py glib-interfaces-gen.py - glib-signals-marshal-gen.py libtpcodegen.py libglibcodegen.py libqtcodegen.py qt-client-gen.py qt-constants-gen.py qt-types-gen.py manager-file.py with-session-bus.sh xincludator.py ) string(REPLACE "." " " sh_toolchain_files ${toolchain_files}) set(TELEPATHY_SPEC_SRCDIR ${CMAKE_SOURCE_DIR}/../telepathy-spec) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/maintainer-update-from-telepathy-spec.sh " #!/bin/sh set -e cd ${CMAKE_SOURCE_DIR} for x in ${sh_toolchain_files}; do if test -f ${TELEPATHY_SPEC_SRCDIR}/tools/$$x; then cp ${TELEPATHY_SPEC_SRCDIR}/tools/$$x $$x; fi; done ") add_custom_target(maintainer-update-from-telepathy-spec ${SH} ${CMAKE_CURRENT_BINARY_DIR}/maintainer-update-from-telepathy-spec.sh WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) set(TELEPATHY_GLIB_SRCDIR ${CMAKE_SOURCE_DIR}/../telepathy-glib) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/maintainer-update-from-telepathy-glib.sh " #!/bin/sh set -e cd ${CMAKE_SOURCE_DIR} for x in ${sh_toolchain_files}; do if test -f ${TELEPATHY_GLIB_SRCDIR}/tools/$$x; then cp ${TELEPATHY_GLIB_SRCDIR}/tools/$$x $$x; fi; done ") add_custom_target(maintainer-update-from-telepathy-glib ${SH} ${CMAKE_CURRENT_BINARY_DIR}/maintainer-update-from-telepathy-glib.sh WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) diff --git a/tools/c-constants-gen.py b/tools/c-constants-gen.py index 8969ffd..a08afee 100644 --- a/tools/c-constants-gen.py +++ b/tools/c-constants-gen.py @@ -1,154 +1,182 @@ #!/usr/bin/python from sys import argv, stdout, stderr import xml.dom.minidom +from libtpcodegen import file_set_contents, u from libglibcodegen import NS_TP, get_docstring, \ get_descendant_text, get_by_path class Generator(object): - def __init__(self, prefix, dom): + def __init__(self, prefix, dom, output_base): self.prefix = prefix + '_' self.spec = get_by_path(dom, "spec")[0] + self.output_base = output_base + self.__header = [] + self.__docs = [] + def __call__(self): self.do_header() self.do_body() self.do_footer() + file_set_contents(self.output_base + '.h', u('').join(self.__header).encode('utf-8')) + file_set_contents(self.output_base + '-gtk-doc.h', u('').join(self.__docs).encode('utf-8')) + def write(self, code): - stdout.write(code.encode('utf-8')) + self.__header.append(code) + + def d(self, code): + self.__docs.append(code) # Header def do_header(self): self.write('/* Generated from ') self.write(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: self.write(', version ' + get_descendant_text(version)) self.write('\n\n') for copyright in get_by_path(self.spec, 'copyright'): self.write(get_descendant_text(copyright)) self.write('\n') self.write(get_descendant_text(get_by_path(self.spec, 'license'))) self.write('\n') self.write(get_descendant_text(get_by_path(self.spec, 'docstring'))) self.write(""" */ #ifdef __cplusplus extern "C" { #endif \n""") # Body def do_body(self): for elem in self.spec.getElementsByTagNameNS(NS_TP, '*'): if elem.localName == 'flags': self.do_flags(elem) elif elem.localName == 'enum': self.do_enum(elem) def do_flags(self, flags): name = flags.getAttribute('plural') or flags.getAttribute('name') value_prefix = flags.getAttribute('singular') or \ flags.getAttribute('value-prefix') or \ flags.getAttribute('name') - self.write("""\ + self.d("""\ /** - * -%s: + * %s: """ % (self.prefix + name).replace('_', '')) for flag in get_by_path(flags, 'flag'): self.do_gtkdoc(flag, value_prefix) - self.write(' *\n') + self.d(' *\n') docstrings = get_by_path(flags, 'docstring') if docstrings: - self.write("""\ + self.d("""\ * * """ % get_descendant_text(docstrings).replace('\n', ' ')) - self.write("""\ + self.d("""\ * Bitfield/set of flags generated from the Telepathy specification. */ -typedef enum { """) + + self.write("typedef enum /*< flags >*/ {\n") + for flag in get_by_path(flags, 'flag'): self.do_val(flag, value_prefix) self.write("""\ } %s; """ % (self.prefix + name).replace('_', '')) def do_enum(self, enum): name = enum.getAttribute('singular') or enum.getAttribute('name') value_prefix = enum.getAttribute('singular') or \ enum.getAttribute('value-prefix') or \ enum.getAttribute('name') name_plural = enum.getAttribute('plural') or \ enum.getAttribute('name') + 's' - self.write("""\ + self.d("""\ /** - * -%s: + * %s: """ % (self.prefix + name).replace('_', '')) vals = get_by_path(enum, 'enumvalue') for val in vals: self.do_gtkdoc(val, value_prefix) - self.write(' *\n') + self.d(' *\n') docstrings = get_by_path(enum, 'docstring') if docstrings: - self.write("""\ + self.d("""\ * * """ % get_descendant_text(docstrings).replace('\n', ' ')) - self.write("""\ + self.d("""\ * Bitfield/set of flags generated from the Telepathy specification. */ -typedef enum { """) + + self.write("typedef enum {\n") + for val in vals: self.do_val(val, value_prefix) - self.write("""\ -} %(mixed-name)s; + self.write("} %s;\n" % (self.prefix + name).replace('_', '')) + self.d("""\ /** - * NUM_%(upper-plural)s: + * %(upper-prefix)sNUM_%(upper-plural)s: * * 1 higher than the highest valid value of #%(mixed-name)s. */ -#define NUM_%(upper-plural)s (%(last-val)s+1) + +/** + * NUM_%(upper-prefix)s%(upper-plural)s: (skip) + * + * 1 higher than the highest valid value of #%(mixed-name)s. + * In new code, use %(upper-prefix)sNUM_%(upper-plural)s instead. + */ +""" % {'mixed-name' : (self.prefix + name).replace('_', ''), + 'upper-prefix' : self.prefix.upper(), + 'upper-plural' : name_plural.upper(), + 'last-val' : vals[-1].getAttribute('value')}) + + self.write("""\ +#define %(upper-prefix)sNUM_%(upper-plural)s (%(last-val)s+1) +#define NUM_%(upper-prefix)s%(upper-plural)s %(upper-prefix)sNUM_%(upper-plural)s """ % {'mixed-name' : (self.prefix + name).replace('_', ''), - 'upper-plural' : (self.prefix + name_plural).upper(), + 'upper-prefix' : self.prefix.upper(), + 'upper-plural' : name_plural.upper(), 'last-val' : vals[-1].getAttribute('value')}) def do_val(self, val, value_prefix): name = val.getAttribute('name') suffix = val.getAttribute('suffix') use_name = (self.prefix + value_prefix + '_' + \ (suffix or name)).upper() assert not (name and suffix) or name == suffix, \ 'Flag/enumvalue name %s != suffix %s' % (name, suffix) self.write(' %s = %s,\n' % (use_name, val.getAttribute('value'))) def do_gtkdoc(self, node, value_prefix): - self.write(' * @') - self.write((self.prefix + value_prefix + '_' + + self.d(' * @') + self.d((self.prefix + value_prefix + '_' + node.getAttribute('suffix')).upper()) - self.write(': \n') + self.d(get_descendant_text(docstring).replace('\n', ' ')) + self.d(']]>\n') # Footer def do_footer(self): self.write(""" #ifdef __cplusplus } #endif """) if __name__ == '__main__': argv = argv[1:] - Generator(argv[0], xml.dom.minidom.parse(argv[1]))() + Generator(argv[0], xml.dom.minidom.parse(argv[1]), argv[2])() diff --git a/tools/glib-ginterface-gen.py b/tools/glib-ginterface-gen.py index 13f7f69..c0ce20d 100644 --- a/tools/glib-ginterface-gen.py +++ b/tools/glib-ginterface-gen.py @@ -1,802 +1,832 @@ #!/usr/bin/python # glib-ginterface-gen.py: service-side interface generator # # Generate dbus-glib 0.x service GInterfaces from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006, 2007 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 sys import os.path import xml.dom.minidom -from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ - 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, headers, end_headers, not_implemented_func, allow_havoc): self.dom = dom self.__header = [] self.__body = [] + self.__docs = [] assert prefix.endswith('_') assert not signal_marshal_prefix.endswith('_') # The main_prefix, sub_prefix thing is to get: # FOO_ -> (FOO_, _) # FOO_SVC_ -> (FOO_, _SVC_) # but # FOO_BAR/ -> (FOO_BAR_, _) # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_) if '/' in prefix: main_prefix, sub_prefix = prefix.upper().split('/', 1) prefix = prefix.replace('/', '_') else: main_prefix, sub_prefix = prefix.upper().split('_', 1) self.MAIN_PREFIX_ = main_prefix + '_' self._SUB_PREFIX_ = '_' + sub_prefix self.Prefix_ = prefix self.Prefix = prefix.replace('_', '') 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 self.not_implemented_func = not_implemented_func self.allow_havoc = allow_havoc def h(self, s): self.__header.append(s) 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('_', '') node_name_lc = self.node_name_lc = node_name.lower() node_name_uc = self.node_name_uc = node_name.upper() interfaces = node.getElementsByTagName('interface') assert len(interfaces) == 1, interfaces interface = interfaces[0] self.iface_name = interface.getAttribute('name') tmp = interface.getAttribute('tp:implement-service') if tmp == "no": return tmp = interface.getAttribute('tp:causes-havoc') 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('') methods = interface.getElementsByTagName('method') signals = interface.getElementsByTagName('signal') properties = interface.getElementsByTagName('property') # Don't put properties in dbus-glib glue glue_properties = [] self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed)) self.b(' GTypeInterface parent_class;') for method in methods: self.b(' %s %s;' % self.get_method_impl_names(method)) self.b('};') self.b('') if signals: self.b('enum {') for signal in signals: self.b(' %s,' % self.get_signal_const_entry(signal)) self.b(' N_%s_SIGNALS' % node_name_uc) self.b('};') self.b('static guint %s_signals[N_%s_SIGNALS] = {0};' % (node_name_lc, node_name_uc)) self.b('') self.b('static void %s%s_base_init (gpointer klass);' % (self.prefix_, node_name_lc)) self.b('') self.b('GType') self.b('%s%s_get_type (void)' % (self.prefix_, node_name_lc)) self.b('{') self.b(' static GType type = 0;') self.b('') self.b(' if (G_UNLIKELY (type == 0))') self.b(' {') self.b(' static const GTypeInfo info = {') self.b(' sizeof (%s%sClass),' % (self.Prefix, node_name_mixed)) self.b(' %s%s_base_init, /* base_init */' % (self.prefix_, node_name_lc)) self.b(' NULL, /* base_finalize */') self.b(' NULL, /* class_init */') self.b(' NULL, /* class_finalize */') self.b(' NULL, /* class_data */') self.b(' 0,') self.b(' 0, /* n_preallocs */') self.b(' NULL /* instance_init */') self.b(' };') self.b('') self.b(' type = g_type_register_static (G_TYPE_INTERFACE,') self.b(' "%s%s", &info, 0);' % (self.Prefix, node_name_mixed)) self.b(' }') self.b('') self.b(' return type;') 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.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.h(' *') - self.h(' * In a full implementation of this interface (i.e. all') - self.h(' * methods implemented), the interface initialization') - self.h(' * function used in G_IMPLEMENT_INTERFACE() would') - self.h(' * typically look like this:') - self.h(' *') - self.h(' * ') - self.h(' * static void') - self.h(' * implement_%s (gpointer klass,' % self.node_name_lc) - self.h(' * gpointer unused G_GNUC_UNUSED)') - self.h(' * {') - # "#" is special to gtkdoc under some circumstances; it appears - # that escaping "##" as "##" or "##" doesn't work, - # but adding an extra hash symbol does. Thanks, gtkdoc :-( - self.h(' * #define IMPLEMENT(x) %s%s_implement_###x (\\' + 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(' * ') + 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.h(' * klass, my_object_###x)') + 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.h(' * IMPLEMENT (%s);' % class_member_name) + self.d(' * IMPLEMENT (%s);' % class_member_name) - self.h(' * #undef IMPLEMENT') - self.h(' * }') - self.h(' * ') + self.d(' * #undef IMPLEMENT') + self.d(' * }') + self.d(' * ') else: - self.h(' * This interface has no D-Bus methods, so an') - self.h(' * implementation can typically pass %NULL to') - self.h(' * G_IMPLEMENT_INTERFACE() as the interface') - self.h(' * initialization function.') + 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.h(' */') + self.d(' */') + self.d('') self.h('typedef struct _%s%sClass %s%sClass;' % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) self.h('') self.h('GType %s%s_get_type (void);' % (self.prefix_, node_name_lc)) gtype = self.current_gtype = \ self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc classname = self.Prefix + node_name_mixed self.h('#define %s \\\n (%s%s_get_type ())' % (gtype, self.prefix_, node_name_lc)) self.h('#define %s%s(obj) \\\n' ' (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))' % (self.PREFIX_, node_name_uc, gtype, classname)) self.h('#define %sIS%s%s(obj) \\\n' ' (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))' % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype)) self.h('#define %s%s_GET_CLASS(obj) \\\n' ' (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))' % (self.PREFIX_, node_name_uc, gtype, classname)) self.h('') self.h('') base_init_code = [] for method in methods: self.do_method(method) for signal in signals: base_init_code.extend(self.do_signal(signal)) self.b('static inline void') self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)' % (self.prefix_, node_name_lc)) self.b('{') if properties: self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {' % (len(properties) + 1)) for m in properties: access = m.getAttribute('access') assert access in ('read', 'write', 'readwrite') 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') + prop_emits_changed = get_emits_changed(m) + + 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(' 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') self.b('%s%s_base_init (gpointer klass)' % (self.prefix_, node_name_lc)) self.b('{') self.b(' static gboolean initialized = FALSE;') self.b('') self.b(' if (!initialized)') self.b(' {') self.b(' initialized = TRUE;') self.b(' %s%s_base_init_once (klass);' % (self.prefix_, node_name_lc)) self.b(' }') # insert anything we need to do per implementation here self.b('}') self.h('') self.b('static const DBusGMethodInfo _%s%s_methods[] = {' % (self.prefix_, node_name_lc)) method_blob, offsets = self.get_method_glue(methods) 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('') self.b('static const DBusGObjectInfo _%s%s_object_info = {' % (self.prefix_, node_name_lc)) self.b(' 0,') # version self.b(' _%s%s_methods,' % (self.prefix_, node_name_lc)) self.b(' %d,' % len(methods)) self.b('"' + method_blob.replace('\0', '\\0') + '",') self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",') self.b('"' + self.get_property_glue(glue_properties).replace('\0', '\\0') + '",') self.b('};') self.b('') self.node_name_mixed = None self.node_name_lc = None self.node_name_uc = None def get_method_glue(self, methods): info = [] offsets = [] for method in methods: offsets.append(len(''.join(info))) info.append(self.iface_name + '\0') info.append(method.getAttribute('name') + '\0') info.append('A\0') # async counter = 0 for arg in method.getElementsByTagName('arg'): out = arg.getAttribute('direction') == 'out' name = arg.getAttribute('name') if not name: assert out name = 'arg%u' % counter counter += 1 info.append(name + '\0') if out: info.append('O\0') else: info.append('I\0') if out: info.append('F\0') # not const info.append('N\0') # not error or return info.append(arg.getAttribute('type') + '\0') info.append('\0') return ''.join(info) + '\0', offsets def do_method_glue(self, method, offset): 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)) def get_signal_glue(self, signals): info = [] for signal in signals: info.append(self.iface_name) info.append(signal.getAttribute('name')) return '\0'.join(info) + '\0\0' # the implementation can be the same get_property_glue = get_signal_glue def get_method_impl_names(self, method): dbus_method_name = method.getAttribute('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 in_class = [] # Examples refer to Thing.DoStuff (su) -> ii # DoStuff dbus_method_name = method.getAttribute('name') # do_stuff 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 + '_' + class_member_name) # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *, # const char *, guint, DBusGMethodInvocation); impl_name = stub_name + '_impl' # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *, # gint, gint); ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' + class_member_name) # Gather arguments in_args = [] out_args = [] for i in method.getElementsByTagName('arg'): name = i.getAttribute('name') direction = i.getAttribute('direction') or 'in' dtype = i.getAttribute('type') assert direction in ('in', 'out') if name: name = direction + '_' + name elif direction == 'in': name = direction + str(len(in_args)) else: name = direction + str(len(out_args)) ctype, gtype, marshaller, pointer = type_to_gtype(dtype) if pointer: ctype = 'const ' + ctype struct = (ctype, name) if direction == 'in': in_args.append(struct) 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: self.h(' %s%s,' % (ctype, name)) self.h(' DBusGMethodInvocation *context);') # Class member (in class definition) in_class.append(' %s %s;' % (impl_name, class_member_name)) # Stub definition (in body only - it's static) self.b('static void') self.b('%s (%s%s *self,' % (stub_name, self.Prefix, self.node_name_mixed)) for (ctype, name) in in_args: 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)') tmp = ['self'] + [name for (ctype, name) in in_args] + ['context'] self.b(' {') self.b(' (impl) (%s);' % ',\n '.join(tmp)) self.b(' }') self.b(' else') self.b(' {') if self.not_implemented_func: self.b(' %s (context);' % self.not_implemented_func) else: self.b(' GError e = { DBUS_GERROR, ') self.b(' DBUS_GERROR_UNKNOWN_METHOD,') self.b(' "Method not implemented" };') self.b('') self.b(' dbus_g_method_return_error (context, &e);') self.b(' }') self.b('}') self.b('') # Implementation registration (in both header and body) self.h('void %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(' * %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') self.h('/* this comment is to stop gtkdoc realising this is static */') self.h(('void %s (' % ret_name) + (',\n '.join(tmp)) + ');') self.h('static inline void') self.h(('%s (' % ret_name) + (',\n '.join(tmp)) + ')') self.h('{') tmp = ['context'] + [name for (ctype, name) in out_args] self.h(' dbus_g_method_return (' + ',\n '.join(tmp) + ');') self.h('}') self.h('') return in_class def get_signal_const_entry(self, signal): assert self.node_name_uc is not None return ('SIGNAL_%s_%s' % (self.node_name_uc, signal.getAttribute('name'))) def do_signal(self, signal): assert self.node_name_mixed is not None in_base_init = [] # for signal: Thing::StuffHappened (s, u) # we want to emit: # void tp_svc_thing_emit_stuff_happened (gpointer instance, # 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_' + ugly_name.lower()) const_name = self.get_signal_const_entry(signal) # Gather arguments args = [] for i in signal.getElementsByTagName('arg'): name = i.getAttribute('name') dtype = i.getAttribute('type') tp_type = i.getAttribute('tp:type') if name: name = 'arg_' + name else: name = 'arg' + str(len(args)) ctype, gtype, marshaller, pointer = type_to_gtype(dtype) if pointer: ctype = 'const ' + ctype struct = (ctype, name, gtype) args.append(struct) tmp = (['gpointer instance'] + [ctype + name for (ctype, name, gtype) in args]) self.h(('void %s (' % stub_name) + (',\n '.join(tmp)) + ');') # 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)) + ')') self.b('{') self.b(' g_assert (instance != NULL);') self.b(' g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));' % (self.current_gtype)) tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name), '0'] + [name for (ctype, name, gtype) in args]) self.b(' g_signal_emit (' + ',\n '.join(tmp) + ');') self.b('}') self.b('') signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', '-') - in_base_init.append(' /**') - in_base_init.append(' * %s%s::%s:' + + 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: - in_base_init.append(' * @%s: %s (FIXME, generate documentation)' + self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) - in_base_init.append(' *') - in_base_init.append(' * The %s D-Bus signal is emitted whenever ' + self.d(' *') + self.d(' * The %s D-Bus signal is emitted whenever ' 'this GObject signal is.' % dbus_name) - in_base_init.append(' */') + 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",' % 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)) in_base_init.append('') 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(cmp_by_name) + nodes.sort(key=key_by_name) self.h('#include ') self.h('#include ') - if self.have_properties(nodes): - self.h('#include ') + 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"' % self.basename) self.b('') - for header in self.headers: - self.b('#include %s' % header) - self.b('') for node in nodes: self.do_node(node) self.h('') self.h('G_END_DECLS') self.b('') for header in self.end_headers: self.b('#include %s' % header) self.h('') self.b('') - open(self.basename + '.h', 'w').write('\n'.join(self.__header)) - open(self.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: --include='' (may be repeated) --include='"header.h"' (ditto) --include-end='"header.h"' (ditto) Include extra headers in the generated .c file --signal-marshal-prefix='prefix' Use the given prefix on generated signal marshallers (default is prefix.lower()). --filename='BASENAME' Set the basename for the output files (default is prefix.lower() + 'ginterfaces') --not-implemented-func='symbol' Set action when methods not implemented in the interface vtable are called. symbol must have signature void symbol (DBusGMethodInvocation *context) and return some sort of "not implemented" error via dbus_g_method_return_error (context, ...) -""" +""") sys.exit(1) if __name__ == '__main__': from getopt import gnu_getopt options, argv = gnu_getopt(sys.argv[1:], '', ['filename=', 'signal-marshal-prefix=', 'include=', 'include-end=', 'allow-unstable', 'not-implemented-func=']) try: prefix = argv[1] except IndexError: cmdline_error() basename = prefix.lower() + 'ginterfaces' signal_marshal_prefix = prefix.lower().rstrip('_') headers = [] end_headers = [] not_implemented_func = '' allow_havoc = False for option, value in options: if option == '--filename': basename = value elif option == '--signal-marshal-prefix': signal_marshal_prefix = value elif option == '--include': if value[0] not in '<"': value = '"%s"' % value headers.append(value) elif option == '--include-end': if value[0] not in '<"': value = '"%s"' % value end_headers.append(value) elif option == '--not-implemented-func': not_implemented_func = value elif option == '--allow-unstable': allow_havoc = True try: dom = xml.dom.minidom.parse(argv[0]) except IndexError: cmdline_error() Generator(dom, prefix, basename, signal_marshal_prefix, headers, end_headers, not_implemented_func, allow_havoc)() diff --git a/tools/glib-gtypes-generator.py b/tools/glib-gtypes-generator.py index ebc2ad4..1477bd3 100644 --- a/tools/glib-gtypes-generator.py +++ b/tools/glib-gtypes-generator.py @@ -1,291 +1,304 @@ #!/usr/bin/python # Generate GLib GInterfaces from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006, 2007 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 sys import xml.dom.minidom +from libtpcodegen import file_set_contents, u from libglibcodegen import escape_as_identifier, \ get_docstring, \ NS_TP, \ Signature, \ type_to_gtype, \ xml_escape def types_to_gtypes(types): return [type_to_gtype(t)[1] for t in types] class GTypesGenerator(object): def __init__(self, dom, output, mixed_case_prefix): self.dom = dom self.Prefix = mixed_case_prefix self.PREFIX_ = self.Prefix.upper() + '_' self.prefix_ = self.Prefix.lower() + '_' - self.header = open(output + '.h', 'w') - self.body = open(output + '-body.h', 'w') + self.header = [] + self.body = [] + self.docs = [] + self.output = output - for f in (self.header, self.body): - f.write('/* Auto-generated, do not edit.\n *\n' - ' * This file may be distributed under the same terms\n' - ' * as the specification from which it was generated.\n' - ' */\n\n') + for f in (self.header, self.body, self.docs): + f.append('/* Auto-generated, do not edit.\n *\n' + ' * This file may be distributed under the same terms\n' + ' * as the specification from which it was generated.\n' + ' */\n\n') # keys are e.g. 'sv', values are the key escaped self.need_mappings = {} # keys are the contents of the struct (e.g. 'sssu'), values are the # key escaped self.need_structs = {} # keys are the contents of the struct (e.g. 'sssu'), values are the # key escaped self.need_struct_arrays = {} # keys are the contents of the array (unlike need_struct_arrays!), # values are the key escaped self.need_other_arrays = {} def h(self, code): - self.header.write(code.encode("utf-8")) + self.header.append(code) def c(self, code): - self.body.write(code.encode("utf-8")) + self.body.append(code) + + def d(self, code): + self.docs.append(code) def do_mapping_header(self, mapping): members = mapping.getElementsByTagNameNS(NS_TP, 'member') assert len(members) == 2 impl_sig = ''.join([elt.getAttribute('type') for elt in members]) esc_impl_sig = escape_as_identifier(impl_sig) name = (self.PREFIX_ + 'HASH_TYPE_' + mapping.getAttribute('name').upper()) impl = self.prefix_ + 'type_dbus_hash_' + esc_impl_sig docstring = get_docstring(mapping) or '(Undocumented)' - self.h('/**\n * %s:\n *\n' % name) - self.h(' * %s\n' % xml_escape(docstring)) - self.h(' *\n') - self.h(' * This macro expands to a call to a function\n') - self.h(' * that returns the #GType of a #GHashTable\n') - self.h(' * appropriate for representing a D-Bus\n') - self.h(' * dictionary of signature\n') - self.h(' * a{%s}.\n' % impl_sig) - self.h(' *\n') + self.d('/**\n * %s:\n *\n' % name.strip()) + self.d(' * %s\n' % xml_escape(docstring)) + self.d(' *\n') + self.d(' * This macro expands to a call to a function\n') + self.d(' * that returns the #GType of a #GHashTable\n') + self.d(' * appropriate for representing a D-Bus\n') + self.d(' * dictionary of signature\n') + self.d(' * a{%s}.\n' % impl_sig) + self.d(' *\n') key, value = members - self.h(' * Keys (D-Bus type %s,\n' + self.d(' * Keys (D-Bus type %s,\n' % key.getAttribute('type')) tp_type = key.getAttributeNS(NS_TP, 'type') if tp_type: - self.h(' * type %s,\n' % tp_type) - self.h(' * named %s):\n' + self.d(' * type %s,\n' % tp_type) + self.d(' * named %s):\n' % key.getAttribute('name')) docstring = get_docstring(key) or '(Undocumented)' - self.h(' * %s\n' % xml_escape(docstring)) - self.h(' *\n') + self.d(' * %s\n' % xml_escape(docstring)) + self.d(' *\n') - self.h(' * Values (D-Bus type %s,\n' + self.d(' * Values (D-Bus type %s,\n' % value.getAttribute('type')) tp_type = value.getAttributeNS(NS_TP, 'type') if tp_type: - self.h(' * type %s,\n' % tp_type) - self.h(' * named %s):\n' + self.d(' * type %s,\n' % tp_type) + self.d(' * named %s):\n' % value.getAttribute('name')) docstring = get_docstring(value) or '(Undocumented)' - self.h(' * %s\n' % xml_escape(docstring)) - self.h(' *\n') + self.d(' * %s\n' % xml_escape(docstring)) + self.d(' *\n') - self.h(' */\n') + self.d(' */\n') self.h('#define %s (%s ())\n\n' % (name, impl)) self.need_mappings[impl_sig] = esc_impl_sig array_name = mapping.getAttribute('array-name') if array_name: gtype_name = self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper() contents_sig = 'a{' + impl_sig + '}' esc_contents_sig = escape_as_identifier(contents_sig) impl = self.prefix_ + 'type_dbus_array_of_' + esc_contents_sig - self.h('/**\n * %s:\n\n' % gtype_name) - self.h(' * Expands to a call to a function\n') - self.h(' * that returns the #GType of a #GPtrArray\n') - self.h(' * of #%s.\n' % name) - self.h(' */\n') + self.d('/**\n * %s:\n\n' % gtype_name) + self.d(' * Expands to a call to a function\n') + self.d(' * that returns the #GType of a #GPtrArray\n') + self.d(' * of #%s.\n' % name) + self.d(' */\n\n') + self.h('#define %s (%s ())\n\n' % (gtype_name, impl)) self.need_other_arrays[contents_sig] = esc_contents_sig def do_struct_header(self, struct): members = struct.getElementsByTagNameNS(NS_TP, 'member') impl_sig = ''.join([elt.getAttribute('type') for elt in members]) esc_impl_sig = escape_as_identifier(impl_sig) name = (self.PREFIX_ + 'STRUCT_TYPE_' + struct.getAttribute('name').upper()) impl = self.prefix_ + 'type_dbus_struct_' + esc_impl_sig docstring = struct.getElementsByTagNameNS(NS_TP, 'docstring') if docstring: docstring = docstring[0].toprettyxml() if docstring.startswith(''): docstring = docstring[14:] if docstring.endswith('\n'): docstring = docstring[:-16] if docstring.strip() in ('', ''): docstring = '(Undocumented)' else: docstring = '(Undocumented)' - self.h('/**\n * %s:\n\n' % name) - self.h(' * %s\n' % xml_escape(docstring)) - self.h(' *\n') - self.h(' * This macro expands to a call to a function\n') - self.h(' * that returns the #GType of a #GValueArray\n') - self.h(' * appropriate for representing a D-Bus struct\n') - self.h(' * with signature (%s).\n' + self.d('/**\n * %s:\n\n' % name) + self.d(' * %s\n' % xml_escape(docstring)) + self.d(' *\n') + self.d(' * This macro expands to a call to a function\n') + self.d(' * that returns the #GType of a #GValueArray\n') + self.d(' * appropriate for representing a D-Bus struct\n') + self.d(' * with signature (%s).\n' % impl_sig) - self.h(' *\n') + self.d(' *\n') for i, member in enumerate(members): - self.h(' * Member %d (D-Bus type ' + self.d(' * Member %d (D-Bus type ' '%s,\n' % (i, member.getAttribute('type'))) tp_type = member.getAttributeNS(NS_TP, 'type') if tp_type: - self.h(' * type %s,\n' % tp_type) - self.h(' * named %s):\n' + self.d(' * type %s,\n' % tp_type) + self.d(' * named %s):\n' % member.getAttribute('name')) docstring = get_docstring(member) or '(Undocumented)' - self.h(' * %s\n' % xml_escape(docstring)) - self.h(' *\n') + self.d(' * %s\n' % xml_escape(docstring)) + self.d(' *\n') + + self.d(' */\n\n') - self.h(' */\n') self.h('#define %s (%s ())\n\n' % (name, impl)) array_name = struct.getAttribute('array-name') if array_name != '': array_name = (self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper()) impl = self.prefix_ + 'type_dbus_array_' + esc_impl_sig - self.h('/**\n * %s:\n\n' % array_name) - self.h(' * Expands to a call to a function\n') - self.h(' * that returns the #GType of a #GPtrArray\n') - self.h(' * of #%s.\n' % name) - self.h(' */\n') + self.d('/**\n * %s:\n\n' % array_name) + self.d(' * Expands to a call to a function\n') + self.d(' * that returns the #GType of a #GPtrArray\n') + self.d(' * of #%s.\n' % name) + self.d(' */\n\n') + self.h('#define %s (%s ())\n\n' % (array_name, impl)) self.need_struct_arrays[impl_sig] = esc_impl_sig self.need_structs[impl_sig] = esc_impl_sig def __call__(self): mappings = self.dom.getElementsByTagNameNS(NS_TP, 'mapping') structs = self.dom.getElementsByTagNameNS(NS_TP, 'struct') for mapping in mappings: self.do_mapping_header(mapping) for sig in self.need_mappings: self.h('GType %stype_dbus_hash_%s (void);\n\n' % (self.prefix_, self.need_mappings[sig])) self.c('GType\n%stype_dbus_hash_%s (void)\n{\n' % (self.prefix_, self.need_mappings[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') # FIXME: translate sig into two GTypes items = tuple(Signature(sig)) gtypes = types_to_gtypes(items) self.c(' t = dbus_g_type_get_map ("GHashTable", ' '%s, %s);\n' % (gtypes[0], gtypes[1])) self.c(' return t;\n') self.c('}\n\n') for struct in structs: self.do_struct_header(struct) for sig in self.need_structs: self.h('GType %stype_dbus_struct_%s (void);\n\n' % (self.prefix_, self.need_structs[sig])) self.c('GType\n%stype_dbus_struct_%s (void)\n{\n' % (self.prefix_, self.need_structs[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') self.c(' t = dbus_g_type_get_struct ("GValueArray",\n') items = tuple(Signature(sig)) gtypes = types_to_gtypes(items) for gtype in gtypes: self.c(' %s,\n' % gtype) self.c(' G_TYPE_INVALID);\n') self.c(' return t;\n') self.c('}\n\n') for sig in self.need_struct_arrays: self.h('GType %stype_dbus_array_%s (void);\n\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c('GType\n%stype_dbus_array_%s (void)\n{\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') self.c(' t = dbus_g_type_get_collection ("GPtrArray", ' '%stype_dbus_struct_%s ());\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c(' return t;\n') self.c('}\n\n') for sig in self.need_other_arrays: self.h('GType %stype_dbus_array_of_%s (void);\n\n' % (self.prefix_, self.need_other_arrays[sig])) self.c('GType\n%stype_dbus_array_of_%s (void)\n{\n' % (self.prefix_, self.need_other_arrays[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') if sig[:2] == 'a{' and sig[-1:] == '}': # array of mappings self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_hash_%s ());\n' % (self.prefix_, escape_as_identifier(sig[2:-1]))) elif sig[:2] == 'a(' and sig[-1:] == ')': # array of arrays of struct self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_array_%s ());\n' % (self.prefix_, escape_as_identifier(sig[2:-1]))) elif sig[:1] == 'a': # array of arrays of non-struct self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_array_of_%s ());\n' % (self.prefix_, escape_as_identifier(sig[1:]))) else: raise AssertionError("array of '%s' not supported" % sig) self.c(' return t;\n') self.c('}\n\n') + file_set_contents(self.output + '.h', u('').join(self.header).encode('utf-8')) + file_set_contents(self.output + '-body.h', u('').join(self.body).encode('utf-8')) + file_set_contents(self.output + '-gtk-doc.h', u('').join(self.docs).encode('utf-8')) + if __name__ == '__main__': argv = sys.argv[1:] dom = xml.dom.minidom.parse(argv[0]) GTypesGenerator(dom, argv[1], argv[2])() diff --git a/tools/glib-interfaces-gen.py b/tools/glib-interfaces-gen.py index 9543968..b67d7b4 100644 --- a/tools/glib-interfaces-gen.py +++ b/tools/glib-interfaces-gen.py @@ -1,119 +1,207 @@ #!/usr/bin/python from sys import argv, stdout, stderr import xml.dom.minidom +from libtpcodegen import file_set_contents, u from libglibcodegen import NS_TP, get_docstring, \ get_descendant_text, get_by_path class Generator(object): def __init__(self, prefix, implfile, declfile, dom): self.prefix = prefix + '_' - self.impls = open(implfile, 'w') - self.decls = open(declfile, 'w') + + assert declfile.endswith('.h') + docfile = declfile[:-2] + '-gtk-doc.h' + + self.implfile = implfile + self.declfile = declfile + self.docfile = docfile + + self.impls = [] + self.decls = [] + self.docs = [] self.spec = get_by_path(dom, "spec")[0] def h(self, code): - self.decls.write(code.encode('utf-8')) + self.decls.append(code) def c(self, code): - self.impls.write(code.encode('utf-8')) + self.impls.append(code) + + def d(self, code): + self.docs.append(code) def __call__(self): for f in self.h, self.c: self.do_header(f) self.do_body() + file_set_contents(self.implfile, u('').join(self.impls).encode('utf-8')) + file_set_contents(self.declfile, u('').join(self.decls).encode('utf-8')) + file_set_contents(self.docfile, u('').join(self.docs).encode('utf-8')) + # Header def do_header(self, f): f('/* Generated from: ') f(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: f(' version ' + get_descendant_text(version)) f('\n\n') for copyright in get_by_path(self.spec, 'copyright'): f(get_descendant_text(copyright)) f('\n') f('\n') f(get_descendant_text(get_by_path(self.spec, 'license'))) f(get_descendant_text(get_by_path(self.spec, 'docstring'))) f(""" */ +#include """) # Body def do_body(self): for iface in self.spec.getElementsByTagName('interface'): self.do_iface(iface) def do_iface(self, iface): parent_name = get_by_path(iface, '../@name') - self.h("""\ + self.d("""\ /** * %(IFACE_DEFINE)s: * * The interface name "%(name)s" */ +""" % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \ + parent_name).upper().replace('/', ''), + 'name' : iface.getAttribute('name')}) + + self.h(""" #define %(IFACE_DEFINE)s \\ "%(name)s" """ % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \ parent_name).upper().replace('/', ''), 'name' : iface.getAttribute('name')}) - self.h(""" + self.d(""" /** * %(IFACE_QUARK_DEFINE)s: * * Expands to a call to a function that returns a quark for the interface \ name "%(name)s" */ +""" % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \ + parent_name).upper().replace('/', ''), + 'iface_quark_func' : (self.prefix + 'iface_quark_' + \ + parent_name).lower().replace('/', ''), + 'name' : iface.getAttribute('name')}) + + self.h(""" #define %(IFACE_QUARK_DEFINE)s \\ (%(iface_quark_func)s ()) GQuark %(iface_quark_func)s (void); """ % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \ parent_name).upper().replace('/', ''), 'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) self.c("""\ GQuark %(iface_quark_func)s (void) { static GQuark quark = 0; if (G_UNLIKELY (quark == 0)) { quark = g_quark_from_static_string ("%(name)s"); } return quark; } """ % {'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) for prop in iface.getElementsByTagNameNS(None, 'property'): - self.decls.write(""" + self.d(""" /** * %(IFACE_PREFIX)s_%(PROP_UC)s: * * The fully-qualified property name "%(name)s.%(prop)s" */ +""" % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \ + parent_name).upper().replace('/', ''), + 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(), + 'name' : iface.getAttribute('name'), + 'prop' : prop.getAttribute('name'), + }) + + self.h(""" #define %(IFACE_PREFIX)s_%(PROP_UC)s \\ "%(name)s.%(prop)s" """ % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \ parent_name).upper().replace('/', ''), 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) + + for prop in iface.getElementsByTagNameNS(NS_TP, 'contact-attribute'): + self.d(""" +/** + * %(TOKEN_PREFIX)s_%(TOKEN_UC)s: + * + * The fully-qualified contact attribute token name "%(name)s/%(prop)s" + */ +""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ + parent_name).upper().replace('/', ''), + 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), + 'name' : iface.getAttribute('name'), + 'prop' : prop.getAttribute('name'), + }) + + self.h(""" +#define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\ +"%(name)s/%(prop)s" +""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ + parent_name).upper().replace('/', ''), + 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), + 'name' : iface.getAttribute('name'), + 'prop' : prop.getAttribute('name'), + }) + + for prop in iface.getElementsByTagNameNS(NS_TP, 'hct'): + if (prop.getAttribute('is-family') != "yes"): + self.d(""" +/** + * %(TOKEN_PREFIX)s_%(TOKEN_UC)s: + * + * The fully-qualified capability token name "%(name)s/%(prop)s" + */ +""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ + parent_name).upper().replace('/', ''), + 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), + 'name' : iface.getAttribute('name'), + 'prop' : prop.getAttribute('name'), + }) + + self.h(""" +#define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\ +"%(name)s/%(prop)s" +""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ + parent_name).upper().replace('/', ''), + 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), + 'name' : iface.getAttribute('name'), + 'prop' : prop.getAttribute('name'), + }) + if __name__ == '__main__': argv = argv[1:] Generator(argv[0], argv[1], argv[2], xml.dom.minidom.parse(argv[3]))() diff --git a/tools/libglibcodegen.py b/tools/libglibcodegen.py index 6a9d214..5c76f07 100644 --- a/tools/libglibcodegen.py +++ b/tools/libglibcodegen.py @@ -1,172 +1,216 @@ """Library code for GLib/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 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 which gets sequences of capital letters wrong in the same way. (e.g. in Telepathy, SendDTMF -> send_dt_mf) """ ret = '' for c in s: if c >= 'A' and c <= 'Z': length = len(ret) if length > 0 and (length < 2 or ret[length-2] != '_'): ret += '_' ret += c.lower() else: ret += c return ret def signal_to_marshal_type(signal): """ return a list of strings indicating the marshalling type for this signal. """ mtype=[] for i in signal.getElementsByTagName("arg"): name =i.getAttribute("name") type = i.getAttribute("type") mtype.append(type_to_gtype(type)[2]) return mtype _glib_marshallers = ['VOID', 'BOOLEAN', 'CHAR', 'UCHAR', 'INT', 'STRING', 'UINT', 'LONG', 'ULONG', 'ENUM', 'FLAGS', 'FLOAT', 'DOUBLE', 'STRING', 'PARAM', 'BOXED', 'POINTER', 'OBJECT', 'UINT_POINTER'] def signal_to_marshal_name(signal, prefix): mtype = signal_to_marshal_type(signal) if len(mtype): name = '_'.join(mtype) else: name = 'VOID' if name in _glib_marshallers: return 'g_cclosure_marshal_VOID__' + name else: return prefix + '_marshal_VOID__' + name def method_to_glue_marshal_name(method, prefix): mtype = [] for i in method.getElementsByTagName("arg"): if i.getAttribute("direction") != "out": type = i.getAttribute("type") mtype.append(type_to_gtype(type)[2]) mtype.append('POINTER') name = '_'.join(mtype) if name in _glib_marshallers: return 'g_cclosure_marshal_VOID__' + name else: return prefix + '_marshal_VOID__' + name def type_to_gtype(s): if s == 'y': #byte return ("guchar ", "G_TYPE_UCHAR","UCHAR", False) elif s == 'b': #boolean return ("gboolean ", "G_TYPE_BOOLEAN","BOOLEAN", False) elif s == 'n': #int16 return ("gint ", "G_TYPE_INT","INT", False) elif s == 'q': #uint16 return ("guint ", "G_TYPE_UINT","UINT", False) elif s == 'i': #int32 return ("gint ", "G_TYPE_INT","INT", False) elif s == 'u': #uint32 return ("guint ", "G_TYPE_UINT","UINT", False) elif s == 'x': #int64 return ("gint64 ", "G_TYPE_INT64","INT64", False) elif s == 't': #uint64 return ("guint64 ", "G_TYPE_UINT64","UINT64", False) elif s == 'd': #double return ("gdouble ", "G_TYPE_DOUBLE","DOUBLE", False) elif s == 's': #string return ("gchar *", "G_TYPE_STRING", "STRING", True) elif s == 'g': #signature - FIXME return ("gchar *", "DBUS_TYPE_G_SIGNATURE", "STRING", True) elif s == 'o': #object path return ("gchar *", "DBUS_TYPE_G_OBJECT_PATH", "BOXED", True) elif s == 'v': #variant return ("GValue *", "G_TYPE_VALUE", "BOXED", True) elif s == 'as': #array of strings return ("gchar **", "G_TYPE_STRV", "BOXED", True) elif s == 'ay': #byte array return ("GArray *", "dbus_g_type_get_collection (\"GArray\", G_TYPE_UCHAR)", "BOXED", True) elif s == 'au': #uint array return ("GArray *", "DBUS_TYPE_G_UINT_ARRAY", "BOXED", True) elif s == 'ai': #int array return ("GArray *", "DBUS_TYPE_G_INT_ARRAY", "BOXED", True) elif s == 'ax': #int64 array return ("GArray *", "DBUS_TYPE_G_INT64_ARRAY", "BOXED", True) elif s == 'at': #uint64 array return ("GArray *", "DBUS_TYPE_G_UINT64_ARRAY", "BOXED", True) elif s == 'ad': #double array return ("GArray *", "DBUS_TYPE_G_DOUBLE_ARRAY", "BOXED", True) elif s == 'ab': #boolean array return ("GArray *", "DBUS_TYPE_G_BOOLEAN_ARRAY", "BOXED", True) elif s == 'ao': #object path array 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) elif s[:2] in ('a(', 'aa'): # array of structs or arrays, recurse gtype = type_to_gtype(s[1:])[1] return ("GPtrArray *", "(dbus_g_type_get_collection (\"GPtrArray\", "+gtype+"))", "BOXED", True) elif s[:1] == '(': #struct gtype = "(dbus_g_type_get_struct (\"GValueArray\", " for subsig in Signature(s[1:-1]): gtype = gtype + type_to_gtype(subsig)[1] + ", " gtype = gtype + "G_TYPE_INVALID))" return ("GValueArray *", gtype, "BOXED", True) # we just don't know .. - raise Exception, "don't know the GType for " + s + raise Exception("don't know the GType for " + s) + +def move_into_gvalue(gvaluep, gtype, marshaller, name): + if gtype == 'G_TYPE_STRING': + return 'g_value_take_string (%s, %s);' % (gvaluep, name) + elif marshaller == 'BOXED': + return 'g_value_take_boxed (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_UCHAR': + return 'g_value_set_uchar (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_BOOLEAN': + return 'g_value_set_boolean (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_INT': + return 'g_value_set_int (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_UINT': + return 'g_value_set_uint (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_INT64': + return 'g_value_set_int (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_UINT64': + return 'g_value_set_uint64 (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_DOUBLE': + return 'g_value_set_double (%s, %s);' % (gvaluep, name) + else: + raise AssertionError("Don't know how to put %s in a GValue" % gtype) + +def copy_into_gvalue(gvaluep, gtype, marshaller, name): + if gtype == 'G_TYPE_STRING': + return 'g_value_set_string (%s, %s);' % (gvaluep, name) + elif marshaller == 'BOXED': + return 'g_value_set_boxed (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_UCHAR': + return 'g_value_set_uchar (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_BOOLEAN': + return 'g_value_set_boolean (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_INT': + return 'g_value_set_int (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_UINT': + return 'g_value_set_uint (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_INT64': + return 'g_value_set_int (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_UINT64': + return 'g_value_set_uint64 (%s, %s);' % (gvaluep, name) + elif gtype == 'G_TYPE_DOUBLE': + return 'g_value_set_double (%s, %s);' % (gvaluep, name) + else: + raise AssertionError("Don't know how to put %s in a GValue" % gtype) diff --git a/tools/libqtcodegen.py b/tools/libqtcodegen.py index 9ccb860..34d53a1 100644 --- a/tools/libqtcodegen.py +++ b/tools/libqtcodegen.py @@ -1,502 +1,502 @@ """Library code for Qt D-Bus-related code generation. The master copy of this library is in the telepathy-qt repository - please make any changes there. """ # Copyright (C) 2008 Collabora Limited # Copyright (C) 2008 Nokia Corporation # # 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 -from sys import maxint, stderr +from sys import maxsize, stderr import re from libtpcodegen import get_by_path, get_descendant_text, NS_TP, xml_escape class Xzibit(Exception): def __init__(self, parent, child): self.parent = parent self.child = child def __str__(self): - print """ + print(""" Nested <%s>s are forbidden. Parent: %s... Child: %s... """ % (self.parent.nodeName, self.parent.toxml()[:100], - self.child.toxml()[:100]) + self.child.toxml()[:100])) class _QtTypeBinding: def __init__(self, val, inarg, outarg, array_val, custom_type, array_of, array_depth=None): self.val = val self.inarg = inarg self.outarg = outarg self.array_val = array_val self.custom_type = custom_type self.array_of = array_of self.array_depth = array_depth if array_depth is None: self.array_depth = int(bool(array_val)) elif array_depth >= 1: assert array_val else: assert not array_val class RefTarget(object): KIND_INTERFACE, KIND_METHOD, KIND_SIGNAL, KIND_PROPERTY = 'node', 'method', 'signal', 'property' def __init__(self, el): self.kind = el.localName assert self.kind in (self.KIND_INTERFACE, self.KIND_METHOD, self.KIND_SIGNAL, self.KIND_PROPERTY) if self.kind == self.KIND_INTERFACE: self.dbus_text = el.getAttribute('name').lstrip('/').replace('_', '') + 'Interface' else: self.member_text = el.getAttribute('name') assert el.parentNode.parentNode.localName == self.KIND_INTERFACE host_class = el.parentNode.parentNode.getAttribute('name').lstrip('/').replace('_', '') + 'Interface' if self.kind == self.KIND_PROPERTY: self.member_link = 'requestProperty%s()' % (self.member_text) self.dbus_link = '%s::%s' % (host_class, self.member_link) else: self.member_text = '%s()' % self.member_text self.dbus_text = '%s::%s' % (host_class, self.member_text) class RefRegistry(object): def __init__(self, spec): self.targets = {} for node in spec.getElementsByTagName('node'): iface, = get_by_path(node, 'interface') iface_name = iface.getAttribute('name') self.targets[iface_name] = RefTarget(node) for method in iface.getElementsByTagName(RefTarget.KIND_METHOD): self.targets[iface_name + '.' + method.getAttribute('name')] = RefTarget(method) for signal in iface.getElementsByTagName(RefTarget.KIND_SIGNAL): self.targets[iface_name + '.' + signal.getAttribute('name')] = RefTarget(signal) for prop in iface.getElementsByTagName(RefTarget.KIND_PROPERTY): self.targets[iface_name + '.' + prop.getAttribute('name')] = RefTarget(prop) def process(self, ref): assert ref.namespaceURI == NS_TP def get_closest_parent(el, needle): node = el while node is not None and node.localName != needle: node = node.parentNode return node local = get_descendant_text(ref).strip() if ref.localName == 'member-ref': ns = get_closest_parent(ref, 'interface').getAttribute('name') path = ns + '.' + local.strip() else: if ref.hasAttribute('namespace'): ns = ref.getAttribute('namespace').replace('ofdT', 'org.freedesktop.Telepathy') path = ns + '.' + local.strip() else: path = local target = self.targets.get(path) if target is None: parent = get_closest_parent(ref, 'interface') or get_closest_parent(ref, 'error') parent_name = parent.getAttribute('name') if (path + parent_name).find('.DRAFT') == -1 and (path + parent_name).find('.FUTURE') == -1: - print >> stderr, 'WARNING: Failed to resolve %s to "%s" in "%s"' % ( - ref.localName, path, parent_name) + print(('WARNING: Failed to resolve %s to "%s" in "%s"' % ( + ref.localName, path, parent_name)), file=stderr) return path if ref.localName == 'member-ref': if target.kind == target.KIND_PROPERTY: return '\\link %s %s \\endlink' % (target.member_link, target.member_text) else: return target.member_text else: if target.kind == target.KIND_PROPERTY: return '\\link %s %s \\endlink' % (target.dbus_link, target.dbus_text) else: return target.dbus_text def binding_from_usage(sig, tptype, custom_lists, external=False, explicit_own_ns=None): # 'signature' : ('qt-type', 'pass-by-reference', 'array-type') natives = { 'y' : ('uchar', False, None), 'b' : ('bool', False, 'BoolList'), 'n' : ('short', False, 'ShortList'), 'q' : ('ushort', False, 'UShortList'), 'i' : ('int', False, 'IntList'), 'u' : ('uint', False, 'UIntList'), 'x' : ('qlonglong', False, 'LongLongList'), 't' : ('qulonglong', False, 'ULongLongList'), 'd' : ('double', False, 'DoubleList'), 's' : ('QString', True, None), 'v' : ('QDBusVariant', True, None), 'o' : ('QDBusObjectPath', True, 'ObjectPathList'), 'g' : ('QDBusSignature', True, 'SignatureList'), 'as' : ('QStringList', True, "StringListList"), 'ay' : ('QByteArray', True, "ByteArrayList"), 'av' : ('QVariantList', True, "VariantListList"), 'a{sv}' : ('QVariantMap', True, None) } val, inarg = None, None custom_type = False array_of = None - if natives.has_key(sig): + if sig in natives: typename, pass_by_ref, array_name = natives[sig] val = typename inarg = (pass_by_ref and ('const %s&' % val)) or val - elif sig[0] == 'a' and natives.has_key(sig[1:]) and natives[sig[1:]][2]: + elif sig[0] == 'a' and sig[1:] in natives and natives[sig[1:]][2]: val = natives[sig[1:]][2] if explicit_own_ns: val = explicit_own_ns + '::' + val inarg = 'const %s&' % val array_of = natives[sig[1:]][0] elif tptype: tptype = tptype.replace('_', '') custom_type = True if external: tptype = 'Tp::' + tptype elif explicit_own_ns: tptype = explicit_own_ns + '::' + tptype if tptype.endswith('[]'): tptype = tptype[:-2] extra_list_nesting = 0 while tptype.endswith('[]'): extra_list_nesting += 1 tptype = tptype[:-2] - assert custom_lists.has_key(tptype), ('No array version of custom type %s in the spec, but array version used' % tptype) + assert tptype in custom_lists, ('No array version of custom type %s in the spec, but array version used' % tptype) val = custom_lists[tptype] + 'List' * extra_list_nesting else: val = tptype inarg = 'const %s&' % val else: assert False, 'Don\'t know how to map type (%s, %s)' % (sig, tptype) outarg = val + '&' return _QtTypeBinding(val, inarg, outarg, None, custom_type, array_of) def binding_from_decl(name, array_name, array_depth=None, external=False, explicit_own_ns=''): val = name.replace('_', '') if external: val = 'Tp::' + val elif explicit_own_ns: val = explicit_own_ns + '::' + val inarg = 'const %s&' % val outarg = '%s&' % val return _QtTypeBinding(val, inarg, outarg, array_name.replace('_', ''), True, None, array_depth) def extract_arg_or_member_info(els, custom_lists, externals, typesns, refs, docstring_indent=' * ', docstring_brackets=None, docstring_maxwidth=80): names = [] docstrings = [] bindings = [] for el in els: names.append(get_qt_name(el)) docstrings.append(format_docstring(el, refs, docstring_indent, docstring_brackets, docstring_maxwidth)) sig = el.getAttribute('type') tptype = el.getAttributeNS(NS_TP, 'type') bindings.append(binding_from_usage(sig, tptype, custom_lists, (sig, tptype) in externals, typesns)) return names, docstrings, bindings def format_docstring(el, refs, indent=' * ', brackets=None, maxwidth=80): docstring_el = None for x in el.childNodes: if x.namespaceURI == NS_TP and x.localName == 'docstring': docstring_el = x if not docstring_el: return '' lines = [] # escape backslashes, so they won't be interpreted starting doxygen commands and we can later # insert doxygen commands we actually want def escape_slashes(x): if x.nodeType == x.TEXT_NODE: x.data = x.data.replace('\\', '\\\\') elif x.nodeType == x.ELEMENT_NODE: for y in x.childNodes: escape_slashes(y) else: return escape_slashes(docstring_el) doc = docstring_el.ownerDocument for n in docstring_el.getElementsByTagNameNS(NS_TP, 'rationale'): nested = n.getElementsByTagNameNS(NS_TP, 'rationale') if nested: raise Xzibit(n, nested[0]) div = doc.createElement('div') div.setAttribute('class', 'rationale') for rationale_body in n.childNodes: div.appendChild(rationale_body.cloneNode(True)) n.parentNode.replaceChild(div, n) if docstring_el.getAttribute('xmlns') == 'http://www.w3.org/1999/xhtml': for ref in docstring_el.getElementsByTagNameNS(NS_TP, 'member-ref') + docstring_el.getElementsByTagNameNS(NS_TP, 'dbus-ref'): nested = ref.getElementsByTagNameNS(NS_TP, 'member-ref') + ref.getElementsByTagNameNS(NS_TP, 'dbus-ref') if nested: raise Xzibit(n, nested[0]) text = doc.createTextNode(' \\endhtmlonly ') text.data += refs.process(ref) text.data += ' \\htmlonly ' ref.parentNode.replaceChild(text, ref) splitted = ''.join([el.toxml() for el in docstring_el.childNodes]).strip(' ').strip('\n').split('\n') - level = min([not match and maxint or match.end() - 1 for match in [re.match('^ *[^ ]', line) for line in splitted]]) - assert level != maxint + level = min([not match and maxsize or match.end() - 1 for match in [re.match('^ *[^ ]', line) for line in splitted]]) + assert level != maxsize lines = ['\\htmlonly'] + [line[level:] for line in splitted] + ['\\endhtmlonly'] else: content = xml_escape(get_descendant_text(docstring_el).replace('\n', ' ').strip()) while content.find(' ') != -1: content = content.replace(' ', ' ') left = maxwidth - len(indent) - 1 line = '' while content: step = (content.find(' ') + 1) or len(content) if step > left: lines.append(line) line = '' left = maxwidth - len(indent) - 1 left = left - step line = line + content[:step] content = content[step:] if line: lines.append(line) output = [] if lines: if brackets: output.append(brackets[0]) else: output.append(indent) output.append('\n') for line in lines: output.append(indent) output.append(line) output.append('\n') if lines and brackets: output.append(brackets[1]) output.append('\n') return ''.join(output) def gather_externals(spec): externals = [] for ext in spec.getElementsByTagNameNS(NS_TP, 'external-type'): sig = ext.getAttribute('type') tptype = ext.getAttribute('name') externals.append((sig, tptype)) return externals def gather_custom_lists(spec, typesns): custom_lists = {} structs = [(provider, typesns) for provider in spec.getElementsByTagNameNS(NS_TP, 'struct')] mappings = [(provider, typesns) for provider in spec.getElementsByTagNameNS(NS_TP, 'mapping')] exts = [(provider, 'Telepathy') for provider in spec.getElementsByTagNameNS(NS_TP, 'external-type')] for (provider, ns) in structs + mappings + exts: tptype = provider.getAttribute('name').replace('_', '') array_val = provider.getAttribute('array-name').replace('_', '') array_depth = provider.getAttribute('array-depth') if array_depth: array_depth = int(array_depth) else: - array_depth = None + array_depth = -1 if array_val: custom_lists[tptype] = array_val custom_lists[ns + '::' + tptype] = ns + '::' + array_val if array_depth >= 2: - for i in xrange(array_depth): + for i in range(array_depth): custom_lists[tptype + ('[]' * (i+1))] = ( array_val + ('List' * i)) custom_lists[ns + '::' + tptype + ('[]' * (i+1))] = ( ns + '::' + array_val + ('List' * i)) return custom_lists def get_headerfile_cmd(realinclude, prettyinclude, indent=' * '): prettyinclude = prettyinclude or realinclude if realinclude: if prettyinclude: return indent + ('\\headerfile %s <%s>\n' % (realinclude, prettyinclude)) else: return indent + ('\\headerfile %s <%s>\n' % (realinclude)) else: return '' def get_qt_name(el): name = el.getAttribute('name') if el.localName in ('method', 'signal', 'property'): bname = el.getAttributeNS(NS_TP, 'name-for-bindings') if bname: name = bname if not name: return None if name[0].isupper() and name[1].islower(): name = name[0].lower() + name[1:] return qt_identifier_escape(name.replace('_', '')) def qt_identifier_escape(str): built = (str[0].isdigit() and ['_']) or [] for c in str: if c.isalnum(): built.append(c) else: built.append('_') str = ''.join(built) # List of reserved identifiers # Initial list from http://cs.smu.ca/~porter/csc/ref/cpp_keywords.html # Keywords inherited from C90 reserved = ['auto', 'const', 'double', 'float', 'int', 'short', 'struct', 'unsigned', 'break', 'continue', 'else', 'for', 'long', 'signed', 'switch', 'void', 'case', 'default', 'enum', 'goto', 'register', 'sizeof', 'typedef', 'volatile', 'char', 'do', 'extern', 'if', 'return', 'static', 'union', 'while', # C++-only keywords 'asm', 'dynamic_cast', 'namespace', 'reinterpret_cast', 'try', 'bool', 'explicit', 'new', 'static_cast', 'typeid', 'catch', 'false', 'operator', 'template', 'typename', 'class', 'friend', 'private', 'this', 'using', 'const_cast', 'inline', 'public', 'throw', 'virtual', 'delete', 'mutable', 'protected', 'true', 'wchar_t', # Operator replacements 'and', 'bitand', 'compl', 'not_eq', 'or_eq', 'xor_eq', 'and_eq', 'bitor', 'not', 'or', 'xor', # Predefined identifiers 'INT_MIN', 'INT_MAX', 'MAX_RAND', 'NULL', # Qt 'SIGNAL', 'SLOT', 'signals', 'slots'] while str in reserved: str = str + '_' return str diff --git a/tools/libtpcodegen.py b/tools/libtpcodegen.py index 837ff2f..99de663 100644 --- a/tools/libtpcodegen.py +++ b/tools/libtpcodegen.py @@ -1,215 +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(''): docstring = docstring[14:].lstrip() if docstring.endswith(''): docstring = docstring[:-15].rstrip() if docstring in ('', ''): 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('>', '>') diff --git a/tools/manager-file.py b/tools/manager-file.py index 45f6404..5e61c75 100644 --- a/tools/manager-file.py +++ b/tools/manager-file.py @@ -1,175 +1,188 @@ #!/usr/bin/python # manager-file.py: generate .manager files and TpCMParamSpec arrays from the # same data (should be suitable for all connection managers that don't have # plugins) # # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (c) Collabora Ltd. # # 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 re import sys +import os _NOT_C_STR = re.compile(r'[^A-Za-z0-9_-]') def c_string(x): # whitelist-based brute force and ignorance - escape nearly all punctuation return '"' + _NOT_C_STR.sub(lambda c: r'\x%02x' % ord(c), x) + '"' def desktop_string(x): return x.replace(' ', r'\s').replace('\n', r'\n').replace('\r', r'\r').replace('\t', r'\t') supported = list('sbuiqn') fdefaultencoders = { 's': desktop_string, 'b': (lambda b: b and '1' or '0'), 'u': (lambda n: '%u' % n), 'i': (lambda n: '%d' % n), 'q': (lambda n: '%u' % n), 'n': (lambda n: '%d' % n), } for x in supported: assert x in fdefaultencoders gtypes = { 's': 'G_TYPE_STRING', 'b': 'G_TYPE_BOOLEAN', 'u': 'G_TYPE_UINT', 'i': 'G_TYPE_INT', 'q': 'G_TYPE_UINT', 'n': 'G_TYPE_INT', } for x in supported: assert x in gtypes gdefaultencoders = { 's': c_string, 'b': (lambda b: b and 'GINT_TO_POINTER (TRUE)' or 'GINT_TO_POINTER (FALSE)'), 'u': (lambda n: 'GUINT_TO_POINTER (%u)' % n), 'i': (lambda n: 'GINT_TO_POINTER (%d)' % n), 'q': (lambda n: 'GUINT_TO_POINTER (%u)' % n), 'n': (lambda n: 'GINT_TO_POINTER (%d)' % n), } for x in supported: assert x in gdefaultencoders gdefaultdefaults = { 's': 'NULL', 'b': 'GINT_TO_POINTER (FALSE)', 'u': 'GUINT_TO_POINTER (0)', 'i': 'GINT_TO_POINTER (0)', 'q': 'GUINT_TO_POINTER (0)', 'n': 'GINT_TO_POINTER (0)', } for x in supported: assert x in gdefaultdefaults gflags = { 'has-default': 'TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT', 'register': 'TP_CONN_MGR_PARAM_FLAG_REGISTER', 'required': 'TP_CONN_MGR_PARAM_FLAG_REQUIRED', 'secret': 'TP_CONN_MGR_PARAM_FLAG_SECRET', 'dbus-property': 'TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY', } def write_manager(f, manager, protos): # pointless backwards compat section - print >> f, '[ConnectionManager]' - print >> f, 'BusName=org.freedesktop.Telepathy.ConnectionManager.' + manager - print >> f, 'ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/' + manager + print('[ConnectionManager]', file=f) + print('BusName=org.freedesktop.Telepathy.ConnectionManager.' + manager, file=f) + print('ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/' + manager, file=f) # protocols - for proto, params in protos.iteritems(): - print >> f - print >> f, '[Protocol %s]' % proto + for proto, params in protos.items(): + print(file=f) + print('[Protocol %s]' % proto, file=f) defaults = {} - for param, info in params.iteritems(): + for param, info in params.items(): dtype = info['dtype'] flags = info.get('flags', '').split() struct_field = info.get('struct_field', param.replace('-', '_')) filter = info.get('filter', 'NULL') filter_data = info.get('filter_data', 'NULL') setter_data = 'NULL' if 'default' in info: default = fdefaultencoders[dtype](info['default']) defaults[param] = default if flags: flags = ' ' + ' '.join(flags) else: flags = '' - print >> f, 'param-%s=%s%s' % (param, desktop_string(dtype), flags) + print('param-%s=%s%s' % (param, desktop_string(dtype), flags), file=f) - for param, default in defaults.iteritems(): - print >> f, 'default-%s=%s' % (param, default) + for param, default in defaults.items(): + print('default-%s=%s' % (param, default), file=f) def write_c_params(f, manager, proto, struct, params): - print >> f, "static const TpCMParamSpec %s_%s_params[] = {" % (manager, proto) + print("static const TpCMParamSpec %s_%s_params[] = {" % (manager, proto), file=f) - for param, info in params.iteritems(): + for param, info in params.items(): dtype = info['dtype'] flags = info.get('flags', '').split() struct_field = info.get('struct_field', param.replace('-', '_')) filter = info.get('filter', 'NULL') filter_data = info.get('filter_data', 'NULL') setter_data = 'NULL' if 'default' in info: default = gdefaultencoders[dtype](info['default']) else: default = gdefaultdefaults[dtype] if flags: flags = ' | '.join([gflags[flag] for flag in flags]) else: flags = '0' if struct is None or struct_field is None: struct_offset = '0' else: struct_offset = 'G_STRUCT_OFFSET (%s, %s)' % (struct, struct_field) - print >> f, (''' { %s, %s, %s, + print((''' { %s, %s, %s, %s, %s, /* default */ %s, /* struct offset */ %s, /* filter */ %s, /* filter data */ %s /* setter data */ },''' % (c_string(param), c_string(dtype), gtypes[dtype], flags, - default, struct_offset, filter, filter_data, setter_data)) + default, struct_offset, filter, filter_data, setter_data)), file=f) - print >> f, " { NULL }" - print >> f, "};" + print(" { NULL }", file=f) + print("};", file=f) if __name__ == '__main__': environment = {} - execfile(sys.argv[1], environment) - - f = open('%s/%s.manager' % (sys.argv[2], environment['MANAGER']), 'w') + exec(compile(open(sys.argv[1], "rb").read(), sys.argv[1], 'exec'), environment) + + filename = '%s/%s.manager' % (sys.argv[2], environment['MANAGER']) + try: + os.remove(filename) + except OSError: + pass + f = open(filename + '.tmp', 'w') write_manager(f, environment['MANAGER'], environment['PARAMS']) f.close() - - f = open('%s/param-spec-struct.h' % sys.argv[2], 'w') + os.rename(filename + '.tmp', filename) + + filename = '%s/param-spec-struct.h' % sys.argv[2] + try: + os.remove(filename) + except OSError: + pass + f = open(filename + '.tmp', 'w') for protocol in environment['PARAMS']: write_c_params(f, environment['MANAGER'], protocol, environment['STRUCTS'][protocol], environment['PARAMS'][protocol]) f.close() + os.rename(filename + '.tmp', filename) diff --git a/tools/qt-client-gen.py b/tools/qt-client-gen.py index d79ac30..b58ecd8 100644 --- a/tools/qt-client-gen.py +++ b/tools/qt-client-gen.py @@ -1,547 +1,548 @@ #!/usr/bin/python # # Copyright (C) 2008 Collabora Limited # Copyright (C) 2008 Nokia Corporation # # 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 from sys import argv import xml.dom.minidom import codecs from getopt import gnu_getopt +import functools from libtpcodegen import NS_TP, get_descendant_text, get_by_path from libqtcodegen import binding_from_usage, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_headerfile_cmd, get_qt_name, qt_identifier_escape, RefRegistry class Generator(object): def __init__(self, opts): try: self.group = opts.get('--group', '') self.headerfile = opts['--headerfile'] self.implfile = opts['--implfile'] self.namespace = opts['--namespace'] self.typesnamespace = opts['--typesnamespace'] self.realinclude = opts['--realinclude'] self.prettyinclude = opts.get('--prettyinclude') self.extraincludes = opts.get('--extraincludes', None) self.mainiface = opts.get('--mainiface', None) self.must_define = opts.get('--must-define', None) self.dbus_proxy = opts.get('--dbus-proxy', 'Tp::DBusProxy') self.visibility = opts.get('--visibility', '') ifacedom = xml.dom.minidom.parse(opts['--ifacexml']) specdom = xml.dom.minidom.parse(opts['--specxml']) - except KeyError, k: + except KeyError as k: assert False, 'Missing required parameter %s' % k.args[0] self.hs = [] self.bs = [] self.ifacenodes = ifacedom.getElementsByTagName('node') self.spec, = get_by_path(specdom, "spec") self.custom_lists = gather_custom_lists(self.spec, self.typesnamespace) self.externals = gather_externals(self.spec) self.refs = RefRegistry(self.spec) def __call__(self): # Output info header and includes self.h("""\ /* * This file contains D-Bus client proxy classes generated by qt-client-gen.py. * * This file can be distributed under the same terms as the specification from * which it was generated. */ """) if self.must_define: self.h('\n') self.h('#ifndef %s\n' % self.must_define) self.h('#error %s\n' % self.must_define) self.h('#endif\n') self.h('\n') if self.extraincludes: for include in self.extraincludes.split(','): self.h('#include %s\n' % include) self.h(""" #include #include #include #include #include #include #include #include namespace Tp { class PendingVariant; class PendingOperation; } """) if self.must_define: self.b("""#define %s\n""" % (self.must_define)) self.b("""#include "%s" """ % self.realinclude) # Begin namespace for ns in self.namespace.split('::'): self.hb("""\ namespace %s { """ % ns) # Output interface proxies def ifacenodecmp(x, y): - xname, yname = [self.namespace + '::' + node.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' for node in x, y] + xname, yname = [self.namespace + '::' + node.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' for node in (x, y)] if xname == self.mainiface: return -1 elif yname == self.mainiface: return 1 else: - return cmp(xname, yname) + return (xname > yname) - (xname < yname) - self.ifacenodes.sort(cmp=ifacenodecmp) + self.ifacenodes.sort(key=functools.cmp_to_key(ifacenodecmp)) for ifacenode in self.ifacenodes: self.do_ifacenode(ifacenode) # End namespace self.hb(''.join(['}\n' for ns in self.namespace.split('::')])) # Add metatype declaration - otherwise QTBUG #2151 might be triggered for ifacenode in self.ifacenodes: classname = ifacenode.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' self.h("Q_DECLARE_METATYPE(" + self.namespace + "::" + classname + "*)\n") # Write output to files - (codecs.getwriter('utf-8')(open(self.headerfile, 'w'))).write(''.join(self.hs)) - (codecs.getwriter('utf-8')(open(self.implfile, 'w'))).write(''.join(self.bs)) + (codecs.getwriter('utf-8')(open(self.headerfile, 'wb'))).write(''.join(self.hs)) + (codecs.getwriter('utf-8')(open(self.implfile, 'wb'))).write(''.join(self.bs)) def do_ifacenode(self, ifacenode): # Extract info name = ifacenode.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' iface, = get_by_path(ifacenode, 'interface') dbusname = iface.getAttribute('name') # Begin class, constructors self.h(""" /** * \\class %(name)s %(headercmd)s\ %(groupcmd)s\ * * Proxy class providing a 1:1 mapping of the D-Bus interface "%(dbusname)s". */ class %(visibility)s %(name)s : public Tp::AbstractInterface { Q_OBJECT public: /** * Returns the name of the interface "%(dbusname)s", which this class * represents. * * \\return The D-Bus interface name. */ static inline QLatin1String staticInterfaceName() { return QLatin1String("%(dbusname)s"); } /** * Creates a %(name)s associated with the given object on the session bus. * * \\param busName Name of the service the object is on. * \\param objectPath Path to the object on the service. * \\param parent Passed to the parent class constructor. */ %(name)s( const QString& busName, const QString& objectPath, QObject* parent = 0 ); /** * Creates a %(name)s associated with the given object on the given bus. * * \\param connection The bus via which the object can be reached. * \\param busName Name of the service the object is on. * \\param objectPath Path to the object on the service. * \\param parent Passed to the parent class constructor. */ %(name)s( const QDBusConnection& connection, const QString& busName, const QString& objectPath, QObject* parent = 0 ); """ % {'name' : name, 'headercmd' : get_headerfile_cmd(self.realinclude, self.prettyinclude), 'groupcmd' : self.group and (' * \\ingroup %s\n' % self.group), 'dbusname' : dbusname, 'visibility': self.visibility, }) self.b(""" %(name)s::%(name)s(const QString& busName, const QString& objectPath, QObject *parent) : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), QDBusConnection::sessionBus(), parent) { } %(name)s::%(name)s(const QDBusConnection& connection, const QString& busName, const QString& objectPath, QObject *parent) : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), connection, parent) { } """ % {'name' : name}) # Construct from DBusProxy subclass self.h(""" /** * Creates a %(name)s associated with the same object as the given proxy. * * \\param proxy The proxy to use. It will also be the QObject::parent() * for this object. */ %(name)s(%(dbus_proxy)s *proxy); """ % {'name' : name, 'dbus_proxy' : self.dbus_proxy}) self.b(""" %(name)s::%(name)s(%(dbus_proxy)s *proxy) : Tp::AbstractInterface(proxy, staticInterfaceName()) { } """ % {'name' : name, 'dbus_proxy' : self.dbus_proxy}) # Main interface mainiface = self.mainiface or 'Tp::AbstractInterface' if mainiface != self.namespace + '::' + name: self.h(""" /** * Creates a %(name)s associated with the same object as the given proxy. * Additionally, the created proxy will have the same parent as the given * proxy. * * \\param mainInterface The proxy to use. */ explicit %(name)s(const %(mainiface)s& mainInterface); /** * Creates a %(name)s associated with the same object as the given proxy. * However, a different parent object can be specified. * * \\param mainInterface The proxy to use. * \\param parent Passed to the parent class constructor. */ %(name)s(const %(mainiface)s& mainInterface, QObject* parent); """ % {'name' : name, 'mainiface' : mainiface}) self.b(""" %(name)s::%(name)s(const %(mainiface)s& mainInterface) : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), mainInterface.parent()) { } %(name)s::%(name)s(const %(mainiface)s& mainInterface, QObject *parent) : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), parent) { } """ % {'name' : name, 'mainiface' : mainiface}) # Properties has_props = False for prop in get_by_path(iface, 'property'): # Skip tp:properties if not prop.namespaceURI: self.do_prop(prop) has_props = True self.h(""" /** * Request all of the DBus properties on the interface. * * \\return A pending variant map which will emit finished when the properties have * been retrieved. */ Tp::PendingVariantMap *requestAllProperties() const { return internalRequestAllProperties(); } """) # Methods methods = get_by_path(iface, 'method') if methods: self.h(""" public Q_SLOTS:\ """) for method in methods: self.do_method(method) # Signals signals = get_by_path(iface, 'signal') if signals: self.h(""" Q_SIGNALS:\ """) for signal in signals: self.do_signal(signal) # invalidated handler (already a slot in the superclass) # we can't just use disconnect(this, NULL, NULL, NULL) because # (a) that would disconnect QObject::destroyed() and other non-D-Bus # signals, and (b) QtDBus doesn't support that usage anyway (it needs # specific signals in order to remove its signal match rules) self.h(""" protected: virtual void invalidate(Tp::DBusProxy *, const QString &, const QString &); """) self.b(""" void %(name)s::invalidate(Tp::DBusProxy *proxy, const QString &error, const QString &message) { """ % {'name' : name}) for signal in signals: self.do_signal_disconnect(signal) self.b(""" Tp::AbstractInterface::invalidate(proxy, error, message); } """) # Close class self.h("""\ }; """) def do_prop(self, prop): name = prop.getAttribute('name') access = prop.getAttribute('access') gettername = name settername = None docstring = format_docstring(prop, self.refs, ' * ').replace('*/', '*/') sig = prop.getAttribute('type') tptype = prop.getAttributeNS(NS_TP, 'type') binding = binding_from_usage(sig, tptype, self.custom_lists, (sig, tptype) in self.externals, self.typesnamespace) if 'write' in access: settername = 'set' + name if 'read' in access: self.h(""" /** * Asynchronous getter for the remote object property \\c %(name)s of type \\c %(val)s. * %(docstring)s\ * * \\return A pending variant which will emit finished when the property has been * retrieved. */ inline Tp::PendingVariant *%(gettername)s() const { return internalRequestProperty(QLatin1String("%(name)s")); } """ % {'name' : name, 'docstring' : docstring, 'val' : binding.val, 'gettername' : 'requestProperty' + name}) if 'write' in access: self.h(""" /** * Asynchronous setter for the remote object property \\c %(name)s of type \\c %(type)s. * %(docstring)s\ * * \\return A pending operation which will emit finished when the property has been * set. */ inline Tp::PendingOperation *%(settername)s(%(type)s newValue) { return internalSetProperty(QLatin1String("%(name)s"), QVariant::fromValue(newValue)); } """ % {'name' : name, 'docstring' : docstring, 'type' : binding.val, 'name' : name, 'settername' : 'setProperty' + name}) def do_method(self, method): name = method.getAttribute('name') args = get_by_path(method, 'arg') argnames, argdocstrings, argbindings = extract_arg_or_member_info(args, self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') inargs = [] outargs = [] - for i in xrange(len(args)): + for i in range(len(args)): if args[i].getAttribute('direction') == 'out': outargs.append(i) else: inargs.append(i) assert argnames[i] != None, 'No argument name for input argument at index %d for method %s' % (i, name) rettypes = ', '.join([argbindings[i].val for i in outargs]) params = ', '.join([argbindings[i].inarg + ' ' + argnames[i] for i in inargs]) if params: params += ', int timeout = -1' else: params = 'int timeout = -1' self.h(""" /** * Begins a call to the D-Bus method \\c %s on the remote object. %s\ * * Note that \\a timeout is ignored as of now. It will be used once * http://bugreports.qt.nokia.com/browse/QTBUG-11775 is fixed. * """ % (name, format_docstring(method, self.refs, ' * '))) for i in inargs: if argdocstrings[i]: self.h("""\ * * \\param %s %s\ """ % (argnames[i], argdocstrings[i])) self.h("""\ * \\param timeout The timeout in milliseconds. """) for i in outargs: if argdocstrings[i]: self.h("""\ * * \\return %s\ """ % argdocstrings[i]) self.h("""\ */ inline QDBusPendingReply<%(rettypes)s> %(name)s(%(params)s) { if (!invalidationReason().isEmpty()) { return QDBusPendingReply<%(rettypes)s>(QDBusMessage::createError( invalidationReason(), invalidationMessage() )); } """ % {'rettypes' : rettypes, 'name' : name, 'params' : params}) if inargs: self.h(""" QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(), this->staticInterfaceName(), QLatin1String("%s")); callMessage << %s; return this->connection().asyncCall(callMessage, timeout); } """ % (name, ' << '.join(['QVariant::fromValue(%s)' % argnames[i] for i in inargs]))) else: self.h(""" QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(), this->staticInterfaceName(), QLatin1String("%s")); return this->connection().asyncCall(callMessage, timeout); } """ % name) def do_signal(self, signal): name = signal.getAttribute('name') argnames, argdocstrings, argbindings = extract_arg_or_member_info(get_by_path(signal, 'arg'), self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') self.h(""" /** * Represents the signal \\c %s on the remote object. %s\ """ % (name, format_docstring(signal, self.refs, ' * '))) - for i in xrange(len(argnames)): + for i in range(len(argnames)): assert argnames[i] != None, 'Name missing from argument at index %d for signal %s' % (i, name) if argdocstrings[i]: self.h("""\ * * \\param %s %s\ """ % (argnames[i], argdocstrings[i])) self.h("""\ */ void %s(%s); """ % (name, ', '.join(['%s %s' % (binding.inarg, name) for binding, name in zip(argbindings, argnames)]))) def do_signal_disconnect(self, signal): name = signal.getAttribute('name') _, _, argbindings = extract_arg_or_member_info(get_by_path(signal, 'arg'), self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') self.b("""\ disconnect(this, SIGNAL(%s(%s)), NULL, NULL); """ % (name, ', '.join([binding.inarg for binding in argbindings]))) def h(self, str): self.hs.append(str) def b(self, str): self.bs.append(str) def hb(self, str): self.h(str) self.b(str) if __name__ == '__main__': options, argv = gnu_getopt(argv[1:], '', ['group=', 'namespace=', 'typesnamespace=', 'headerfile=', 'implfile=', 'ifacexml=', 'specxml=', 'realinclude=', 'prettyinclude=', 'extraincludes=', 'mainiface=', 'must-define=', 'dbus-proxy=', 'visibility=']) Generator(dict(options))() diff --git a/tools/qt-constants-gen.py b/tools/qt-constants-gen.py index 48fdc43..c96949b 100644 --- a/tools/qt-constants-gen.py +++ b/tools/qt-constants-gen.py @@ -1,310 +1,313 @@ #!/usr/bin/python # # Copyright (C) 2008 Collabora Limited # Copyright (C) 2008 Nokia Corporation # # 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 from sys import argv, stdout, stderr import codecs import xml.dom.minidom from getopt import gnu_getopt from libtpcodegen import NS_TP, get_descendant_text, get_by_path from libqtcodegen import format_docstring, RefRegistry class Generator(object): def __init__(self, opts): try: self.namespace = opts['--namespace'] self.must_define = opts.get('--must-define', None) dom = xml.dom.minidom.parse(opts['--specxml']) - except KeyError, k: + except KeyError as k: assert False, 'Missing required parameter %s' % k.args[0] self.define_prefix = None if '--define-prefix' in opts: self.define_prefix = opts['--define-prefix'] self.old_prefix = None if '--str-constant-prefix' in opts: self.old_prefix = opts['--str-constant-prefix'] self.spec = get_by_path(dom, "spec")[0] self.out = codecs.getwriter('utf-8')(stdout) self.refs = RefRegistry(self.spec) def h(self, code): - self.out.write(code) + if isinstance(code, str): + self.out.buffer.write(code.encode('utf8')) + else: + self.out.buffer.write(code) def __call__(self): # Header self.h('/* Generated from ') self.h(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: self.h(', version ' + get_descendant_text(version)) self.h(""" */ """) if self.must_define: self.h(""" #ifndef %s #error %s #endif """ % (self.must_define, self.must_define)) self.h(""" #include /** * \\addtogroup typesconstants Types and constants * * Enumerated, flag, structure, list and mapping types and utility constants. */ /** * \\defgroup flagtypeconsts Flag type constants * \\ingroup typesconstants * * Types generated from the specification representing bit flag constants and * combinations of them (bitfields). */ /** * \\defgroup enumtypeconsts Enumerated type constants * \\ingroup typesconstants * * Types generated from the specification representing enumerated types ie. * types the values of which are mutually exclusive integral constants. */ /** * \\defgroup ifacestrconsts Interface string constants * \\ingroup typesconstants * * D-Bus interface names of the interfaces in the specification. */ /** * \\defgroup errorstrconsts Error string constants * \\ingroup typesconstants * * Names of the D-Bus errors in the specification. */ """) # Begin namespace self.h(""" namespace %s { """ % self.namespace) # Flags for flags in self.spec.getElementsByTagNameNS(NS_TP, 'flags'): self.do_flags(flags) # Enums for enum in self.spec.getElementsByTagNameNS(NS_TP, 'enum'): self.do_enum(enum) # End namespace self.h("""\ } """) # Interface names for iface in self.spec.getElementsByTagName('interface'): if self.old_prefix: self.h("""\ /** * \\ingroup ifacestrconsts * * The interface name "%(name)s". */ #define %(DEFINE)s "%(name)s" """ % {'name' : iface.getAttribute('name'), 'DEFINE' : self.old_prefix + 'INTERFACE_' + get_by_path(iface, '../@name').upper().replace('/', '')}) if self.define_prefix: self.h("""\ /** * \\ingroup ifacestrconsts * * The interface name "%(name)s" as a QLatin1String, usable in QString requiring contexts even when * building with Q_NO_CAST_FROM_ASCII defined. */ #define %(DEFINE)s (QLatin1String("%(name)s")) """ % {'name' : iface.getAttribute('name'), 'DEFINE' : self.define_prefix + 'IFACE_' + get_by_path(iface, '../@name').upper().replace('/', '')}) # Error names for error in get_by_path(self.spec, 'errors/error'): name = error.getAttribute('name') fullname = get_by_path(error, '../@namespace') + '.' + name.replace(' ', '') if self.old_prefix: define = self.old_prefix + 'ERROR_' + name.replace(' ', '_').replace('.', '_').upper() self.h("""\ /** * \\ingroup errorstrconsts * * The error name "%(fullname)s". %(docstring)s\ */ #define %(DEFINE)s "%(fullname)s" """ % {'fullname' : fullname, 'docstring': format_docstring(error, self.refs), 'DEFINE' : define}) if self.define_prefix: define = self.define_prefix + 'ERROR_' + name.replace(' ', '_').replace('.', '_').upper() self.h("""\ /** * \\ingroup errorstrconsts * * The error name "%(fullname)s" as a QLatin1String, usable in QString requiring contexts even when * building with Q_NO_CAST_FROM_ASCII defined. %(docstring)s\ */ #define %(DEFINE)s QLatin1String("%(fullname)s") """ % {'fullname' : fullname, 'docstring': format_docstring(error, self.refs), 'DEFINE' : define}) def do_flags(self, flags): singular = flags.getAttribute('singular') or \ flags.getAttribute('value-prefix') using_name = False if not singular: using_name = True singular = flags.getAttribute('name') if singular.endswith('lags'): singular = singular[:-1] if using_name and singular.endswith('s'): singular = singular[:-1] singular = singular.replace('_', '') plural = (flags.getAttribute('plural') or flags.getAttribute('name') or singular + 's').replace('_', '') self.h("""\ /** * \\ingroup flagtypeconsts * * Flag type generated from the specification. */ enum %(singular)s { """ % {'singular' : singular}) flagvalues = get_by_path(flags, 'flag') for flag in flagvalues: self.do_val(flag, singular, flag == flagvalues[-1]) self.h("""\ %s = 0xffffffffU """ % ("_" + singular + "Padding")) self.h("""\ }; /** * \\typedef QFlags<%(singular)s> %(plural)s * \\ingroup flagtypeconsts * * Type representing combinations of #%(singular)s values. %(docstring)s\ */ typedef QFlags<%(singular)s> %(plural)s; Q_DECLARE_OPERATORS_FOR_FLAGS(%(plural)s) """ % {'singular' : singular, 'plural' : plural, 'docstring' : format_docstring(flags, self.refs)}) def do_enum(self, enum): singular = enum.getAttribute('singular') or \ enum.getAttribute('name') value_prefix = enum.getAttribute('singular') or \ enum.getAttribute('value-prefix') or \ enum.getAttribute('name') if singular.endswith('lags'): singular = singular[:-1] plural = enum.getAttribute('plural') or singular + 's' singular = singular.replace('_', '') value_prefix = value_prefix.replace('_', '') vals = get_by_path(enum, 'enumvalue') self.h("""\ /** * \\enum %(singular)s * \\ingroup enumtypeconsts * * Enumerated type generated from the specification. %(docstring)s\ */ enum %(singular)s { """ % {'singular' : singular, 'docstring' : format_docstring(enum, self.refs)}) for val in vals: self.do_val(val, value_prefix, val == vals[-1]) self.h("""\ %s = 0xffffffffU }; """ % ("_" + singular + "Padding")) self.h("""\ /** * \\ingroup enumtypeconsts * * 1 higher than the highest valid value of %(singular)s. */ const int NUM_%(upper-plural)s = (%(last-val)s+1); """ % {'singular' : singular, 'upper-plural' : plural.upper(), 'last-val' : vals[-1].getAttribute('value')}) def do_val(self, val, prefix, last): name = (val.getAttribute('suffix') or val.getAttribute('name')).replace('_', '') self.h("""\ %s\ %s = %s, """ % (format_docstring(val, self.refs, indent=' * ', brackets=(' /**', ' */')), prefix + name, val.getAttribute('value'))) if __name__ == '__main__': options, argv = gnu_getopt(argv[1:], '', ['namespace=', 'str-constant-prefix=', 'define-prefix=', 'must-define=', 'specxml=']) Generator(dict(options))() diff --git a/tools/qt-svc-gen.py b/tools/qt-svc-gen.py index 58b77bb..565dd01 100644 --- a/tools/qt-svc-gen.py +++ b/tools/qt-svc-gen.py @@ -1,742 +1,743 @@ #!/usr/bin/python # # Copyright (C) 2012 Collabora Limited # Copyright (C) 2012 Nokia Corporation # # 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 from sys import argv import xml.dom.minidom import codecs from getopt import gnu_getopt +import functools from libtpcodegen import NS_TP, get_descendant_text, get_by_path from libqtcodegen import binding_from_usage, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_headerfile_cmd, get_qt_name, qt_identifier_escape, RefRegistry # TODO generate docstrings def to_lower_camel_case(s): if len(s) <= 1: return s.lower() i = 0 for c in s: if c == '_': break i += 1 ret = s if i == len(s): return s.lower() else: ret = s[0:i].lower() + s[i:] ret = ret.replace('_', '') return ret class Generator(object): def __init__(self, opts): try: self.group = opts.get('--group', '') self.headerfile = opts['--headerfile'] self.implfile = opts['--implfile'] self.namespace = opts['--namespace'] self.typesnamespace = opts['--typesnamespace'] self.realinclude = opts.get('--realinclude', None) self.mocinclude = opts.get('--mocinclude', None) self.prettyinclude = opts.get('--prettyinclude') self.extraincludes = opts.get('--extraincludes', None) self.must_define = opts.get('--must-define', None) self.visibility = opts.get('--visibility', '') ifacedom = xml.dom.minidom.parse(opts['--ifacexml']) specdom = xml.dom.minidom.parse(opts['--specxml']) - except KeyError, k: + except KeyError as k: assert False, 'Missing required parameter %s' % k.args[0] if not self.realinclude: self.realinclude = self.headerfile self.hs = [] self.bs = [] self.ifacenodes = ifacedom.getElementsByTagName('node') self.spec, = get_by_path(specdom, "spec") self.custom_lists = gather_custom_lists(self.spec, self.typesnamespace) self.externals = gather_externals(self.spec) self.refs = RefRegistry(self.spec) def __call__(self): # Output info header and includes self.h("""\ /* * This file contains D-Bus adaptor classes generated by qt-svc-gen.py. * * This file can be distributed under the same terms as the specification from * which it was generated. */ """) if self.must_define: self.h('\n') self.h('#ifndef %s\n' % self.must_define) self.h('#error %s\n' % self.must_define) self.h('#endif\n') self.h('\n') if self.extraincludes: for include in self.extraincludes.split(','): self.h('#include %s\n' % include) self.h("""\ #include #include #include #include #include """) if self.must_define: self.b("""#define %s\n""" % (self.must_define)) self.b("""#include "%s" """ % self.realinclude) if self.mocinclude: self.b("""#include "%s" """ % self.mocinclude) self.b("""\ #include #include """) # Begin namespace for ns in self.namespace.split('::'): self.hb("""\ namespace %s { """ % ns) # Output interface proxies def ifacenodecmp(x, y): - xname, yname = [self.namespace + '::' + node.getAttribute('name').replace('/', '').replace('_', '') + 'Adaptor' for node in x, y] + xname, yname = [self.namespace + '::' + node.getAttribute('name').replace('/', '').replace('_', '') + 'Adaptor' for node in (x, y)] - return cmp(xname, yname) + return (xname > yname) - (xname < yname) - self.ifacenodes.sort(cmp=ifacenodecmp) + self.ifacenodes.sort(key=functools.cmp_to_key(ifacenodecmp)) for ifacenode in self.ifacenodes: self.do_ifacenode(ifacenode) # End namespace self.hb(''.join(['\n}' for ns in self.namespace.split('::')])) # Write output to files - (codecs.getwriter('utf-8')(open(self.headerfile, 'w'))).write(''.join(self.hs)) - (codecs.getwriter('utf-8')(open(self.implfile, 'w'))).write(''.join(self.bs)) + (codecs.getwriter('utf-8')(open(self.headerfile, 'wb'))).write(''.join(self.hs)) + (codecs.getwriter('utf-8')(open(self.implfile, 'wb'))).write(''.join(self.bs)) def do_ifacenode(self, ifacenode): # Extract info name = ifacenode.getAttribute('name').replace('/', '').replace('_', '') + 'Adaptor' iface, = get_by_path(ifacenode, 'interface') dbusname = iface.getAttribute('name') props = get_by_path(iface, 'property') methods = get_by_path(iface, 'method') signals = get_by_path(iface, 'signal') # Begin class, constructors self.h(""" /** * \\class %(name)s %(headercmd)s\ %(groupcmd)s\ * * Adaptor class providing a 1:1 mapping of the D-Bus interface "%(dbusname)s". */ class %(visibility)s %(name)s : public Tp::AbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "%(dbusname)s") Q_CLASSINFO("D-Bus Introspection", "" " \\n" """ % {'name': name, 'headercmd': get_headerfile_cmd(self.realinclude, self.prettyinclude), 'groupcmd': self.group and (' * \\ingroup %s\n' % self.group), 'dbusname': dbusname, 'visibility': self.visibility, }) self.do_introspection(props, methods, signals) self.h("""\ " \\n" "") """) self.do_qprops(props) self.h(""" public: %(name)s(const QDBusConnection& dbusConnection, QObject* adaptee, QObject* parent); virtual ~%(name)s(); """ % {'name': name}) self.do_mic_typedefs(methods) self.b(""" %(name)s::%(name)s(const QDBusConnection& bus, QObject* adaptee, QObject* parent) : Tp::AbstractAdaptor(bus, adaptee, parent) { """ % {'name': name}) self.do_signals_connect(signals) self.b("""\ } %(name)s::~%(name)s() { } """ % {'name': name}) # Properties has_props = False if props: self.h(""" public: // PROPERTIES """) for prop in props: # Skip tp:properties if not prop.namespaceURI: self.do_prop(name, prop) has_props = True # Methods if methods: self.h(""" public Q_SLOTS: // METHODS """) for method in methods: self.do_method(name, method) # Signals if signals: self.h(""" Q_SIGNALS: // SIGNALS """) for signal in signals: self.do_signal(signal) # Close class self.h("""\ }; """) def do_introspection(self, props, methods, signals): self.do_prop_introspection(props) self.do_method_introspection(methods) self.do_signal_introspection(signals) def do_prop_introspection(self, props): for prop in props: if prop.namespaceURI: continue name = prop.getAttribute('name') access = prop.getAttribute('access') sig = prop.getAttribute('type') tptype = prop.getAttributeNS(NS_TP, 'type') binding = binding_from_usage(sig, tptype, self.custom_lists, (sig, tptype) in self.externals, self.typesnamespace) if not binding.custom_type: self.h("""\ " \\n" """ % {'access': access, 'sig': sig, 'name': name, }) else: self.h("""\ " \\n" " \\n" " \\n" """ % {'access': access, 'sig': sig, 'name': name, 'type': binding.val, }) def do_method_introspection(self, methods): for method in methods: name = method.getAttribute('name') args = get_by_path(method, 'arg') argnames, argdocstrings, argbindings = extract_arg_or_member_info(args, self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') if not argnames: self.h("""\ " \\n" """ % {'name': name}) else: self.h("""\ " \\n" """ % {'name': name}) outindex = 0 inindex = 0 - for i in xrange(len(argnames)): + for i in range(len(argnames)): assert argnames[i] != None, 'Name missing from argument at index %d for signal %s' % (i, name) argbinding = argbindings[i] argname = argnames[i] argsig = args[i].getAttribute('type') argdirection = args[i].getAttribute('direction') # QtDBus requires annotating a{sv} if argsig == 'a{sv}': argbinding.custom_type = True if not argbinding.custom_type: self.h("""\ " \\n" """ % {'direction': argdirection, 'sig': argsig, 'name': argname}) else: self.h("""\ " \\n" " \\n" " \\n" """ % {'direction': argdirection, 'sig': argsig, 'name': argname, 'type': argbinding.val, 'index': 'In' + str(inindex) if argdirection == 'in' else 'Out' + str(outindex), }) if argdirection == 'out': outindex += 1 else: inindex += 1 self.h("""\ " \\n" """) def do_signal_introspection(self, signals): for signal in signals: name = signal.getAttribute('name') args = get_by_path(signal, 'arg') argnames, argdocstrings, argbindings = extract_arg_or_member_info(args, self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') if not argnames: self.h("""\ " \\n" """ % {'name': name}) else: self.h("""\ " \\n" """ % {'name': name}) - for i in xrange(len(argnames)): + for i in range(len(argnames)): assert argnames[i] != None, 'Name missing from argument at index %d for signal %s' % (i, name) argbinding = argbindings[i] argname = argnames[i] argsig = args[i].getAttribute('type') if not argbinding.custom_type: self.h("""\ " \\n" """ % {'sig': argsig, 'name': argname}) else: self.h("""\ " \\n" " \\n" " \\n" """ % {'sig': argsig, 'name': argname, 'type': argbinding.val, 'index': i, }) self.h("""\ " \\n" """) def do_mic_typedefs(self, methods): for method in methods: name = method.getAttribute('name') args = get_by_path(method, 'arg') argnames, argdocstrings, argbindings = extract_arg_or_member_info(args, self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') outargs = [] - for i in xrange(len(args)): + for i in range(len(args)): if args[i].getAttribute('direction') == 'out': outargs.append(i) if outargs: outargtypes = ', '.join([argbindings[i].val for i in outargs]) else: outargtypes = '' self.h("""\ typedef Tp::MethodInvocationContextPtr< %(outargtypes)s > %(name)sContextPtr; """ % {'name': name, 'outargtypes': outargtypes, }) def do_qprops(self, props): for prop in props: # Skip tp:properties if not prop.namespaceURI: self.do_qprop(prop) def do_qprop(self, prop): name = prop.getAttribute('name') access = prop.getAttribute('access') gettername = name settername = None if 'write' in access: settername = 'Set' + name sig = prop.getAttribute('type') tptype = prop.getAttributeNS(NS_TP, 'type') binding = binding_from_usage(sig, tptype, self.custom_lists, (sig, tptype) in self.externals, self.typesnamespace) self.h("""\ Q_PROPERTY(%(type)s %(name)s %(getter)s %(setter)s) """ % {'type': binding.val, 'name': name, 'getter': 'READ ' + gettername if ('read' in access) else '', 'setter': 'WRITE ' + settername if ('write' in access) else '', }) def do_prop(self, ifacename, prop): name = prop.getAttribute('name') adaptee_name = to_lower_camel_case(prop.getAttribute('tp:name-for-bindings')) access = prop.getAttribute('access') gettername = name settername = None if 'write' in access: settername = 'Set' + name docstring = format_docstring(prop, self.refs, ' * ').replace('*/', '*/') sig = prop.getAttribute('type') tptype = prop.getAttributeNS(NS_TP, 'type') binding = binding_from_usage(sig, tptype, self.custom_lists, (sig, tptype) in self.externals, self.typesnamespace) if 'read' in access: self.h("""\ /** * Return the value of the exported D-Bus object property \\c %(name)s of type \\c %(type)s. * * Adaptees should export this property as a Qt property named * '%(adaptee_name)s' with type %(type)s. * %(docstring)s\ * * \\return The value of exported property \\c %(name)s. */ %(type)s %(gettername)s() const; """ % {'name': name, 'adaptee_name': adaptee_name, 'docstring': docstring, 'type': binding.val, 'gettername': gettername, }) self.b(""" %(type)s %(ifacename)s::%(gettername)s() const { return qvariant_cast< %(type)s >(adaptee()->property("%(adaptee_name)s")); } """ % {'type': binding.val, 'ifacename': ifacename, 'gettername': gettername, 'adaptee_name': adaptee_name, }) if 'write' in access: self.h("""\ /** * Set the value of the exported D-Bus object property \\c %(name)s of type \\c %(type)s. * * Adaptees should export this property as a writable Qt property named * '%(adaptee_name)s' with type %(type)s. * %(docstring)s\ */ void %(settername)s(const %(type)s &newValue); """ % {'name': name, 'adaptee_name': adaptee_name, 'docstring': docstring, 'settername': settername, 'type': binding.val, }) self.b(""" void %(ifacename)s::%(settername)s(const %(type)s &newValue) { adaptee()->setProperty("%(adaptee_name)s", qVariantFromValue(newValue)); } """ % {'ifacename': ifacename, 'settername': settername, 'type': binding.val, 'adaptee_name': adaptee_name, }) def do_method(self, ifacename, method): name = method.getAttribute('name') adaptee_name = to_lower_camel_case(method.getAttribute('tp:name-for-bindings')) args = get_by_path(method, 'arg') argnames, argdocstrings, argbindings = extract_arg_or_member_info(args, self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') docstring = format_docstring(method, self.refs, ' * ').replace('*/', '*/') inargs = [] outargs = [] - for i in xrange(len(args)): + for i in range(len(args)): if args[i].getAttribute('direction') == 'out': outargs.append(i) else: inargs.append(i) assert argnames[i] != None, 'No argument name for input argument at index %d for method %s' % (i, name) if outargs: rettype = argbindings[outargs[0]].val else: rettype = 'void' params = [argbindings[i].inarg + ' ' + argnames[i] for i in inargs] params.append('const QDBusMessage& dbusMessage') params += [argbindings[i].outarg + ' ' + argnames[i] for i in outargs[1:]] params = ', '.join(params) if outargs: outargtypes = ', '.join([argbindings[i].val for i in outargs]) else: outargtypes = '' invokemethodargs = ', '.join(['Q_ARG(' + argbindings[i].val + ', ' + argnames[i] + ')' for i in inargs]) inparams = [argbindings[i].val for i in inargs] inparams.append("%s::%s::%sContextPtr" % (self.namespace, ifacename, name)) normalized_adaptee_params = ','.join(inparams) adaptee_params = [argbindings[i].inarg + ' ' + argnames[i] for i in inargs] adaptee_params.append('const %(namespace)s::%(ifacename)s::%(name)sContextPtr &context' % {'namespace': self.namespace, 'ifacename': ifacename, 'name': name}) adaptee_params = ', '.join(adaptee_params) self.h("""\ /** * Begins a call to the exported D-Bus method \\c %(name)s on this object. * * Adaptees should export this method as a Qt slot with the following signature: * void %(adaptee_name)s(%(adaptee_params)s); * * Implementations should call MethodInvocationContext::setFinished (or setFinishedWithError * accordingly) on the received \\a context object once the method has finished processing. * %(docstring)s\ * """ % {'name': name, 'adaptee_name': adaptee_name, 'adaptee_params': adaptee_params, 'rettype': rettype, 'docstring': docstring }) for i in inargs: if argdocstrings[i]: self.h("""\ * \\param %s %s\ """ % (argnames[i], argdocstrings[i])) for i in outargs[1:]: if argdocstrings[i]: self.h("""\ * \\param %s Output parameter %s\ """ % (argnames[i], argdocstrings[i])) if outargs: self.h("""\ * \\return %s\ """ % argdocstrings[outargs[0]]) self.h("""\ */ %(rettype)s %(name)s(%(params)s); """ % {'rettype': rettype, 'name': name, 'params': params }) self.b(""" %(rettype)s %(ifacename)s::%(name)s(%(params)s) { - if (!adaptee()->metaObject()->indexOfMethod("%(adaptee_name)s(%(normalized_adaptee_params)s)") == -1) { + if (adaptee()->metaObject()->indexOfMethod("%(adaptee_name)s(%(normalized_adaptee_params)s)") < 0) { dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"))); """ % {'rettype': rettype, 'ifacename': ifacename, 'name': name, 'adaptee_name': adaptee_name, 'normalized_adaptee_params': normalized_adaptee_params, 'params': params, }) if rettype != 'void': self.b("""\ return %(rettype)s(); """ % {'rettype': rettype}) else: self.b("""\ return; """) self.b("""\ } %(name)sContextPtr ctx = %(name)sContextPtr( new Tp::MethodInvocationContext< %(outargtypes)s >(dbusConnection(), dbusMessage)); """ % {'name': name, 'outargtypes': outargtypes, }) if invokemethodargs: self.b("""\ QMetaObject::invokeMethod(adaptee(), "%(adaptee_name)s", %(invokemethodargs)s, Q_ARG(%(namespace)s::%(ifacename)s::%(name)sContextPtr, ctx)); """ % {'namespace': self.namespace, 'ifacename': ifacename, 'name': name, 'adaptee_name': adaptee_name, 'invokemethodargs': invokemethodargs, }) else: self.b("""\ QMetaObject::invokeMethod(adaptee(), "%(lname)s", Q_ARG(%(namespace)s::%(ifacename)s::%(name)sContextPtr, ctx)); """ % {'namespace': self.namespace, 'ifacename': ifacename, 'name': name, 'lname': (name[0].lower() + name[1:]), }) if rettype != 'void': self.b("""\ return %(rettype)s(); """ % {'rettype': rettype}) self.b("}\n") def do_signal(self, signal): name = signal.getAttribute('name') adaptee_name = to_lower_camel_case(signal.getAttribute('tp:name-for-bindings')) argnames, argdocstrings, argbindings = extract_arg_or_member_info(get_by_path(signal, 'arg'), self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') params = ', '.join(['%s %s' % (binding.inarg, param_name) for binding, param_name in zip(argbindings, argnames)]) - for i in xrange(len(argnames)): + for i in range(len(argnames)): assert argnames[i] != None, 'Name missing from argument at index %d for signal %s' % (i, name) self.h("""\ /** * Represents the exported D-Bus signal \\c %(name)s on this object. * * Adaptees should export this signal as a Qt signal with the following signature: * void %(adaptee_name)s(%(params)s); * * The adaptee signal will be automatically relayed as a D-Bus signal once emitted. * """ % {'name': name, 'adaptee_name': adaptee_name, 'params': params }) - for i in xrange(len(argnames)): + for i in range(len(argnames)): assert argnames[i] != None, 'Name missing from argument at index %d for signal %s' % (i, name) if argdocstrings[i]: self.h("""\ * \\param %s %s\ """ % (argnames[i], argdocstrings[i])) self.h("""\ */ void %(name)s(%(params)s); """ % {'name': name, 'params': params }) def do_signals_connect(self, signals): for signal in signals: name = signal.getAttribute('name') adaptee_name = to_lower_camel_case(signal.getAttribute('tp:name-for-bindings')) _, _, argbindings = extract_arg_or_member_info(get_by_path(signal, 'arg'), self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') self.b("""\ connect(adaptee, SIGNAL(%(adaptee_name)s(%(params)s)), SIGNAL(%(name)s(%(params)s))); """ % {'name': name, 'adaptee_name': adaptee_name, 'params': ', '.join([binding.inarg for binding in argbindings]) }) def h(self, str): self.hs.append(str) def b(self, str): self.bs.append(str) def hb(self, str): self.h(str) self.b(str) if __name__ == '__main__': options, argv = gnu_getopt(argv[1:], '', ['group=', 'headerfile=', 'implfile=', 'namespace=', 'typesnamespace=', 'realinclude=', 'mocinclude=', 'prettyinclude=', 'extraincludes=', 'must-define=', 'visibility=', 'ifacexml=', 'specxml=']) Generator(dict(options))() diff --git a/tools/qt-types-gen.py b/tools/qt-types-gen.py index 119cf9c..1ade9da 100644 --- a/tools/qt-types-gen.py +++ b/tools/qt-types-gen.py @@ -1,557 +1,591 @@ #!/usr/bin/python # # Copyright (C) 2008 Collabora Limited # Copyright (C) 2008 Nokia Corporation # # 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 sys import xml.dom.minidom from getopt import gnu_getopt from libtpcodegen import NS_TP, get_descendant_text, get_by_path from libqtcodegen import binding_from_usage, binding_from_decl, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_qt_name, get_headerfile_cmd, RefRegistry class BrokenSpecException(Exception): pass class MissingTypes(BrokenSpecException): def __init__(self, types): super(MissingTypes, self).__init__(self) self.types = types def __str__(self): typelist = ''.join([' %s' % t for t in self.types]) return "The following types were used, but not provided by the spec " \ "or by declarations in all.xml:\n%s" % typelist class UnresolvedDependency(BrokenSpecException): def __init__(self, child, parent): super(UnresolvedDependency, self).__init__(self) self.child = child self.parent = parent def __str__(self): return 'Type %s has unresolved dependency on %s' % ( self.child, self.parent) class EmptyStruct(BrokenSpecException): def __init__(self, struct_name): super(EmptyStruct, self).__init__(self) self.struct_name = struct_name def __str__(self): return 'tp:struct %s should have some members' % self.struct_name class MalformedMapping(BrokenSpecException): def __init__(self, mapping_name, members): super(MalformedMapping, self).__init__(self) self.mapping_name = mapping_name self.members = members def __str__(self): return 'tp:mapping %s should have 2 members, not %u' % ( self.mapping_name, self.members) class WTF(BrokenSpecException): def __init__(self, element_name): super(BrokenSpecException, self).__init__(self) self.element_name = element_name def __str__(self): return 'What the hell is a tp:%s?' % self.element_name class DepInfo: def __init__(self, el, externals, custom_lists): self.el = el name = get_by_path(el, '@name') array_name = get_by_path(el, '@array-name') array_depth = get_by_path(el, '@array-depth') if array_depth: array_depth = int(array_depth) else: array_depth = None self.binding = binding_from_decl(name, array_name, array_depth) self.deps = [] for member in get_by_path(el, 'member'): sig = member.getAttribute('type') tptype = member.getAttributeNS(NS_TP, 'type') if (sig, tptype) in externals: continue if tptype.endswith('[]'): tptype = tptype[:-2] binding = binding_from_usage(sig, tptype, custom_lists) if binding.custom_type: self.deps.append(binding.val) self.revdeps = [] class Generator(object): def __init__(self, opts): try: self.namespace = opts['--namespace'] self.declfile = opts['--declfile'] self.implfile = opts['--implfile'] self.realinclude = opts['--realinclude'] self.prettyinclude = opts.get('--prettyinclude', self.realinclude) self.extraincludes = opts.get('--extraincludes', None) self.must_define = opts.get('--must-define', None) self.visibility = opts.get('--visibility', '') dom = xml.dom.minidom.parse(opts['--specxml']) - except KeyError, k: + except KeyError as k: assert False, 'Missing required parameter %s' % k.args[0] self.decls = [] self.impls = [] self.spec = get_by_path(dom, "spec")[0] self.externals = gather_externals(self.spec) self.custom_lists = gather_custom_lists(self.spec, self.namespace) self.required_custom = [] self.required_arrays = [] self.to_declare = [] self.depinfos = {} self.refs = RefRegistry(self.spec) def __call__(self): # Emit comment header self.both('/* Generated from ') self.both(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: self.both(', version ' + get_descendant_text(version)) self.both(' */\n') # Gather info on available and required types self.gather_required() if self.must_define: self.decl('\n') self.decl('#ifndef %s\n' % self.must_define) self.decl('#error %s\n' % self.must_define) self.decl('#endif') self.decl('\n') if self.extraincludes: for include in self.extraincludes.split(','): self.decl('#include %s\n' % include) self.decl(""" #include #include #include #include #include #include #include #include #include #include #include #include /** * \\addtogroup typesconstants Types and constants * * Enumerated, flag, structure, list and mapping types and utility constants. */ /** * \\defgroup struct Structure types * \\ingroup typesconstants * * Structure types generated from the specification. */ /** * \\defgroup list List types * \\ingroup typesconstants * * List types generated from the specification. */ /** * \\defgroup mapping Mapping types * \\ingroup typesconstants * * Mapping types generated from the specification. */ """) if self.must_define: self.impl(""" #define %s""" % self.must_define) self.impl(""" #include "%s" """ % self.realinclude) self.both(""" namespace %s { """ % self.namespace) # Emit type definitions for types provided in the spec self.provide_all() # Emit type registration function self.decl(""" } // namespace %s """ % self.namespace) self.impl("""\ TP_QT_NO_EXPORT void _registerTypes() { static bool registered = false; if (registered) return; registered = true; """) # Emit Qt metatype declarations self.to_declare.sort() for metatype in self.to_declare: self.decl('Q_DECLARE_METATYPE(%s)\n' % metatype) self.impl(' qDBusRegisterMetaType<%s>();\n' % ((metatype.endswith('>') and metatype + ' ') or metatype)) self.impl("""\ } } // namespace %s """ % self.namespace) # Write output to files - open(self.declfile, 'w').write(''.join(self.decls).encode("utf-8")) - open(self.implfile, 'w').write(''.join(self.impls).encode("utf-8")) + open(self.declfile, 'wb').write(''.join(self.decls).encode("utf-8")) + open(self.implfile, 'wb').write(''.join(self.impls).encode("utf-8")) def decl(self, str): self.decls.append(str) def impl(self, str): self.impls.append(str) def both(self, str): self.decl(str) self.impl(str) def gather_required(self): members = self.spec.getElementsByTagNameNS(NS_TP, 'member') args = self.spec.getElementsByTagName('arg') props = self.spec.getElementsByTagName('property') tp_props = self.spec.getElementsByTagNameNS(NS_TP, 'property') for requirer in members + args + props + tp_props: sig = requirer.getAttribute('type') tptype = requirer.getAttributeNS(NS_TP, 'type') external = (sig, tptype) in self.externals binding = binding_from_usage(sig, tptype, self.custom_lists, external) if binding.custom_type and binding.val not in self.required_custom: self.required_custom.append(binding.val) if not binding.custom_type and binding.array_of and (binding.val, binding.array_of) not in self.required_arrays: self.required_arrays.append((binding.val, binding.array_of)) def provide_all(self): self.required_arrays.sort() for (val, array_of) in self.required_arrays: real = 'QList<%s>' % array_of self.decl("""\ /** * \\struct %s * \\ingroup list %s\ * * Generic list type with %s elements. Convertible with * %s, but needed to have a discrete type in the Qt type system. */ """ % (val, get_headerfile_cmd(self.realinclude, self.prettyinclude), array_of, real)) self.decl(self.faketype(val, real)) self.to_declare.append(self.namespace + '::' + val) + self.both('%s QDBusArgument& operator<<(QDBusArgument& arg, const %s &list)' % + (self.visibility, val)) + self.decl(';\n') + self.impl(""" +{ + int id = qMetaTypeId<%s>(); + arg.beginArray(id); + for (int i = 0; i < list.count(); ++i) { + arg << list.at(i); + } + arg.endArray(); + return arg; +} + +""" % (array_of)) + + self.both('%s const QDBusArgument& operator>>(const QDBusArgument& arg, %s &list)' % + (self.visibility, val)) + self.decl(';\n\n') + self.impl(""" +{ + arg.beginArray(); + list.clear(); + while (!arg.atEnd()) { + %s item; + arg >> item; + list.append(item); + } + arg.endArray(); + return arg; +} + +""" % (array_of)) + structs = self.spec.getElementsByTagNameNS(NS_TP, 'struct') mappings = self.spec.getElementsByTagNameNS(NS_TP, 'mapping') exts = self.spec.getElementsByTagNameNS(NS_TP, 'external-type') for deptype in structs + mappings: info = DepInfo(deptype, self.externals, self.custom_lists) self.depinfos[info.binding.val] = info leaves = [] next_leaves = [] - for val, depinfo in self.depinfos.iteritems(): + for val, depinfo in self.depinfos.items(): leaf = True for dep in depinfo.deps: - if not self.depinfos.has_key(dep): + if dep not in self.depinfos: raise UnresolvedDependency(val, dep) leaf = False self.depinfos[dep].revdeps.append(val) if leaf: next_leaves.append(val) while leaves or next_leaves: if not leaves: leaves = next_leaves leaves.sort() next_leaves = [] val = leaves.pop(0) depinfo = self.depinfos[val] self.output_by_depinfo(depinfo) for revdep in depinfo.revdeps: revdepinfo = self.depinfos[revdep] revdepinfo.deps.remove(val) if not revdepinfo.deps: next_leaves.append(revdep) del self.depinfos[val] for provider in structs + mappings + exts: name = get_by_path(provider, '@name') array_name = get_by_path(provider, '@array-name') array_depth = get_by_path(provider, '@array-depth') if array_depth: array_depth = int(array_depth) else: array_depth = None sig = provider.getAttribute('type') tptype = provider.getAttribute('name') external = (sig, tptype) in self.externals binding = binding_from_decl(name, array_name, array_depth, external) self.provide(binding.val) if binding.array_val: self.provide(binding.array_val) d = binding.array_depth while d > 1: d -= 1 self.provide(binding.array_val + ('List' * d)) if self.required_custom: raise MissingTypes(self.required_custom) def provide(self, type): if type in self.required_custom: self.required_custom.remove(type) def output_by_depinfo(self, depinfo): names, docstrings, bindings = extract_arg_or_member_info(get_by_path(depinfo.el, 'member'), self.custom_lists, self.externals, None, self.refs, ' * ', (' /**', ' */')) members = len(names) if depinfo.el.localName == 'struct': if members == 0: raise EmptyStruct(depinfo.binding.val) self.decl("""\ /** * \\struct %(name)s * \\ingroup struct %(headercmd)s\ * * Structure type generated from the specification. %(docstring)s\ */ struct %(visibility)s %(name)s { """ % { 'name' : depinfo.binding.val, 'headercmd': get_headerfile_cmd(self.realinclude, self.prettyinclude), 'docstring' : format_docstring(depinfo.el, self.refs), 'visibility': self.visibility, }) - for i in xrange(members): + for i in range(members): self.decl("""\ %s\ %s %s; """ % (docstrings[i], bindings[i].val, names[i])) self.decl("""\ }; """) self.both('%s bool operator==(%s v1, %s v2)' % (self.visibility, depinfo.binding.inarg, depinfo.binding.inarg)) self.decl(';\n') self.impl(""" {""") if (bindings[0].val != 'QDBusVariant'): self.impl(""" return ((v1.%s == v2.%s)""" % (names[0], names[0])) else: self.impl(""" return ((v1.%s.variant() == v2.%s.variant())""" % (names[0], names[0])) - for i in xrange(1, members): + for i in range(1, members): if (bindings[i].val != 'QDBusVariant'): self.impl(""" && (v1.%s == v2.%s)""" % (names[i], names[i])) else: self.impl(""" && (v1.%s.variant() == v2.%s.variant())""" % (names[i], names[i])) self.impl(""" ); } """) self.decl('inline bool operator!=(%s v1, %s v2)' % (depinfo.binding.inarg, depinfo.binding.inarg)) self.decl(""" { return !operator==(v1, v2); } """) self.both('%s QDBusArgument& operator<<(QDBusArgument& arg, %s val)' % (self.visibility, depinfo.binding.inarg)) self.decl(';\n') self.impl(""" { arg.beginStructure(); arg << %s; arg.endStructure(); return arg; } """ % ' << '.join(['val.' + name for name in names])) self.both('%s const QDBusArgument& operator>>(const QDBusArgument& arg, %s val)' % (self.visibility, depinfo.binding.outarg)) self.decl(';\n\n') self.impl(""" { arg.beginStructure(); arg >> %s; arg.endStructure(); return arg; } """ % ' >> '.join(['val.' + name for name in names])) elif depinfo.el.localName == 'mapping': if members != 2: raise MalformedMapping(depinfo.binding.val, members) realtype = 'QMap<%s, %s>' % (bindings[0].val, (bindings[1].val.endswith('>') and bindings[1].val + ' ') or bindings[1].val) self.decl("""\ /** * \\struct %s * \\ingroup mapping %s\ * * Mapping type generated from the specification. Convertible with * %s, but needed to have a discrete type in the Qt type system. %s\ */ """ % (depinfo.binding.val, get_headerfile_cmd(self.realinclude, self.prettyinclude), realtype, format_docstring(depinfo.el, self.refs))) self.decl(self.faketype(depinfo.binding.val, realtype)) else: raise WTF(depinfo.el.localName) self.to_declare.append(self.namespace + '::' + depinfo.binding.val) if depinfo.binding.array_val: self.to_declare.append('%s::%s' % (self.namespace, depinfo.binding.array_val)) self.decl("""\ /** * \\ingroup list %s\ * * Array of %s values. */ typedef %s %s; """ % (get_headerfile_cmd(self.realinclude, self.prettyinclude), depinfo.binding.val, 'QList<%s>' % depinfo.binding.val, depinfo.binding.array_val)) i = depinfo.binding.array_depth while i > 1: i -= 1 self.to_declare.append('%s::%s%s' % (self.namespace, depinfo.binding.array_val, ('List' * i))) list_of = depinfo.binding.array_val + ('List' * (i-1)) self.decl("""\ /** * \\ingroup list %s\ * * Array of %s values. */ typedef QList<%s> %sList; """ % (get_headerfile_cmd(self.realinclude, self.prettyinclude), list_of, list_of, list_of)) def faketype(self, fake, real): return """\ struct %(visibility)s %(fake)s : public %(real)s { - inline %(fake)s() : %(real)s() {} - inline %(fake)s(const %(real)s& a) : %(real)s(a) {} + %(fake)s() : %(real)s() {} + %(fake)s(const %(real)s& a) : %(real)s(a) {} - inline %(fake)s& operator=(const %(real)s& a) + %(fake)s& operator=(const %(real)s& a) { *(static_cast<%(real)s*>(this)) = a; return *this; } }; """ % {'fake' : fake, 'real' : real, 'visibility': self.visibility} if __name__ == '__main__': options, argv = gnu_getopt(sys.argv[1:], '', ['declfile=', 'implfile=', 'realinclude=', 'prettyinclude=', 'extraincludes=', 'must-define=', 'namespace=', 'specxml=', 'visibility=', ]) try: Generator(dict(options))() except BrokenSpecException as e: - print >> sys.stderr, 'Your spec is broken, dear developer! %s' % e + print('Your spec is broken, dear developer! %s' % e, file=sys.stderr) sys.exit(42) diff --git a/tools/xincludator.py b/tools/xincludator.py index 63e106a..5f852bf 100644 --- a/tools/xincludator.py +++ b/tools/xincludator.py @@ -1,39 +1,47 @@ #!/usr/bin/python +import sys from sys import argv, stdout, stderr import codecs, locale import os import xml.dom.minidom -stdout = codecs.getwriter('utf-8')(stdout) +if sys.version_info[0] < 3: + stdout = codecs.getwriter('utf-8')(stdout) NS_XI = 'http://www.w3.org/2001/XInclude' def xincludate(dom, base, dropns = []): remove_attrs = [] - for i in xrange(dom.documentElement.attributes.length): + for i in range(dom.documentElement.attributes.length): attr = dom.documentElement.attributes.item(i) if attr.prefix == 'xmlns': if attr.localName in dropns: remove_attrs.append(attr) else: dropns.append(attr.localName) for attr in remove_attrs: dom.documentElement.removeAttributeNode(attr) for include in dom.getElementsByTagNameNS(NS_XI, 'include'): href = include.getAttribute('href') # FIXME: assumes Unixy paths filename = os.path.join(os.path.dirname(base), href) subdom = xml.dom.minidom.parse(filename) xincludate(subdom, filename, dropns) if './' in href: subdom.documentElement.setAttribute('xml:base', href) include.parentNode.replaceChild(subdom.documentElement, include) if __name__ == '__main__': argv = argv[1:] dom = xml.dom.minidom.parse(argv[0]) xincludate(dom, argv[0]) - xml = dom.toxml() - stdout.write(xml) - stdout.write('\n') + + if sys.version_info[0] >= 3: + xml = dom.toxml('utf-8') + stdout.buffer.write(xml) + stdout.buffer.write(b'\n') + else: + xml = dom.toxml() + stdout.write(xml) + stdout.write('\n')