diff options
Diffstat (limited to 'qt4/tools')
-rw-r--r-- | qt4/tools/CMakeLists.txt | 124 | ||||
-rw-r--r-- | qt4/tools/c-constants-gen.py | 154 | ||||
-rw-r--r-- | qt4/tools/check-misc.sh | 13 | ||||
-rw-r--r-- | qt4/tools/check-whitespace.sh | 17 | ||||
-rw-r--r-- | qt4/tools/git-which-branch.sh | 25 | ||||
-rw-r--r-- | qt4/tools/glib-ginterface-gen.py | 802 | ||||
-rw-r--r-- | qt4/tools/glib-gtypes-generator.py | 291 | ||||
-rw-r--r-- | qt4/tools/glib-interfaces-gen.py | 119 | ||||
-rw-r--r-- | qt4/tools/glib-signals-marshal-gen.py | 55 | ||||
-rw-r--r-- | qt4/tools/libglibcodegen.py | 172 | ||||
-rw-r--r-- | qt4/tools/libqt4codegen.py | 499 | ||||
-rw-r--r-- | qt4/tools/libtpcodegen.py | 215 | ||||
-rw-r--r-- | qt4/tools/manager-file.py | 175 | ||||
-rw-r--r-- | qt4/tools/qt4-client-gen.py | 547 | ||||
-rw-r--r-- | qt4/tools/qt4-constants-gen.py | 310 | ||||
-rw-r--r-- | qt4/tools/qt4-types-gen.py | 557 | ||||
-rwxr-xr-x | qt4/tools/repeat-tests.sh | 23 | ||||
-rw-r--r-- | qt4/tools/telepathy-glib.supp | 390 | ||||
-rw-r--r-- | qt4/tools/tp-qt4-tests.supp | 53 | ||||
-rw-r--r-- | qt4/tools/with-session-bus.sh | 94 | ||||
-rw-r--r-- | qt4/tools/xincludator.py | 39 |
21 files changed, 4674 insertions, 0 deletions
diff --git a/qt4/tools/CMakeLists.txt b/qt4/tools/CMakeLists.txt new file mode 100644 index 000000000..3c023216a --- /dev/null +++ b/qt4/tools/CMakeLists.txt @@ -0,0 +1,124 @@ +# Some useful commands +add_custom_command(OUTPUT FIXME.out + + COMMAND egrep + + ARGS -A 5 '[F]IXME|[T]ODO|[X]XX' ${CMAKE_SOURCE_DIR}/TelepathyQt4/*.[ch]* + ${CMAKE_SOURCE_DIR}/TelepathyQt4/*.[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-qt4) + + 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-qt4/ + + 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 + libqt4codegen.py + qt4-client-gen.py + qt4-constants-gen.py + qt4-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/qt4/tools/c-constants-gen.py b/qt4/tools/c-constants-gen.py new file mode 100644 index 000000000..8969ffdca --- /dev/null +++ b/qt4/tools/c-constants-gen.py @@ -0,0 +1,154 @@ +#!/usr/bin/python + +from sys import argv, stdout, stderr +import xml.dom.minidom + +from libglibcodegen import NS_TP, get_docstring, \ + get_descendant_text, get_by_path + +class Generator(object): + def __init__(self, prefix, dom): + self.prefix = prefix + '_' + self.spec = get_by_path(dom, "spec")[0] + + def __call__(self): + self.do_header() + self.do_body() + self.do_footer() + + def write(self, code): + stdout.write(code.encode('utf-8')) + + # 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("""\ +/** + * +%s: +""" % (self.prefix + name).replace('_', '')) + for flag in get_by_path(flags, 'flag'): + self.do_gtkdoc(flag, value_prefix) + self.write(' *\n') + docstrings = get_by_path(flags, 'docstring') + if docstrings: + self.write("""\ + * <![CDATA[%s]]> + * +""" % get_descendant_text(docstrings).replace('\n', ' ')) + self.write("""\ + * Bitfield/set of flags generated from the Telepathy specification. + */ +typedef enum { +""") + 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("""\ +/** + * +%s: +""" % (self.prefix + name).replace('_', '')) + vals = get_by_path(enum, 'enumvalue') + for val in vals: + self.do_gtkdoc(val, value_prefix) + self.write(' *\n') + docstrings = get_by_path(enum, 'docstring') + if docstrings: + self.write("""\ + * <![CDATA[%s]]> + * +""" % get_descendant_text(docstrings).replace('\n', ' ')) + self.write("""\ + * Bitfield/set of flags generated from the Telepathy specification. + */ +typedef enum { +""") + for val in vals: + self.do_val(val, value_prefix) + self.write("""\ +} %(mixed-name)s; + +/** + * NUM_%(upper-plural)s: + * + * 1 higher than the highest valid value of #%(mixed-name)s. + */ +#define NUM_%(upper-plural)s (%(last-val)s+1) + +""" % {'mixed-name' : (self.prefix + name).replace('_', ''), + 'upper-plural' : (self.prefix + 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 + '_' + + node.getAttribute('suffix')).upper()) + self.write(': <![CDATA[') + docstring = get_by_path(node, 'docstring') + self.write(get_descendant_text(docstring).replace('\n', ' ')) + self.write(']]>\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]))() diff --git a/qt4/tools/check-misc.sh b/qt4/tools/check-misc.sh new file mode 100644 index 000000000..89e8e871a --- /dev/null +++ b/qt4/tools/check-misc.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +fail=0 + +( . "${tools_dir}"/check-whitespace.sh ) || fail=$? + +if egrep '(Free\s*Software\s*Foundation.*02139|02111-1307)' "$@" +then + echo "^^^ The above files contain the FSF's old address in GPL headers" + fail=1 +fi + +exit $fail diff --git a/qt4/tools/check-whitespace.sh b/qt4/tools/check-whitespace.sh new file mode 100644 index 000000000..534833126 --- /dev/null +++ b/qt4/tools/check-whitespace.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +fail=0 + +if grep -n ' $' "$@" +then + echo "^^^ The above files contain unwanted trailing spaces" + fail=1 +fi + +if grep -n ' ' "$@" +then + echo "^^^ The above files contain tabs" + fail=1 +fi + +exit $fail diff --git a/qt4/tools/git-which-branch.sh b/qt4/tools/git-which-branch.sh new file mode 100644 index 000000000..b96b5d5e2 --- /dev/null +++ b/qt4/tools/git-which-branch.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# git-which-branch.sh - output the name of the current git branch +# +# The canonical location of this program is the telepathy-spec tools/ +# directory, please synchronize any changes with that copy. +# +# Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/> +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. + +default="$1" +if { ref="`git symbolic-ref HEAD 2>/dev/null`"; }; then + echo ${ref#refs/heads/} + exit 0 +fi + +if test -n "$default"; then + echo "$default" >/dev/null + exit 0 +fi + +echo "no git branch found" >&2 +exit 1 diff --git a/qt4/tools/glib-ginterface-gen.py b/qt4/tools/glib-ginterface-gen.py new file mode 100644 index 000000000..13f7f69eb --- /dev/null +++ b/qt4/tools/glib-ginterface-gen.py @@ -0,0 +1,802 @@ +#!/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 + + +NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + +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 = [] + + 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 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)) + + 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 ' + 'interface.') + self.h(' */') + 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)) + + 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(' * <programlisting>') + 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.prefix_, self.node_name_lc)) + self.h(' * 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.h(' * #undef IMPLEMENT') + self.h(' * }') + self.h(' * </programlisting>') + 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.h(' */') + + 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') + + 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) + 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) + + 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') + for (ctype, name) in in_args: + self.b(' * @%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.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);' + % (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.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' + % dbus_method_name) + self.b(' *') + self.b(' * 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.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('}') + 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') + for (ctype, name) in out_args: + self.h(' * @%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(' */') + 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') + for (ctype, name, gtype) in args: + self.b(' * @%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.' + % (dbus_name, self.iface_name)) + self.b(' */') + + 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.Prefix, self.node_name_mixed, signal_name)) + for (ctype, name, gtype) in args: + in_base_init.append(' * @%s: %s (FIXME, generate documentation)' + % (name, ctype)) + in_base_init.append(' *') + in_base_init.append(' * The %s D-Bus signal is emitted whenever ' + 'this GObject signal is.' % dbus_name) + in_base_init.append(' */') + 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_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) + + self.h('#include <glib-object.h>') + self.h('#include <dbus/dbus-glib.h>') + + if self.have_properties(nodes): + self.h('#include <telepathy-glib/dbus-properties-mixin.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)) + + +def cmdline_error(): + print """\ +usage: + gen-ginterface [OPTIONS] xmlfile Prefix_ +options: + --include='<header.h>' (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/qt4/tools/glib-gtypes-generator.py b/qt4/tools/glib-gtypes-generator.py new file mode 100644 index 000000000..ebc2ad4c9 --- /dev/null +++ b/qt4/tools/glib-gtypes-generator.py @@ -0,0 +1,291 @@ +#!/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 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') + + 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') + + # 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")) + + def c(self, code): + self.body.write(code.encode("utf-8")) + + 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(' * <literal>a{%s}</literal>.\n' % impl_sig) + self.h(' *\n') + + key, value = members + + self.h(' * Keys (D-Bus type <literal>%s</literal>,\n' + % key.getAttribute('type')) + tp_type = key.getAttributeNS(NS_TP, 'type') + if tp_type: + self.h(' * type <literal>%s</literal>,\n' % tp_type) + self.h(' * named <literal>%s</literal>):\n' + % key.getAttribute('name')) + docstring = get_docstring(key) or '(Undocumented)' + self.h(' * %s\n' % xml_escape(docstring)) + self.h(' *\n') + + self.h(' * Values (D-Bus type <literal>%s</literal>,\n' + % value.getAttribute('type')) + tp_type = value.getAttributeNS(NS_TP, 'type') + if tp_type: + self.h(' * type <literal>%s</literal>,\n' % tp_type) + self.h(' * named <literal>%s</literal>):\n' + % value.getAttribute('name')) + docstring = get_docstring(value) or '(Undocumented)' + self.h(' * %s\n' % xml_escape(docstring)) + self.h(' *\n') + + self.h(' */\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.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('<tp:docstring>'): + docstring = docstring[14:] + if docstring.endswith('</tp:docstring>\n'): + docstring = docstring[:-16] + if docstring.strip() in ('<tp:docstring/>', ''): + 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 <literal>(%s)</literal>.\n' + % impl_sig) + self.h(' *\n') + + for i, member in enumerate(members): + self.h(' * Member %d (D-Bus type ' + '<literal>%s</literal>,\n' + % (i, member.getAttribute('type'))) + tp_type = member.getAttributeNS(NS_TP, 'type') + if tp_type: + self.h(' * type <literal>%s</literal>,\n' % tp_type) + self.h(' * named <literal>%s</literal>):\n' + % member.getAttribute('name')) + docstring = get_docstring(member) or '(Undocumented)' + self.h(' * %s\n' % xml_escape(docstring)) + self.h(' *\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.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') + +if __name__ == '__main__': + argv = sys.argv[1:] + + dom = xml.dom.minidom.parse(argv[0]) + + GTypesGenerator(dom, argv[1], argv[2])() diff --git a/qt4/tools/glib-interfaces-gen.py b/qt4/tools/glib-interfaces-gen.py new file mode 100644 index 000000000..95439687e --- /dev/null +++ b/qt4/tools/glib-interfaces-gen.py @@ -0,0 +1,119 @@ +#!/usr/bin/python + +from sys import argv, stdout, stderr +import xml.dom.minidom + +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') + self.spec = get_by_path(dom, "spec")[0] + + def h(self, code): + self.decls.write(code.encode('utf-8')) + + def c(self, code): + self.impls.write(code.encode('utf-8')) + + def __call__(self): + for f in self.h, self.c: + self.do_header(f) + self.do_body() + + # 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(""" + */ + +""") + + # 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("""\ +/** + * %(IFACE_DEFINE)s: + * + * The interface name "%(name)s" + */ +#define %(IFACE_DEFINE)s \\ +"%(name)s" +""" % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \ + parent_name).upper().replace('/', ''), + 'name' : iface.getAttribute('name')}) + + self.h(""" +/** + * %(IFACE_QUARK_DEFINE)s: + * + * Expands to a call to a function that returns a quark for the interface \ +name "%(name)s" + */ +#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(""" +/** + * %(IFACE_PREFIX)s_%(PROP_UC)s: + * + * The fully-qualified property name "%(name)s.%(prop)s" + */ +#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'), + }) + +if __name__ == '__main__': + argv = argv[1:] + Generator(argv[0], argv[1], argv[2], xml.dom.minidom.parse(argv[3]))() diff --git a/qt4/tools/glib-signals-marshal-gen.py b/qt4/tools/glib-signals-marshal-gen.py new file mode 100644 index 000000000..0d02c1341 --- /dev/null +++ b/qt4/tools/glib-signals-marshal-gen.py @@ -0,0 +1,55 @@ +#!/usr/bin/python + +import sys +import xml.dom.minidom +from string import ascii_letters, digits + + +from libglibcodegen import signal_to_marshal_name, method_to_glue_marshal_name + + +class Generator(object): + + def __init__(self, dom): + self.dom = dom + self.marshallers = {} + + def do_method(self, method): + marshaller = method_to_glue_marshal_name(method, 'PREFIX') + + assert '__' in marshaller + rhs = marshaller.split('__', 1)[1].split('_') + + self.marshallers[marshaller] = rhs + + def do_signal(self, signal): + marshaller = signal_to_marshal_name(signal, 'PREFIX') + + assert '__' in marshaller + rhs = marshaller.split('__', 1)[1].split('_') + + self.marshallers[marshaller] = rhs + + def __call__(self): + methods = self.dom.getElementsByTagName('method') + + for method in methods: + self.do_method(method) + + signals = self.dom.getElementsByTagName('signal') + + for signal in signals: + self.do_signal(signal) + + all = self.marshallers.keys() + all.sort() + for marshaller in all: + rhs = self.marshallers[marshaller] + if not marshaller.startswith('g_cclosure'): + print 'VOID:' + ','.join(rhs) + +if __name__ == '__main__': + argv = sys.argv[1:] + dom = xml.dom.minidom.parse(argv[0]) + + Generator(dom)() diff --git a/qt4/tools/libglibcodegen.py b/qt4/tools/libglibcodegen.py new file mode 100644 index 000000000..6a9d21485 --- /dev/null +++ b/qt4/tools/libglibcodegen.py @@ -0,0 +1,172 @@ +"""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 + 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 diff --git a/qt4/tools/libqt4codegen.py b/qt4/tools/libqt4codegen.py new file mode 100644 index 000000000..dd2237891 --- /dev/null +++ b/qt4/tools/libqt4codegen.py @@ -0,0 +1,499 @@ +"""Library code for Qt4 D-Bus-related code generation. + +The master copy of this library is in the telepathy-qt4 repository - +please make any changes there. +""" + +# Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk> +# 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 +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 """ + Nested <%s>s are forbidden. + Parent: + %s... + Child: + %s... + """ % (self.parent.nodeName, self.parent.toxml()[:100], + self.child.toxml()[:100]) + +class _Qt4TypeBinding: + 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) + 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): + 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]: + 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) + 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 _Qt4TypeBinding(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 _Qt4TypeBinding(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_qt4_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 + 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 + + 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): + 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: + return indent + ('\\headerfile %s <%s>\n' % (realinclude, prettyinclude)) + else: + return '' + +def get_qt4_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 qt4_identifier_escape(name.replace('_', '')) + +def qt4_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/qt4/tools/libtpcodegen.py b/qt4/tools/libtpcodegen.py new file mode 100644 index 000000000..837ff2f74 --- /dev/null +++ b/qt4/tools/libtpcodegen.py @@ -0,0 +1,215 @@ +"""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 + + +from string import ascii_letters, digits + + +NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + +_ASCII_ALNUM = ascii_letters + digits + + +def cmp_by_name(node1, node2): + return cmp(node1.getAttributeNode("name").nodeValue, + node2.getAttributeNode("name").nodeValue) + + +def escape_as_identifier(identifier): + """Escape the given string to be a valid D-Bus object path or service + name component, using a reversible encoding to ensure uniqueness. + + The reversible encoding is as follows: + + * The empty string becomes '_' + * Otherwise, each non-alphanumeric character is replaced by '_' plus + two lower-case hex digits; the same replacement is carried out on + the first character, if it's a digit + """ + # '' -> '_' + if not identifier: + return '_' + + # A bit of a fast path for strings which are already OK. + # We deliberately omit '_' because, for reversibility, that must also + # be escaped. + if (identifier.strip(_ASCII_ALNUM) == '' and + identifier[0] in ascii_letters): + return identifier + + # The first character may not be a digit + if identifier[0] not in ascii_letters: + ret = ['_%02x' % ord(identifier[0])] + else: + ret = [identifier[0]] + + # Subsequent characters may be digits or ASCII letters + for c in identifier[1:]: + if c in _ASCII_ALNUM: + ret.append(c) + else: + ret.append('_%02x' % ord(c)) + + return ''.join(ret) + + +def get_by_path(element, path): + branches = path.split('/') + branch = branches[0] + + # Is the current branch an attribute, if so, return the attribute value + if branch[0] == '@': + return element.getAttribute(branch[1:]) + + # Find matching children for the branch + children = [] + if branch == '..': + children.append(element.parentNode) + else: + for x in element.childNodes: + if x.localName == branch: + children.append(x) + + ret = [] + # If this is not the last path element, recursively gather results from + # children + if len(branches) > 1: + for x in children: + add = get_by_path(x, '/'.join(branches[1:])) + if isinstance(add, list): + ret += add + else: + return add + else: + ret = children + + return ret + + +def get_docstring(element): + docstring = None + for x in element.childNodes: + if x.namespaceURI == NS_TP and x.localName == 'docstring': + docstring = x + if docstring is not None: + docstring = docstring.toxml().replace('\n', ' ').strip() + if docstring.startswith('<tp:docstring>'): + docstring = docstring[14:].lstrip() + if docstring.endswith('</tp:docstring>'): + docstring = docstring[:-15].rstrip() + if docstring in ('<tp:docstring/>', ''): + docstring = '' + return docstring + +def get_deprecated(element): + text = [] + for x in element.childNodes: + if hasattr(x, 'data'): + text.append(x.data.replace('\n', ' ').strip()) + else: + # This caters for tp:dbus-ref elements, but little else. + if x.childNodes and hasattr(x.childNodes[0], 'data'): + text.append(x.childNodes[0].data.replace('\n', ' ').strip()) + return ' '.join(text) + +def get_descendant_text(element_or_elements): + if not element_or_elements: + return '' + if isinstance(element_or_elements, list): + return ''.join(map(get_descendant_text, element_or_elements)) + parts = [] + for x in element_or_elements.childNodes: + if x.nodeType == x.TEXT_NODE: + parts.append(x.nodeValue) + elif x.nodeType == x.ELEMENT_NODE: + parts.append(get_descendant_text(x)) + else: + pass + return ''.join(parts) + + +class _SignatureIter: + """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we + can run genginterface in a limited environment with only Python + (like Scratchbox). + """ + def __init__(self, string): + self.remaining = string + + def next(self): + 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/qt4/tools/manager-file.py b/qt4/tools/manager-file.py new file mode 100644 index 000000000..45f640403 --- /dev/null +++ b/qt4/tools/manager-file.py @@ -0,0 +1,175 @@ +#!/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. <http://www.collabora.co.uk/> +# +# 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 + +_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 + + # protocols + for proto, params in protos.iteritems(): + print >> f + print >> f, '[Protocol %s]' % proto + + defaults = {} + + for param, info in params.iteritems(): + 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) + + for param, default in defaults.iteritems(): + print >> f, 'default-%s=%s' % (param, default) + +def write_c_params(f, manager, proto, struct, params): + print >> f, "static const TpCMParamSpec %s_%s_params[] = {" % (manager, proto) + + for param, info in params.iteritems(): + 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, + %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)) + + print >> f, " { NULL }" + print >> f, "};" + +if __name__ == '__main__': + environment = {} + execfile(sys.argv[1], environment) + + f = open('%s/%s.manager' % (sys.argv[2], environment['MANAGER']), 'w') + write_manager(f, environment['MANAGER'], environment['PARAMS']) + f.close() + + f = open('%s/param-spec-struct.h' % sys.argv[2], 'w') + for protocol in environment['PARAMS']: + write_c_params(f, environment['MANAGER'], protocol, + environment['STRUCTS'][protocol], + environment['PARAMS'][protocol]) + f.close() diff --git a/qt4/tools/qt4-client-gen.py b/qt4/tools/qt4-client-gen.py new file mode 100644 index 000000000..465029be4 --- /dev/null +++ b/qt4/tools/qt4-client-gen.py @@ -0,0 +1,547 @@ +#!/usr/bin/python +# +# Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk> +# 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 + +from libtpcodegen import NS_TP, get_descendant_text, get_by_path +from libqt4codegen import binding_from_usage, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_headerfile_cmd, get_qt4_name, qt4_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: + 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 qt4-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 <QtGlobal> + +#include <QString> +#include <QObject> +#include <QVariant> + +#include <QDBusPendingReply> + +#include <TelepathyQt4/AbstractInterface> +#include <TelepathyQt4/DBusProxy> +#include <TelepathyQt4/Global> + +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] + + if xname == self.mainiface: + return -1 + elif yname == self.mainiface: + return 1 + else: + return cmp(xname, yname) + + self.ifacenodes.sort(cmp=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)) + + 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)): + 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)): + 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/qt4/tools/qt4-constants-gen.py b/qt4/tools/qt4-constants-gen.py new file mode 100644 index 000000000..a28050a7a --- /dev/null +++ b/qt4/tools/qt4-constants-gen.py @@ -0,0 +1,310 @@ +#!/usr/bin/python +# +# Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk> +# 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 libqt4codegen 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: + 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) + + 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 <QFlags> + +/** + * \\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/qt4/tools/qt4-types-gen.py b/qt4/tools/qt4-types-gen.py new file mode 100644 index 000000000..0f5545d60 --- /dev/null +++ b/qt4/tools/qt4-types-gen.py @@ -0,0 +1,557 @@ +#!/usr/bin/python +# +# Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk> +# 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 libqt4codegen import binding_from_usage, binding_from_decl, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_qt4_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 <tp:external-type/> 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: + 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 <QtGlobal> + +#include <QByteArray> +#include <QString> +#include <QStringList> +#include <QVariantList> +#include <QVariantMap> + +#include <QDBusArgument> +#include <QDBusMetaType> +#include <QDBusObjectPath> +#include <QDBusSignature> +#include <QDBusVariant> + +#include <TelepathyQt4/Global> + +/** + * \\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("""\ +TELEPATHY_QT4_NO_EXPORT void _registerTypes() +{ + static bool registered = false; + if (registered) + return; + registered = true; + +""") + + # Emit Qt4 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")) + + 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 Qt4 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) + + 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(): + leaf = True + + for dep in depinfo.deps: + if not self.depinfos.has_key(dep): + 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): + 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): + 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 Qt4 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) {} + + inline %(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 + sys.exit(42) diff --git a/qt4/tools/repeat-tests.sh b/qt4/tools/repeat-tests.sh new file mode 100755 index 000000000..8da2f9ea8 --- /dev/null +++ b/qt4/tools/repeat-tests.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +if [ $# -ne 2 ] +then + echo "usage: $0 <command> <number of repetitions>" + echo "example: $0 \"make check-valgrind\" 100" + echo " or: $0 \"make -j4 check\" 100" + exit 1 +fi + +for i in `seq 1 $2` +do + echo -n "Running test iteration ${i}... " + log="test-round-${i}.log" + $1 > ${log} 2>&1 + if grep -q "FAIL" $log + then + echo "FAILED (log in $log)" + else + echo "PASSED" + rm ${log} + fi +done diff --git a/qt4/tools/telepathy-glib.supp b/qt4/tools/telepathy-glib.supp new file mode 100644 index 000000000..28bd5a06a --- /dev/null +++ b/qt4/tools/telepathy-glib.supp @@ -0,0 +1,390 @@ +# Valgrind error suppression file + +# ============================= libc ================================== + +{ + ld.so initialization + selinux + Memcheck:Leak + ... + fun:_dl_init + obj:/lib/ld-*.so +} + +{ + dlopen initialization, triggered by handle-leak-debug code + Memcheck:Leak + ... + fun:__libc_dlopen_mode + fun:init + fun:backtrace + fun:handle_leak_debug_bt + fun:dynamic_ensure_handle + fun:tp_handle_ensure +} + +# default.supp has these for 2.10, but they're too specific +{ + Debian libc6 (2.10.x, 2.11.x) stripped dynamic linker + Memcheck:Cond + fun:index + fun:expand_dynamic_string_token + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib/ld-*.so +} +{ + Debian libc6 (2.9.x - 2.11.x) stripped dynamic linker + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib/ld-*.so +} + +{ + ld.so initialization on glibc 2.9 + Memcheck:Cond + fun:strlen + fun:_dl_init_paths + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib/ld-2.9.so +} + +# ======================= libselinux on Debian amd64 ===================== + +{ + I have no idea what SELinux is doing but it's not my problem + Memcheck:Cond + ... + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 +} + +{ + I have no idea what SELinux is doing but it's not my problem + Memcheck:Value8 + ... + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 +} + +{ + I have no idea what SELinux is doing but it's not my problem + Memcheck:Leak + ... + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 +} + +# ============================= GLib ================================== + +{ + g_set_prgname copies its argument + Memcheck:Leak + ... + fun:g_set_prgname +} + +{ + one g_get_charset per child^Wprocess + Memcheck:Leak + ... + fun:g_get_charset +} + +{ + one g_get_home_dir per process + Memcheck:Leak + ... + fun:g_get_home_dir +} + +{ + GQuarks can't be freed + Memcheck:Leak + ... + fun:g_quark_from_static_string +} + +{ + GQuarks can't be freed + Memcheck:Leak + ... + fun:g_quark_from_string +} + +{ + interned strings can't be freed + Memcheck:Leak + ... + fun:g_intern_string +} + +{ + interned strings can't be freed + Memcheck:Leak + ... + fun:g_intern_static_string +} + +{ + shared global default g_main_context + Memcheck:Leak + ... + fun:g_main_context_new + fun:g_main_context_default +} + +{ + GTest initialization + Memcheck:Leak + ... + fun:g_test_init + fun:main +} + +{ + GTest admin + Memcheck:Leak + ... + fun:g_test_add_vtable +} + +{ + GTest pseudorandomness + Memcheck:Leak + ... + fun:g_rand_new_with_seed_array + fun:test_run_seed + ... + fun:g_test_run +} + +{ + GSLice initialization + Memcheck:Leak + ... + fun:g_malloc0 + fun:g_slice_init_nomessage + fun:g_slice_alloc +} + +# ============================= GObject =============================== + +{ + g_type_init + Memcheck:Leak + ... + fun:g_type_init +} + +{ + g_type_init_with_debug_flags + Memcheck:Leak + ... + fun:g_type_init_with_debug_flags +} + +{ + g_type_register_static + Memcheck:Leak + ... + fun:g_type_register_static +} + +{ + g_type_add_interface_static + Memcheck:Leak + ... + fun:g_type_add_interface_static +} + +{ + initialization of interfaces + Memcheck:Leak + ... + fun:type_iface_vtable_base_init_Wm + fun:g_type_class_ref +} + +# ============================= GIO =================================== + +{ + GIO init + Memcheck:Leak + ... + fun:g_inet_address_class_intern_init +} + +{ + g_simple_async_result class + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_simple_async_result_new +} + +# ============================= dbus-glib ============================= + +{ + registering marshallers is permanent + Memcheck:Leak + ... + fun:dbus_g_object_register_marshaller_array + fun:dbus_g_object_register_marshaller +} + +{ + dbus-glib specialized GTypes are permanent + Memcheck:Leak + ... + fun:dbus_g_type_specialized_init +} + +{ + libdbus shared connection + Memcheck:Leak + ... + fun:dbus_g_bus_get +} + +{ + dbus-gobject registrations aren't freed unless we fall off the bus + Memcheck:Leak + ... + fun:g_slist_append + fun:dbus_g_connection_register_g_object +} + +{ + DBusGProxy slots aren't freed unless we fall off the bus + Memcheck:Leak + ... + fun:dbus_connection_allocate_data_slot + ... + fun:dbus_g_proxy_constructor +} + +{ + error registrations are for life, not just for Christmas + Memcheck:Leak + ... + fun:dbus_g_error_domain_register +} + +{ + DBusGProxy class init + Memcheck:Leak + ... + fun:dbus_g_proxy_class_init +} + +# ============================= telepathy-glib ======================== + +{ + tp_dbus_daemon_constructor @daemons once per DBusConnection + Memcheck:Leak + ... + fun:g_slice_alloc + fun:tp_dbus_daemon_constructor +} + +{ + tp_proxy_subclass_add_error_mapping refs the enum + Memcheck:Leak + ... + fun:g_type_class_ref + fun:tp_proxy_subclass_add_error_mapping +} + +{ + tp_proxy_or_subclass_hook_on_interface_add never frees its list + Memcheck:Leak + ... + fun:tp_proxy_or_subclass_hook_on_interface_add +} + +{ + tp_dbus_daemon_constructor filter not freed til we fall off the bus + Memcheck:Leak + ... + fun:dbus_connection_add_filter + fun:tp_dbus_daemon_constructor +} + +{ + tp_g_socket_address_from_variant reffing GNIO types + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:tp_g_socket_address_from_variant +} + +{ + creating classes for DBusGProxy + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_object_new + ... + fun:tp_proxy_borrow_interface_by_id +} + +{ + creating classes for tp_dbus_daemon_new + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_object_new + ... + fun:tp_dbus_daemon_new +} + +{ + creating classes for TpCHannel + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_object_new + ... + fun:tp_channel_new +} + +{ + creating a boxed type to use in TpCapabilities + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_param_spec_boxed + fun:tp_capabilities_class_intern_init +} + +# ============================= questionable ========================== + +{ + creating classes for instances (this is a pretty big hammer) + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_type_create_instance + ... + fun:g_param_spec_string +} diff --git a/qt4/tools/tp-qt4-tests.supp b/qt4/tools/tp-qt4-tests.supp new file mode 100644 index 000000000..2c0f12011 --- /dev/null +++ b/qt4/tools/tp-qt4-tests.supp @@ -0,0 +1,53 @@ +# QDBus doesn't call dbus_shutdown, so some D-Bus internal data structures are leaked. +# We never call any low-level dbus message creation functions ourselves - if there are leaks, +# they're either caused by not calling dbus_shutdown, QDBus bugs or libdbus bugs - neither of which +# are our problem. + +{ + Initial session bus registration message + Memcheck:Leak + fun:malloc + fun:dbus_message_new_empty_header +} + +# The conference test CM channel object leaks some crazy GValue boxed data which I don't have the +# energy to investigate how to properly free now - it's not production code anyway. + +{ + Conference test CM channel boxed GValue data + Memcheck:Leak + ... + fun:g_boxed_copy + ... + fun:_ZN18TestConferenceChan12initTestCaseEv +} + +# Reported as https://bugs.freedesktop.org/show_bug.cgi?id=32116 + +{ + TpBaseConnectionManager legacy protocol objects + Memcheck:Leak + ... + fun:g_object_new + ... + fun:tp_base_connection_manager_register +} + +# O(number of error domains) leak from dbus_g_method_return_error +{ + dbus_g_method_return_error error domain enum class + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:dbus_g_method_return_error +} + +# O(1) leak from tp_base_connection_manager installing the param spec for the dbus-daemon param +{ + tp_base_connection_manager dbus-daemon param spec + Memcheck:Leak + ... + fun:g_param_spec_object + fun:tp_base_connection_manager_class_intern_init +} diff --git a/qt4/tools/with-session-bus.sh b/qt4/tools/with-session-bus.sh new file mode 100644 index 000000000..063bd7e17 --- /dev/null +++ b/qt4/tools/with-session-bus.sh @@ -0,0 +1,94 @@ +#!/bin/sh +# with-session-bus.sh - run a program with a temporary D-Bus session daemon +# +# The canonical location of this program is the telepathy-glib tools/ +# directory, please synchronize any changes with that copy. +# +# Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/> +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. + +set -e + +me=with-session-bus + +dbus_daemon_args="--print-address=5 --print-pid=6 --fork" +sleep=0 + +usage () +{ + echo "usage: $me [options] -- program [program_options]" >&2 + echo "Requires write access to the current directory." >&2 + echo "" >&2 + echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2 + echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2 + echo "The output of dbus-monitor is saved in $me-<pid>.dbus-monitor-logs" >&2 + exit 2 +} + +while test "z$1" != "z--"; do + case "$1" in + --sleep=*) + sleep="$1" + sleep="${sleep#--sleep=}" + shift + ;; + --session) + dbus_daemon_args="$dbus_daemon_args --session" + shift + ;; + --config-file=*) + # FIXME: assumes config file doesn't contain any special characters + dbus_daemon_args="$dbus_daemon_args $1" + shift + ;; + *) + usage + ;; + esac +done +shift +if test "z$1" = "z"; then usage; fi + +exec 5> $me-$$.address +exec 6> $me-$$.pid + +cleanup () +{ + pid=`head -n1 $me-$$.pid` + if test -n "$pid" ; then + echo "Killing temporary bus daemon: $pid" >&2 + kill -INT "$pid" + fi + rm -f $me-$$.address + rm -f $me-$$.pid +} + +trap cleanup INT HUP TERM +dbus-daemon $dbus_daemon_args + +{ echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2 +{ echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2 + +e=0 +DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" +export DBUS_SESSION_BUS_ADDRESS + +if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then + echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 + dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ + > $me-$$.dbus-monitor-logs 2>&1 & +fi + +"$@" || e=$? + +if test $sleep != 0; then + sleep $sleep +fi + +trap - INT HUP TERM +cleanup + +exit $e diff --git a/qt4/tools/xincludator.py b/qt4/tools/xincludator.py new file mode 100644 index 000000000..63e106ace --- /dev/null +++ b/qt4/tools/xincludator.py @@ -0,0 +1,39 @@ +#!/usr/bin/python + +from sys import argv, stdout, stderr +import codecs, locale +import os +import xml.dom.minidom + +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): + 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') |