File indexing completed on 2024-05-05 17:01:42

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, 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, 'w').write(''.join(self.decls).encode("utf-8"))
0258         open(self.implfile, 'w').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         structs = self.spec.getElementsByTagNameNS(NS_TP, 'struct')
0306         mappings = self.spec.getElementsByTagNameNS(NS_TP, 'mapping')
0307         exts = self.spec.getElementsByTagNameNS(NS_TP, 'external-type')
0308 
0309         for deptype in structs + mappings:
0310             info = DepInfo(deptype, self.externals, self.custom_lists)
0311             self.depinfos[info.binding.val] = info
0312 
0313         leaves = []
0314         next_leaves = []
0315 
0316         for val, depinfo in self.depinfos.iteritems():
0317             leaf = True
0318 
0319             for dep in depinfo.deps:
0320                 if not self.depinfos.has_key(dep):
0321                     raise UnresolvedDependency(val, dep)
0322 
0323                 leaf = False
0324                 self.depinfos[dep].revdeps.append(val)
0325 
0326             if leaf:
0327                 next_leaves.append(val)
0328 
0329         while leaves or next_leaves:
0330             if not leaves:
0331                 leaves = next_leaves
0332                 leaves.sort()
0333                 next_leaves = []
0334 
0335             val = leaves.pop(0)
0336             depinfo = self.depinfos[val]
0337             self.output_by_depinfo(depinfo)
0338 
0339             for revdep in depinfo.revdeps:
0340                 revdepinfo = self.depinfos[revdep]
0341                 revdepinfo.deps.remove(val)
0342 
0343                 if not revdepinfo.deps:
0344                     next_leaves.append(revdep)
0345 
0346             del self.depinfos[val]
0347 
0348         for provider in structs + mappings + exts:
0349             name = get_by_path(provider, '@name')
0350             array_name = get_by_path(provider, '@array-name')
0351             array_depth = get_by_path(provider, '@array-depth')
0352             if array_depth:
0353                 array_depth = int(array_depth)
0354             else:
0355                 array_depth = None
0356             sig = provider.getAttribute('type')
0357             tptype = provider.getAttribute('name')
0358             external = (sig, tptype) in self.externals
0359             binding = binding_from_decl(name, array_name, array_depth, external)
0360             self.provide(binding.val)
0361 
0362             if binding.array_val:
0363                 self.provide(binding.array_val)
0364 
0365             d = binding.array_depth
0366             while d > 1:
0367                 d -= 1
0368                 self.provide(binding.array_val + ('List' * d))
0369 
0370         if self.required_custom:
0371             raise MissingTypes(self.required_custom)
0372 
0373     def provide(self, type):
0374         if type in self.required_custom:
0375             self.required_custom.remove(type)
0376 
0377     def output_by_depinfo(self, depinfo):
0378         names, docstrings, bindings = extract_arg_or_member_info(get_by_path(depinfo.el, 'member'), self.custom_lists, self.externals, None, self.refs, '     * ', ('    /**', '     */'))
0379         members = len(names)
0380 
0381         if depinfo.el.localName == 'struct':
0382             if members == 0:
0383                 raise EmptyStruct(depinfo.binding.val)
0384 
0385             self.decl("""\
0386 /**
0387  * \\struct %(name)s
0388  * \\ingroup struct
0389 %(headercmd)s\
0390  *
0391  * Structure type generated from the specification.
0392 %(docstring)s\
0393  */
0394 struct %(visibility)s %(name)s
0395 {
0396 """ % {
0397         'name' : depinfo.binding.val,
0398         'headercmd': get_headerfile_cmd(self.realinclude, self.prettyinclude),
0399         'docstring' : format_docstring(depinfo.el, self.refs),
0400         'visibility': self.visibility,
0401         })
0402 
0403             for i in xrange(members):
0404                 self.decl("""\
0405 %s\
0406     %s %s;
0407 """ % (docstrings[i], bindings[i].val, names[i]))
0408 
0409             self.decl("""\
0410 };
0411 
0412 """)
0413 
0414             self.both('%s bool operator==(%s v1, %s v2)' %
0415                     (self.visibility,
0416                      depinfo.binding.inarg,
0417                      depinfo.binding.inarg))
0418             self.decl(';\n')
0419             self.impl("""
0420 {""")
0421             if (bindings[0].val != 'QDBusVariant'):
0422                 self.impl("""
0423     return ((v1.%s == v2.%s)""" % (names[0], names[0]))
0424             else:
0425                 self.impl("""
0426     return ((v1.%s.variant() == v2.%s.variant())""" % (names[0], names[0]))
0427             for i in xrange(1, members):
0428                 if (bindings[i].val != 'QDBusVariant'):
0429                     self.impl("""
0430             && (v1.%s == v2.%s)""" % (names[i], names[i]))
0431                 else:
0432                     self.impl("""
0433             && (v1.%s.variant() == v2.%s.variant())""" % (names[i], names[i]))
0434             self.impl("""
0435             );
0436 }
0437 
0438 """)
0439 
0440             self.decl('inline bool operator!=(%s v1, %s v2)' %
0441                       (depinfo.binding.inarg, depinfo.binding.inarg))
0442             self.decl("""
0443 {
0444     return !operator==(v1, v2);
0445 }
0446 """)
0447 
0448             self.both('%s QDBusArgument& operator<<(QDBusArgument& arg, %s val)' %
0449                     (self.visibility, depinfo.binding.inarg))
0450             self.decl(';\n')
0451             self.impl("""
0452 {
0453     arg.beginStructure();
0454     arg << %s;
0455     arg.endStructure();
0456     return arg;
0457 }
0458 
0459 """ % ' << '.join(['val.' + name for name in names]))
0460 
0461             self.both('%s const QDBusArgument& operator>>(const QDBusArgument& arg, %s val)' %
0462                     (self.visibility, depinfo.binding.outarg))
0463             self.decl(';\n\n')
0464             self.impl("""
0465 {
0466     arg.beginStructure();
0467     arg >> %s;
0468     arg.endStructure();
0469     return arg;
0470 }
0471 
0472 """ % ' >> '.join(['val.' + name for name in names]))
0473         elif depinfo.el.localName == 'mapping':
0474             if members != 2:
0475                 raise MalformedMapping(depinfo.binding.val, members)
0476 
0477             realtype = 'QMap<%s, %s>' % (bindings[0].val, (bindings[1].val.endswith('>') and bindings[1].val + ' ') or bindings[1].val)
0478             self.decl("""\
0479 /**
0480  * \\struct %s
0481  * \\ingroup mapping
0482 %s\
0483  *
0484  * Mapping type generated from the specification. Convertible with
0485  * %s, but needed to have a discrete type in the Qt type system.
0486 %s\
0487  */
0488 """ % (depinfo.binding.val, get_headerfile_cmd(self.realinclude, self.prettyinclude), realtype, format_docstring(depinfo.el, self.refs)))
0489             self.decl(self.faketype(depinfo.binding.val, realtype))
0490         else:
0491             raise WTF(depinfo.el.localName)
0492 
0493         self.to_declare.append(self.namespace + '::' + depinfo.binding.val)
0494 
0495         if depinfo.binding.array_val:
0496             self.to_declare.append('%s::%s' % (self.namespace, depinfo.binding.array_val))
0497             self.decl("""\
0498 /**
0499  * \\ingroup list
0500 %s\
0501  *
0502  * Array of %s values.
0503  */
0504 typedef %s %s;
0505 
0506 """ % (get_headerfile_cmd(self.realinclude, self.prettyinclude), depinfo.binding.val, 'QList<%s>' % depinfo.binding.val, depinfo.binding.array_val))
0507 
0508         i = depinfo.binding.array_depth
0509         while i > 1:
0510             i -= 1
0511             self.to_declare.append('%s::%s%s' % (self.namespace, depinfo.binding.array_val, ('List' * i)))
0512             list_of = depinfo.binding.array_val + ('List' * (i-1))
0513             self.decl("""\
0514 /**
0515  * \\ingroup list
0516 %s\
0517  *
0518  * Array of %s values.
0519  */
0520 typedef QList<%s> %sList;
0521 
0522 """ % (get_headerfile_cmd(self.realinclude, self.prettyinclude), list_of, list_of, list_of))
0523 
0524     def faketype(self, fake, real):
0525         return """\
0526 struct %(visibility)s %(fake)s : public %(real)s
0527 {
0528     inline %(fake)s() : %(real)s() {}
0529     inline %(fake)s(const %(real)s& a) : %(real)s(a) {}
0530 
0531     inline %(fake)s& operator=(const %(real)s& a)
0532     {
0533         *(static_cast<%(real)s*>(this)) = a;
0534         return *this;
0535     }
0536 };
0537 
0538 """ % {'fake' : fake, 'real' : real, 'visibility': self.visibility}
0539 
0540 if __name__ == '__main__':
0541     options, argv = gnu_getopt(sys.argv[1:], '',
0542             ['declfile=',
0543              'implfile=',
0544              'realinclude=',
0545              'prettyinclude=',
0546              'extraincludes=',
0547              'must-define=',
0548              'namespace=',
0549              'specxml=',
0550              'visibility=',
0551              ])
0552 
0553     try:
0554         Generator(dict(options))()
0555     except BrokenSpecException as e:
0556         print >> sys.stderr, 'Your spec is broken, dear developer! %s' % e
0557         sys.exit(42)