File indexing completed on 2024-04-21 05:01:51

0001 #!/usr/bin/python
0002 #
0003 # Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk>
0004 # Copyright (C) 2008 Nokia Corporation
0005 #
0006 # This library is free software; you can redistribute it and/or
0007 # modify it under the terms of the GNU Lesser General Public
0008 # License as published by the Free Software Foundation; either
0009 # version 2.1 of the License, or (at your option) any later version.
0010 #
0011 # This library is distributed in the hope that it will be useful,
0012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014 # Lesser General Public License for more details.
0015 #
0016 # You should have received a copy of the GNU Lesser General Public
0017 # License along with this library; if not, write to the Free Software
0018 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0019 
0020 import sys
0021 import xml.dom.minidom
0022 from getopt import gnu_getopt
0023 
0024 from libtpcodegen import NS_TP, get_descendant_text, get_by_path
0025 from libqtcodegen import binding_from_usage, binding_from_decl, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_qt_name, get_headerfile_cmd, RefRegistry
0026 
0027 class BrokenSpecException(Exception):
0028     pass
0029 
0030 class MissingTypes(BrokenSpecException):
0031     def __init__(self, types):
0032         super(MissingTypes, self).__init__(self)
0033         self.types = types
0034 
0035     def __str__(self):
0036         typelist = ''.join(['    %s' % t for t in self.types])
0037         return "The following types were used, but not provided by the spec " \
0038             "or by <tp:external-type/> declarations in all.xml:\n%s" % typelist
0039 
0040 class UnresolvedDependency(BrokenSpecException):
0041     def __init__(self, child, parent):
0042         super(UnresolvedDependency, self).__init__(self)
0043         self.child = child
0044         self.parent = parent
0045 
0046     def __str__(self):
0047         return 'Type %s has unresolved dependency on %s' % (
0048             self.child, self.parent)
0049 
0050 class EmptyStruct(BrokenSpecException):
0051     def __init__(self, struct_name):
0052         super(EmptyStruct, self).__init__(self)
0053         self.struct_name = struct_name
0054 
0055     def __str__(self):
0056         return 'tp:struct %s should have some members' % self.struct_name
0057 
0058 class MalformedMapping(BrokenSpecException):
0059     def __init__(self, mapping_name, members):
0060         super(MalformedMapping, self).__init__(self)
0061         self.mapping_name = mapping_name
0062         self.members = members
0063 
0064     def __str__(self):
0065         return 'tp:mapping %s should have 2 members, not %u' % (
0066             self.mapping_name, self.members)
0067 
0068 class WTF(BrokenSpecException):
0069     def __init__(self, element_name):
0070         super(BrokenSpecException, self).__init__(self)
0071         self.element_name = element_name
0072 
0073     def __str__(self):
0074         return 'What the hell is a tp:%s?' % self.element_name
0075 
0076 
0077 class DepInfo:
0078     def __init__(self, el, externals, custom_lists):
0079         self.el = el
0080         name = get_by_path(el, '@name')
0081         array_name = get_by_path(el, '@array-name')
0082         array_depth = get_by_path(el, '@array-depth')
0083         if array_depth:
0084             array_depth = int(array_depth)
0085         else:
0086             array_depth = None
0087         self.binding = binding_from_decl(name, array_name, array_depth)
0088         self.deps = []
0089 
0090         for member in get_by_path(el, 'member'):
0091             sig = member.getAttribute('type')
0092             tptype = member.getAttributeNS(NS_TP, 'type')
0093 
0094             if (sig, tptype) in externals:
0095                 continue
0096 
0097             if tptype.endswith('[]'):
0098                 tptype = tptype[:-2]
0099 
0100             binding = binding_from_usage(sig, tptype, custom_lists)
0101 
0102             if binding.custom_type:
0103                 self.deps.append(binding.val)
0104 
0105         self.revdeps = []
0106 
0107 class Generator(object):
0108     def __init__(self, opts):
0109         try:
0110             self.namespace = opts['--namespace']
0111             self.declfile = opts['--declfile']
0112             self.implfile = opts['--implfile']
0113             self.realinclude = opts['--realinclude']
0114             self.prettyinclude = opts.get('--prettyinclude', self.realinclude)
0115             self.extraincludes = opts.get('--extraincludes', None)
0116             self.must_define = opts.get('--must-define', None)
0117             self.visibility = opts.get('--visibility', '')
0118             dom = xml.dom.minidom.parse(opts['--specxml'])
0119         except KeyError as k:
0120             assert False, 'Missing required parameter %s' % k.args[0]
0121 
0122         self.decls = []
0123         self.impls = []
0124         self.spec = get_by_path(dom, "spec")[0]
0125         self.externals = gather_externals(self.spec)
0126         self.custom_lists = gather_custom_lists(self.spec, self.namespace)
0127         self.required_custom = []
0128         self.required_arrays = []
0129         self.to_declare = []
0130         self.depinfos = {}
0131         self.refs = RefRegistry(self.spec)
0132 
0133     def __call__(self):
0134         # Emit comment header
0135 
0136         self.both('/* Generated from ')
0137         self.both(get_descendant_text(get_by_path(self.spec, 'title')))
0138         version = get_by_path(self.spec, "version")
0139 
0140         if version:
0141             self.both(', version ' + get_descendant_text(version))
0142 
0143         self.both(' */\n')
0144 
0145         # Gather info on available and required types
0146 
0147         self.gather_required()
0148 
0149         if self.must_define:
0150             self.decl('\n')
0151             self.decl('#ifndef %s\n' % self.must_define)
0152             self.decl('#error %s\n' % self.must_define)
0153             self.decl('#endif')
0154 
0155         self.decl('\n')
0156 
0157         if self.extraincludes:
0158             for include in self.extraincludes.split(','):
0159                 self.decl('#include %s\n' % include)
0160 
0161         self.decl("""
0162 #include <QtGlobal>
0163 
0164 #include <QByteArray>
0165 #include <QString>
0166 #include <QStringList>
0167 #include <QVariantList>
0168 #include <QVariantMap>
0169 
0170 #include <QDBusArgument>
0171 #include <QDBusMetaType>
0172 #include <QDBusObjectPath>
0173 #include <QDBusSignature>
0174 #include <QDBusVariant>
0175 
0176 #include <TelepathyQt/Global>
0177 
0178 /**
0179  * \\addtogroup typesconstants Types and constants
0180  *
0181  * Enumerated, flag, structure, list and mapping types and utility constants.
0182  */
0183 
0184 /**
0185  * \\defgroup struct Structure types
0186  * \\ingroup typesconstants
0187  *
0188  * Structure types generated from the specification.
0189  */
0190 
0191 /**
0192  * \\defgroup list List types
0193  * \\ingroup typesconstants
0194  *
0195  * List types generated from the specification.
0196  */
0197 
0198 /**
0199  * \\defgroup mapping Mapping types
0200  * \\ingroup typesconstants
0201  *
0202  * Mapping types generated from the specification.
0203  */
0204 
0205 """)
0206 
0207         if self.must_define:
0208             self.impl("""
0209 #define %s""" % self.must_define)
0210 
0211         self.impl("""
0212 #include "%s"
0213 """ % self.realinclude)
0214 
0215         self.both("""
0216 namespace %s
0217 {
0218 """ % self.namespace)
0219 
0220         # Emit type definitions for types provided in the spec
0221 
0222         self.provide_all()
0223 
0224         # Emit type registration function
0225 
0226         self.decl("""
0227 } // namespace %s
0228 
0229 """ % self.namespace)
0230 
0231         self.impl("""\
0232 TP_QT_NO_EXPORT void _registerTypes()
0233 {
0234     static bool registered = false;
0235     if (registered)
0236         return;
0237     registered = true;
0238 
0239 """)
0240 
0241         # Emit Qt metatype declarations
0242 
0243         self.to_declare.sort()
0244 
0245         for metatype in self.to_declare:
0246             self.decl('Q_DECLARE_METATYPE(%s)\n' % metatype)
0247             self.impl('    qDBusRegisterMetaType<%s>();\n' % ((metatype.endswith('>') and metatype + ' ') or metatype))
0248 
0249         self.impl("""\
0250 }
0251 
0252 } // namespace %s
0253 """ % self.namespace)
0254 
0255         # Write output to files
0256 
0257         open(self.declfile, 'wb').write(''.join(self.decls).encode("utf-8"))
0258         open(self.implfile, 'wb').write(''.join(self.impls).encode("utf-8"))
0259 
0260     def decl(self, str):
0261         self.decls.append(str)
0262 
0263     def impl(self, str):
0264         self.impls.append(str)
0265 
0266     def both(self, str):
0267         self.decl(str)
0268         self.impl(str)
0269 
0270     def gather_required(self):
0271         members = self.spec.getElementsByTagNameNS(NS_TP, 'member')
0272         args = self.spec.getElementsByTagName('arg')
0273         props = self.spec.getElementsByTagName('property')
0274         tp_props = self.spec.getElementsByTagNameNS(NS_TP, 'property')
0275 
0276         for requirer in members + args + props + tp_props:
0277             sig = requirer.getAttribute('type')
0278             tptype = requirer.getAttributeNS(NS_TP, 'type')
0279             external = (sig, tptype) in self.externals
0280             binding = binding_from_usage(sig, tptype, self.custom_lists, external)
0281 
0282             if binding.custom_type and binding.val not in self.required_custom:
0283                 self.required_custom.append(binding.val)
0284 
0285             if not binding.custom_type and binding.array_of and (binding.val, binding.array_of) not in self.required_arrays:
0286                 self.required_arrays.append((binding.val, binding.array_of))
0287 
0288     def provide_all(self):
0289         self.required_arrays.sort()
0290         for (val, array_of) in self.required_arrays:
0291             real = 'QList<%s>' % array_of
0292             self.decl("""\
0293 /**
0294  * \\struct %s
0295  * \\ingroup list
0296 %s\
0297  *
0298  * Generic list type with %s elements. Convertible with
0299  * %s, but needed to have a discrete type in the Qt type system.
0300  */
0301 """ % (val, get_headerfile_cmd(self.realinclude, self.prettyinclude), array_of, real))
0302             self.decl(self.faketype(val, real))
0303             self.to_declare.append(self.namespace + '::' + val)
0304 
0305             self.both('%s QDBusArgument& operator<<(QDBusArgument& arg, const %s &list)' %
0306                     (self.visibility, val))
0307             self.decl(';\n')
0308             self.impl("""
0309 {
0310     int id = qMetaTypeId<%s>();
0311     arg.beginArray(id);
0312     for (int i = 0; i < list.count(); ++i) {
0313         arg << list.at(i);
0314     }
0315     arg.endArray();
0316     return arg;
0317 }
0318 
0319 """ % (array_of))
0320 
0321             self.both('%s const QDBusArgument& operator>>(const QDBusArgument& arg, %s &list)' %
0322                     (self.visibility, val))
0323             self.decl(';\n\n')
0324             self.impl("""
0325 {
0326     arg.beginArray();
0327     list.clear();
0328     while (!arg.atEnd()) {
0329         %s item;
0330         arg >> item;
0331         list.append(item);
0332     }
0333     arg.endArray();
0334     return arg;
0335 }
0336 
0337 """ % (array_of))
0338 
0339         structs = self.spec.getElementsByTagNameNS(NS_TP, 'struct')
0340         mappings = self.spec.getElementsByTagNameNS(NS_TP, 'mapping')
0341         exts = self.spec.getElementsByTagNameNS(NS_TP, 'external-type')
0342 
0343         for deptype in structs + mappings:
0344             info = DepInfo(deptype, self.externals, self.custom_lists)
0345             self.depinfos[info.binding.val] = info
0346 
0347         leaves = []
0348         next_leaves = []
0349 
0350         for val, depinfo in self.depinfos.items():
0351             leaf = True
0352 
0353             for dep in depinfo.deps:
0354                 if dep not in self.depinfos:
0355                     raise UnresolvedDependency(val, dep)
0356 
0357                 leaf = False
0358                 self.depinfos[dep].revdeps.append(val)
0359 
0360             if leaf:
0361                 next_leaves.append(val)
0362 
0363         while leaves or next_leaves:
0364             if not leaves:
0365                 leaves = next_leaves
0366                 leaves.sort()
0367                 next_leaves = []
0368 
0369             val = leaves.pop(0)
0370             depinfo = self.depinfos[val]
0371             self.output_by_depinfo(depinfo)
0372 
0373             for revdep in depinfo.revdeps:
0374                 revdepinfo = self.depinfos[revdep]
0375                 revdepinfo.deps.remove(val)
0376 
0377                 if not revdepinfo.deps:
0378                     next_leaves.append(revdep)
0379 
0380             del self.depinfos[val]
0381 
0382         for provider in structs + mappings + exts:
0383             name = get_by_path(provider, '@name')
0384             array_name = get_by_path(provider, '@array-name')
0385             array_depth = get_by_path(provider, '@array-depth')
0386             if array_depth:
0387                 array_depth = int(array_depth)
0388             else:
0389                 array_depth = None
0390             sig = provider.getAttribute('type')
0391             tptype = provider.getAttribute('name')
0392             external = (sig, tptype) in self.externals
0393             binding = binding_from_decl(name, array_name, array_depth, external)
0394             self.provide(binding.val)
0395 
0396             if binding.array_val:
0397                 self.provide(binding.array_val)
0398 
0399             d = binding.array_depth
0400             while d > 1:
0401                 d -= 1
0402                 self.provide(binding.array_val + ('List' * d))
0403 
0404         if self.required_custom:
0405             raise MissingTypes(self.required_custom)
0406 
0407     def provide(self, type):
0408         if type in self.required_custom:
0409             self.required_custom.remove(type)
0410 
0411     def output_by_depinfo(self, depinfo):
0412         names, docstrings, bindings = extract_arg_or_member_info(get_by_path(depinfo.el, 'member'), self.custom_lists, self.externals, None, self.refs, '     * ', ('    /**', '     */'))
0413         members = len(names)
0414 
0415         if depinfo.el.localName == 'struct':
0416             if members == 0:
0417                 raise EmptyStruct(depinfo.binding.val)
0418 
0419             self.decl("""\
0420 /**
0421  * \\struct %(name)s
0422  * \\ingroup struct
0423 %(headercmd)s\
0424  *
0425  * Structure type generated from the specification.
0426 %(docstring)s\
0427  */
0428 struct %(visibility)s %(name)s
0429 {
0430 """ % {
0431         'name' : depinfo.binding.val,
0432         'headercmd': get_headerfile_cmd(self.realinclude, self.prettyinclude),
0433         'docstring' : format_docstring(depinfo.el, self.refs),
0434         'visibility': self.visibility,
0435         })
0436 
0437             for i in range(members):
0438                 self.decl("""\
0439 %s\
0440     %s %s;
0441 """ % (docstrings[i], bindings[i].val, names[i]))
0442 
0443             self.decl("""\
0444 };
0445 
0446 """)
0447 
0448             self.both('%s bool operator==(%s v1, %s v2)' %
0449                     (self.visibility,
0450                      depinfo.binding.inarg,
0451                      depinfo.binding.inarg))
0452             self.decl(';\n')
0453             self.impl("""
0454 {""")
0455             if (bindings[0].val != 'QDBusVariant'):
0456                 self.impl("""
0457     return ((v1.%s == v2.%s)""" % (names[0], names[0]))
0458             else:
0459                 self.impl("""
0460     return ((v1.%s.variant() == v2.%s.variant())""" % (names[0], names[0]))
0461             for i in range(1, members):
0462                 if (bindings[i].val != 'QDBusVariant'):
0463                     self.impl("""
0464             && (v1.%s == v2.%s)""" % (names[i], names[i]))
0465                 else:
0466                     self.impl("""
0467             && (v1.%s.variant() == v2.%s.variant())""" % (names[i], names[i]))
0468             self.impl("""
0469             );
0470 }
0471 
0472 """)
0473 
0474             self.decl('inline bool operator!=(%s v1, %s v2)' %
0475                       (depinfo.binding.inarg, depinfo.binding.inarg))
0476             self.decl("""
0477 {
0478     return !operator==(v1, v2);
0479 }
0480 """)
0481 
0482             self.both('%s QDBusArgument& operator<<(QDBusArgument& arg, %s val)' %
0483                     (self.visibility, depinfo.binding.inarg))
0484             self.decl(';\n')
0485             self.impl("""
0486 {
0487     arg.beginStructure();
0488     arg << %s;
0489     arg.endStructure();
0490     return arg;
0491 }
0492 
0493 """ % ' << '.join(['val.' + name for name in names]))
0494 
0495             self.both('%s const QDBusArgument& operator>>(const QDBusArgument& arg, %s val)' %
0496                     (self.visibility, depinfo.binding.outarg))
0497             self.decl(';\n\n')
0498             self.impl("""
0499 {
0500     arg.beginStructure();
0501     arg >> %s;
0502     arg.endStructure();
0503     return arg;
0504 }
0505 
0506 """ % ' >> '.join(['val.' + name for name in names]))
0507         elif depinfo.el.localName == 'mapping':
0508             if members != 2:
0509                 raise MalformedMapping(depinfo.binding.val, members)
0510 
0511             realtype = 'QMap<%s, %s>' % (bindings[0].val, (bindings[1].val.endswith('>') and bindings[1].val + ' ') or bindings[1].val)
0512             self.decl("""\
0513 /**
0514  * \\struct %s
0515  * \\ingroup mapping
0516 %s\
0517  *
0518  * Mapping type generated from the specification. Convertible with
0519  * %s, but needed to have a discrete type in the Qt type system.
0520 %s\
0521  */
0522 """ % (depinfo.binding.val, get_headerfile_cmd(self.realinclude, self.prettyinclude), realtype, format_docstring(depinfo.el, self.refs)))
0523             self.decl(self.faketype(depinfo.binding.val, realtype))
0524         else:
0525             raise WTF(depinfo.el.localName)
0526 
0527         self.to_declare.append(self.namespace + '::' + depinfo.binding.val)
0528 
0529         if depinfo.binding.array_val:
0530             self.to_declare.append('%s::%s' % (self.namespace, depinfo.binding.array_val))
0531             self.decl("""\
0532 /**
0533  * \\ingroup list
0534 %s\
0535  *
0536  * Array of %s values.
0537  */
0538 typedef %s %s;
0539 
0540 """ % (get_headerfile_cmd(self.realinclude, self.prettyinclude), depinfo.binding.val, 'QList<%s>' % depinfo.binding.val, depinfo.binding.array_val))
0541 
0542         i = depinfo.binding.array_depth
0543         while i > 1:
0544             i -= 1
0545             self.to_declare.append('%s::%s%s' % (self.namespace, depinfo.binding.array_val, ('List' * i)))
0546             list_of = depinfo.binding.array_val + ('List' * (i-1))
0547             self.decl("""\
0548 /**
0549  * \\ingroup list
0550 %s\
0551  *
0552  * Array of %s values.
0553  */
0554 typedef QList<%s> %sList;
0555 
0556 """ % (get_headerfile_cmd(self.realinclude, self.prettyinclude), list_of, list_of, list_of))
0557 
0558     def faketype(self, fake, real):
0559         return """\
0560 struct %(visibility)s %(fake)s : public %(real)s
0561 {
0562     %(fake)s() : %(real)s() {}
0563     %(fake)s(const %(real)s& a) : %(real)s(a) {}
0564 
0565     %(fake)s& operator=(const %(real)s& a)
0566     {
0567         *(static_cast<%(real)s*>(this)) = a;
0568         return *this;
0569     }
0570 };
0571 
0572 """ % {'fake' : fake, 'real' : real, 'visibility': self.visibility}
0573 
0574 if __name__ == '__main__':
0575     options, argv = gnu_getopt(sys.argv[1:], '',
0576             ['declfile=',
0577              'implfile=',
0578              'realinclude=',
0579              'prettyinclude=',
0580              'extraincludes=',
0581              'must-define=',
0582              'namespace=',
0583              'specxml=',
0584              'visibility=',
0585              ])
0586 
0587     try:
0588         Generator(dict(options))()
0589     except BrokenSpecException as e:
0590         print('Your spec is broken, dear developer! %s' % e, file=sys.stderr)
0591         sys.exit(42)