File indexing completed on 2024-05-05 09:16: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)