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)