1#!/usr/bin/env python 2# Copyright (c) 2012 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6""" Generator for C style prototypes and definitions """ 7 8import glob 9import os 10import sys 11 12from idl_log import ErrOut, InfoOut, WarnOut 13from idl_node import IDLNode 14from idl_ast import IDLAst 15from idl_option import GetOption, Option, ParseOptions 16from idl_parser import ParseFiles 17 18Option('cgen_debug', 'Debug generate.') 19 20class CGenError(Exception): 21 def __init__(self, msg): 22 self.value = msg 23 24 def __str__(self): 25 return repr(self.value) 26 27 28def CommentLines(lines, tabs=0): 29 # Generate a C style comment block by prepending the block with '<tab>/*' 30 # and adding a '<tab> *' per line. 31 tab = ' ' * tabs 32 33 out = '%s/*' % tab + ('\n%s *' % tab).join(lines) 34 35 # Add a terminating ' */' unless the last line is blank which would mean it 36 # already has ' *' 37 if not lines[-1]: 38 out += '/\n' 39 else: 40 out += ' */\n' 41 return out 42 43def Comment(node, prefix=None, tabs=0): 44 # Generate a comment block from the provided Comment node. 45 comment = node.GetName() 46 lines = comment.split('\n') 47 48 # If an option prefix is provided, then prepend that to the comment 49 # for this node. 50 if prefix: 51 prefix_lines = prefix.split('\n') 52 # If both the prefix and comment start with a blank line ('*') remove 53 # the extra one. 54 if prefix_lines[0] == '*' and lines[0] == '*': 55 lines = prefix_lines + lines[1:] 56 else: 57 lines = prefix_lines + lines; 58 return CommentLines(lines, tabs) 59 60def GetNodeComments(node, tabs=0): 61 # Generate a comment block joining all comment nodes which are children of 62 # the provided node. 63 comment_txt = '' 64 for doc in node.GetListOf('Comment'): 65 comment_txt += Comment(doc, tabs=tabs) 66 return comment_txt 67 68 69class CGen(object): 70 # TypeMap 71 # 72 # TypeMap modifies how an object is stored or passed, for example pointers 73 # are passed as 'const' if they are 'in' parameters, and structures are 74 # preceeded by the keyword 'struct' as well as using a pointer. 75 # 76 TypeMap = { 77 'Array': { 78 'in': 'const %s', 79 'inout': '%s', 80 'out': '%s*', 81 'store': '%s', 82 'return': '%s', 83 'ref': '%s*' 84 }, 85 'Callspec': { 86 'in': '%s', 87 'inout': '%s', 88 'out': '%s', 89 'store': '%s', 90 'return': '%s' 91 }, 92 'Enum': { 93 'in': '%s', 94 'inout': '%s*', 95 'out': '%s*', 96 'store': '%s', 97 'return': '%s' 98 }, 99 'Interface': { 100 'in': 'const %s*', 101 'inout': '%s*', 102 'out': '%s**', 103 'return': '%s*', 104 'store': '%s*' 105 }, 106 'Struct': { 107 'in': 'const %s*', 108 'inout': '%s*', 109 'out': '%s*', 110 'return': ' %s*', 111 'store': '%s', 112 'ref': '%s*' 113 }, 114 'blob_t': { 115 'in': 'const %s', 116 'inout': '%s', 117 'out': '%s', 118 'return': '%s', 119 'store': '%s' 120 }, 121 'mem_t': { 122 'in': 'const %s', 123 'inout': '%s', 124 'out': '%s', 125 'return': '%s', 126 'store': '%s' 127 }, 128 'mem_ptr_t': { 129 'in': 'const %s', 130 'inout': '%s', 131 'out': '%s', 132 'return': '%s', 133 'store': '%s' 134 }, 135 'str_t': { 136 'in': 'const %s', 137 'inout': '%s', 138 'out': '%s', 139 'return': 'const %s', 140 'store': '%s' 141 }, 142 'cstr_t': { 143 'in': '%s', 144 'inout': '%s*', 145 'out': '%s*', 146 'return': '%s', 147 'store': '%s' 148 }, 149 'TypeValue': { 150 'in': '%s', 151 'constptr_in': 'const %s*', # So we can use const* for PP_Var sometimes. 152 'inout': '%s*', 153 'out': '%s*', 154 'return': '%s', 155 'store': '%s' 156 }, 157 } 158 159 160 # 161 # RemapName 162 # 163 # A diction array of PPAPI types that are converted to language specific 164 # types before being returned by by the C generator 165 # 166 RemapName = { 167 'blob_t': 'void**', 168 'float_t': 'float', 169 'double_t': 'double', 170 'handle_t': 'int', 171 'mem_t': 'void*', 172 'mem_ptr_t': 'void**', 173 'str_t': 'char*', 174 'cstr_t': 'const char*', 175 'interface_t' : 'const void*' 176 } 177 178 # Tell how to handle pointers to GL types. 179 for gltype in ['GLbitfield', 'GLboolean', 'GLbyte', 'GLclampf', 180 'GLclampx', 'GLenum', 'GLfixed', 'GLfloat', 'GLint', 181 'GLintptr', 'GLshort', 'GLsizei', 'GLsizeiptr', 182 'GLubyte', 'GLuint', 'GLushort']: 183 ptrtype = gltype + '_ptr_t' 184 TypeMap[ptrtype] = { 185 'in': 'const %s', 186 'inout': '%s', 187 'out': '%s', 188 'return': 'const %s', 189 'store': '%s' 190 } 191 RemapName[ptrtype] = '%s*' % gltype 192 193 def __init__(self): 194 self.dbg_depth = 0 195 196 # 197 # Debug Logging functions 198 # 199 def Log(self, txt): 200 if not GetOption('cgen_debug'): return 201 tabs = ' ' * self.dbg_depth 202 print '%s%s' % (tabs, txt) 203 204 def LogEnter(self, txt): 205 if txt: self.Log(txt) 206 self.dbg_depth += 1 207 208 def LogExit(self, txt): 209 self.dbg_depth -= 1 210 if txt: self.Log(txt) 211 212 213 def GetDefine(self, name, value): 214 out = '#define %s %s' % (name, value) 215 if len(out) > 80: 216 out = '#define %s \\\n %s' % (name, value) 217 return '%s\n' % out 218 219 # 220 # Interface strings 221 # 222 def GetMacroHelper(self, node): 223 macro = node.GetProperty('macro') 224 if macro: return macro 225 name = node.GetName() 226 name = name.upper() 227 return "%s_INTERFACE" % name 228 229 def GetInterfaceMacro(self, node, version = None): 230 name = self.GetMacroHelper(node) 231 if version is None: 232 return name 233 return '%s_%s' % (name, str(version).replace('.', '_')) 234 235 def GetInterfaceString(self, node, version = None): 236 # If an interface name is specified, use that 237 name = node.GetProperty('iname') 238 if not name: 239 # Otherwise, the interface name is the object's name 240 # With '_Dev' replaced by '(Dev)' if it's a Dev interface. 241 name = node.GetName() 242 if name.endswith('_Dev'): 243 name = '%s(Dev)' % name[:-4] 244 if version is None: 245 return name 246 return "%s;%s" % (name, version) 247 248 249 # 250 # Return the array specification of the object. 251 # 252 def GetArraySpec(self, node): 253 assert(node.cls == 'Array') 254 fixed = node.GetProperty('FIXED') 255 if fixed: 256 return '[%s]' % fixed 257 else: 258 return '[]' 259 260 # 261 # GetTypeName 262 # 263 # For any valid 'typed' object such as Member or Typedef 264 # the typenode object contains the typename 265 # 266 # For a given node return the type name by passing mode. 267 # 268 def GetTypeName(self, node, release, prefix=''): 269 self.LogEnter('GetTypeName of %s rel=%s' % (node, release)) 270 271 # For Members, Params, and Typedefs get the type it refers to otherwise 272 # the node in question is it's own type (struct, union etc...) 273 if node.IsA('Member', 'Param', 'Typedef'): 274 typeref = node.GetType(release) 275 else: 276 typeref = node 277 278 if typeref is None: 279 node.Error('No type at release %s.' % release) 280 raise CGenError('No type for %s' % node) 281 282 # If the type is a (BuiltIn) Type then return it's name 283 # remapping as needed 284 if typeref.IsA('Type'): 285 name = CGen.RemapName.get(typeref.GetName(), None) 286 if name is None: name = typeref.GetName() 287 name = '%s%s' % (prefix, name) 288 289 # For Interfaces, use the name + version 290 elif typeref.IsA('Interface'): 291 rel = typeref.first_release[release] 292 name = 'struct %s%s' % (prefix, self.GetStructName(typeref, rel, True)) 293 294 # For structures, preceed with 'struct' or 'union' as appropriate 295 elif typeref.IsA('Struct'): 296 if typeref.GetProperty('union'): 297 name = 'union %s%s' % (prefix, typeref.GetName()) 298 else: 299 name = 'struct %s%s' % (prefix, typeref.GetName()) 300 301 # If it's an enum, or typedef then return the Enum's name 302 elif typeref.IsA('Enum', 'Typedef'): 303 if not typeref.LastRelease(release): 304 first = node.first_release[release] 305 ver = '_' + node.GetVersion(first).replace('.','_') 306 else: 307 ver = '' 308 # The enum may have skipped having a typedef, we need prefix with 'enum'. 309 if typeref.GetProperty('notypedef'): 310 name = 'enum %s%s%s' % (prefix, typeref.GetName(), ver) 311 else: 312 name = '%s%s%s' % (prefix, typeref.GetName(), ver) 313 314 else: 315 raise RuntimeError('Getting name of non-type %s.' % node) 316 self.LogExit('GetTypeName %s is %s' % (node, name)) 317 return name 318 319 320 # 321 # GetRootType 322 # 323 # For a given node return basic type of that object. This is 324 # either a 'Type', 'Callspec', or 'Array' 325 # 326 def GetRootTypeMode(self, node, release, mode): 327 self.LogEnter('GetRootType of %s' % node) 328 # If it has an array spec, then treat it as an array regardless of type 329 if node.GetOneOf('Array'): 330 rootType = 'Array' 331 # Or if it has a callspec, treat it as a function 332 elif node.GetOneOf('Callspec'): 333 rootType, mode = self.GetRootTypeMode(node.GetType(release), release, 334 'return') 335 336 # If it's a plain typedef, try that object's root type 337 elif node.IsA('Member', 'Param', 'Typedef'): 338 rootType, mode = self.GetRootTypeMode(node.GetType(release), 339 release, mode) 340 341 # If it's an Enum, then it's normal passing rules 342 elif node.IsA('Enum'): 343 rootType = node.cls 344 345 # If it's an Interface or Struct, we may be passing by value 346 elif node.IsA('Interface', 'Struct'): 347 if mode == 'return': 348 if node.GetProperty('returnByValue'): 349 rootType = 'TypeValue' 350 else: 351 rootType = node.cls 352 else: 353 if node.GetProperty('passByValue'): 354 rootType = 'TypeValue' 355 else: 356 rootType = node.cls 357 358 # If it's an Basic Type, check if it's a special type 359 elif node.IsA('Type'): 360 if node.GetName() in CGen.TypeMap: 361 rootType = node.GetName() 362 else: 363 rootType = 'TypeValue' 364 else: 365 raise RuntimeError('Getting root type of non-type %s.' % node) 366 self.LogExit('RootType is "%s"' % rootType) 367 return rootType, mode 368 369 370 def GetTypeByMode(self, node, release, mode): 371 self.LogEnter('GetTypeByMode of %s mode=%s release=%s' % 372 (node, mode, release)) 373 name = self.GetTypeName(node, release) 374 ntype, mode = self.GetRootTypeMode(node, release, mode) 375 out = CGen.TypeMap[ntype][mode] % name 376 self.LogExit('GetTypeByMode %s = %s' % (node, out)) 377 return out 378 379 380 # Get the passing mode of the object (in, out, inout). 381 def GetParamMode(self, node): 382 self.Log('GetParamMode for %s' % node) 383 if node.GetProperty('in'): return 'in' 384 if node.GetProperty('out'): return 'out' 385 if node.GetProperty('inout'): return 'inout' 386 if node.GetProperty('constptr_in'): return 'constptr_in' 387 return 'return' 388 389 # 390 # GetComponents 391 # 392 # Returns the signature components of an object as a tuple of 393 # (rtype, name, arrays, callspec) where: 394 # rtype - The store or return type of the object. 395 # name - The name of the object. 396 # arrays - A list of array dimensions as [] or [<fixed_num>]. 397 # args - None if not a function, otherwise a list of parameters. 398 # 399 def GetComponents(self, node, release, mode): 400 self.LogEnter('GetComponents mode %s for %s %s' % (mode, node, release)) 401 402 # Generate passing type by modifying root type 403 rtype = self.GetTypeByMode(node, release, mode) 404 # If this is an array output, change it from type* foo[] to type** foo. 405 # type* foo[] means an array of pointers to type, which is confusing. 406 arrayspec = [self.GetArraySpec(array) for array in node.GetListOf('Array')] 407 if mode == 'out' and len(arrayspec) == 1 and arrayspec[0] == '[]': 408 rtype += '*' 409 del arrayspec[0] 410 411 if node.IsA('Enum', 'Interface', 'Struct'): 412 rname = node.GetName() 413 else: 414 rname = node.GetType(release).GetName() 415 416 if rname in CGen.RemapName: 417 rname = CGen.RemapName[rname] 418 if '%' in rtype: 419 rtype = rtype % rname 420 name = node.GetName() 421 callnode = node.GetOneOf('Callspec') 422 if callnode: 423 callspec = [] 424 for param in callnode.GetListOf('Param'): 425 if not param.IsRelease(release): 426 continue 427 mode = self.GetParamMode(param) 428 ptype, pname, parray, pspec = self.GetComponents(param, release, mode) 429 callspec.append((ptype, pname, parray, pspec)) 430 else: 431 callspec = None 432 433 self.LogExit('GetComponents: %s, %s, %s, %s' % 434 (rtype, name, arrayspec, callspec)) 435 return (rtype, name, arrayspec, callspec) 436 437 438 def Compose(self, rtype, name, arrayspec, callspec, prefix, func_as_ptr, 439 include_name, unsized_as_ptr): 440 self.LogEnter('Compose: %s %s' % (rtype, name)) 441 arrayspec = ''.join(arrayspec) 442 443 # Switch unsized array to a ptr. NOTE: Only last element can be unsized. 444 if unsized_as_ptr and arrayspec[-2:] == '[]': 445 prefix += '*' 446 arrayspec=arrayspec[:-2] 447 448 if not include_name: 449 name = prefix + arrayspec 450 else: 451 name = prefix + name + arrayspec 452 if callspec is None: 453 out = '%s %s' % (rtype, name) 454 else: 455 params = [] 456 for ptype, pname, parray, pspec in callspec: 457 params.append(self.Compose(ptype, pname, parray, pspec, '', True, 458 include_name=True, 459 unsized_as_ptr=unsized_as_ptr)) 460 if func_as_ptr: 461 name = '(*%s)' % name 462 if not params: 463 params = ['void'] 464 out = '%s %s(%s)' % (rtype, name, ', '.join(params)) 465 self.LogExit('Exit Compose: %s' % out) 466 return out 467 468 # 469 # GetSignature 470 # 471 # Returns the 'C' style signature of the object 472 # prefix - A prefix for the object's name 473 # func_as_ptr - Formats a function as a function pointer 474 # include_name - If true, include member name in the signature. 475 # If false, leave it out. In any case, prefix is always 476 # included. 477 # include_version - if True, include version in the member name 478 # 479 def GetSignature(self, node, release, mode, prefix='', func_as_ptr=True, 480 include_name=True, include_version=False): 481 self.LogEnter('GetSignature %s %s as func=%s' % 482 (node, mode, func_as_ptr)) 483 rtype, name, arrayspec, callspec = self.GetComponents(node, release, mode) 484 if include_version: 485 name = self.GetStructName(node, release, True) 486 487 # If not a callspec (such as a struct) use a ptr instead of [] 488 unsized_as_ptr = not callspec 489 490 out = self.Compose(rtype, name, arrayspec, callspec, prefix, 491 func_as_ptr, include_name, unsized_as_ptr) 492 493 self.LogExit('Exit GetSignature: %s' % out) 494 return out 495 496 # Define a Typedef. 497 def DefineTypedef(self, node, releases, prefix='', comment=False): 498 __pychecker__ = 'unusednames=comment' 499 build_list = node.GetUniqueReleases(releases) 500 501 out = 'typedef %s;\n' % self.GetSignature(node, build_list[-1], 'return', 502 prefix, True, 503 include_version=False) 504 # Version mangle any other versions 505 for index, rel in enumerate(build_list[:-1]): 506 out += '\n' 507 out += 'typedef %s;\n' % self.GetSignature(node, rel, 'return', 508 prefix, True, 509 include_version=True) 510 self.Log('DefineTypedef: %s' % out) 511 return out 512 513 # Define an Enum. 514 def DefineEnum(self, node, releases, prefix='', comment=False): 515 __pychecker__ = 'unusednames=comment,releases' 516 self.LogEnter('DefineEnum %s' % node) 517 name = '%s%s' % (prefix, node.GetName()) 518 notypedef = node.GetProperty('notypedef') 519 unnamed = node.GetProperty('unnamed') 520 521 if unnamed: 522 out = 'enum {' 523 elif notypedef: 524 out = 'enum %s {' % name 525 else: 526 out = 'typedef enum {' 527 enumlist = [] 528 for child in node.GetListOf('EnumItem'): 529 value = child.GetProperty('VALUE') 530 comment_txt = GetNodeComments(child, tabs=1) 531 if value: 532 item_txt = '%s%s = %s' % (prefix, child.GetName(), value) 533 else: 534 item_txt = '%s%s' % (prefix, child.GetName()) 535 enumlist.append('%s %s' % (comment_txt, item_txt)) 536 self.LogExit('Exit DefineEnum') 537 538 if unnamed or notypedef: 539 out = '%s\n%s\n};\n' % (out, ',\n'.join(enumlist)) 540 else: 541 out = '%s\n%s\n} %s;\n' % (out, ',\n'.join(enumlist), name) 542 return out 543 544 def DefineMember(self, node, releases, prefix='', comment=False): 545 __pychecker__ = 'unusednames=prefix,comment' 546 release = releases[0] 547 self.LogEnter('DefineMember %s' % node) 548 if node.GetProperty('ref'): 549 out = '%s;' % self.GetSignature(node, release, 'ref', '', True) 550 else: 551 out = '%s;' % self.GetSignature(node, release, 'store', '', True) 552 self.LogExit('Exit DefineMember') 553 return out 554 555 def GetStructName(self, node, release, include_version=False): 556 suffix = '' 557 if include_version: 558 ver_num = node.GetVersion(release) 559 suffix = ('_%s' % ver_num).replace('.', '_') 560 return node.GetName() + suffix 561 562 def DefineStructInternals(self, node, release, 563 include_version=False, comment=True): 564 channel = node.GetProperty('FILE').release_map.GetChannel(release) 565 if channel == 'dev': 566 channel_comment = ' /* dev */' 567 else: 568 channel_comment = '' 569 out = '' 570 if node.GetProperty('union'): 571 out += 'union %s {%s\n' % ( 572 self.GetStructName(node, release, include_version), channel_comment) 573 else: 574 out += 'struct %s {%s\n' % ( 575 self.GetStructName(node, release, include_version), channel_comment) 576 577 channel = node.GetProperty('FILE').release_map.GetChannel(release) 578 # Generate Member Functions 579 members = [] 580 for child in node.GetListOf('Member'): 581 if channel == 'stable' and child.NodeIsDevOnly(): 582 continue 583 member = self.Define(child, [release], tabs=1, comment=comment) 584 if not member: 585 continue 586 members.append(member) 587 out += '%s\n};\n' % '\n'.join(members) 588 return out 589 590 591 def DefineUnversionedInterface(self, node, rel): 592 out = '\n' 593 if node.GetProperty('force_struct_namespace'): 594 # Duplicate the definition to put it in struct namespace. This 595 # attribute is only for legacy APIs like OpenGLES2 and new APIs 596 # must not use this. See http://crbug.com/411799 597 out += self.DefineStructInternals(node, rel, 598 include_version=False, comment=True) 599 else: 600 # Define an unversioned typedef for the most recent version 601 out += 'typedef struct %s %s;\n' % ( 602 self.GetStructName(node, rel, include_version=True), 603 self.GetStructName(node, rel, include_version=False)) 604 return out 605 606 607 def DefineStruct(self, node, releases, prefix='', comment=False): 608 __pychecker__ = 'unusednames=comment,prefix' 609 self.LogEnter('DefineStruct %s' % node) 610 out = '' 611 build_list = node.GetUniqueReleases(releases) 612 613 newest_stable = None 614 newest_dev = None 615 for rel in build_list: 616 channel = node.GetProperty('FILE').release_map.GetChannel(rel) 617 if channel == 'stable': 618 newest_stable = rel 619 if channel == 'dev': 620 newest_dev = rel 621 last_rel = build_list[-1] 622 623 # TODO(noelallen) : Bug 157017 finish multiversion support 624 if node.IsA('Struct'): 625 if len(build_list) != 1: 626 node.Error('Can not support multiple versions of node.') 627 assert len(build_list) == 1 628 # Build the most recent one versioned, with comments 629 out = self.DefineStructInternals(node, last_rel, 630 include_version=False, comment=True) 631 632 if node.IsA('Interface'): 633 # Build the most recent one versioned, with comments 634 out = self.DefineStructInternals(node, last_rel, 635 include_version=True, comment=True) 636 if last_rel == newest_stable: 637 out += self.DefineUnversionedInterface(node, last_rel) 638 639 # Build the rest without comments and with the version number appended 640 for rel in build_list[0:-1]: 641 channel = node.GetProperty('FILE').release_map.GetChannel(rel) 642 # Skip dev channel interface versions that are 643 # Not the newest version, and 644 # Don't have an equivalent stable version. 645 if channel == 'dev' and rel != newest_dev: 646 if not node.DevInterfaceMatchesStable(rel): 647 continue 648 out += '\n' + self.DefineStructInternals(node, rel, 649 include_version=True, 650 comment=False) 651 if rel == newest_stable: 652 out += self.DefineUnversionedInterface(node, rel) 653 654 self.LogExit('Exit DefineStruct') 655 return out 656 657 658 # 659 # Copyright and Comment 660 # 661 # Generate a comment or copyright block 662 # 663 def Copyright(self, node, cpp_style=False): 664 lines = node.GetName().split('\n') 665 if cpp_style: 666 return '//' + '\n//'.join(filter(lambda f: f != '', lines)) + '\n' 667 return CommentLines(lines) 668 669 670 def Indent(self, data, tabs=0): 671 """Handles indentation and 80-column line wrapping.""" 672 tab = ' ' * tabs 673 lines = [] 674 for line in data.split('\n'): 675 # Add indentation 676 line = tab + line 677 space_break = line.rfind(' ', 0, 80) 678 if len(line) <= 80 or 'http://' in line: 679 # Ignore normal line and URLs permitted by the style guide. 680 lines.append(line.rstrip()) 681 elif not '(' in line and space_break >= 0: 682 # Break long typedefs on nearest space. 683 lines.append(line[0:space_break]) 684 lines.append(' ' + line[space_break + 1:]) 685 else: 686 left = line.rfind('(') + 1 687 args = line[left:].split(',') 688 orig_args = args 689 orig_left = left 690 # Try to split on '(arg1)' or '(arg1, arg2)', not '()' 691 while args[0][0] == ')': 692 left = line.rfind('(', 0, left - 1) + 1 693 if left == 0: # No more parens, take the original option 694 args = orig_args 695 left = orig_left 696 break 697 args = line[left:].split(',') 698 699 line_max = 0 700 for arg in args: 701 if len(arg) > line_max: line_max = len(arg) 702 703 if left + line_max >= 80: 704 indent = '%s ' % tab 705 args = (',\n%s' % indent).join([arg.strip() for arg in args]) 706 lines.append('%s\n%s%s' % (line[:left], indent, args)) 707 else: 708 indent = ' ' * (left - 1) 709 args = (',\n%s' % indent).join(args) 710 lines.append('%s%s' % (line[:left], args)) 711 return '\n'.join(lines) 712 713 714 # Define a top level object. 715 def Define(self, node, releases, tabs=0, prefix='', comment=False): 716 # If this request does not match unique release, or if the release is not 717 # available (possibly deprecated) then skip. 718 unique = node.GetUniqueReleases(releases) 719 if not unique or not node.InReleases(releases): 720 return '' 721 722 self.LogEnter('Define %s tab=%d prefix="%s"' % (node,tabs,prefix)) 723 declmap = dict({ 724 'Enum': CGen.DefineEnum, 725 'Function': CGen.DefineMember, 726 'Interface': CGen.DefineStruct, 727 'Member': CGen.DefineMember, 728 'Struct': CGen.DefineStruct, 729 'Typedef': CGen.DefineTypedef 730 }) 731 732 out = '' 733 func = declmap.get(node.cls, None) 734 if not func: 735 ErrOut.Log('Failed to define %s named %s' % (node.cls, node.GetName())) 736 define_txt = func(self, node, releases, prefix=prefix, comment=comment) 737 738 comment_txt = GetNodeComments(node, tabs=0) 739 if comment_txt and comment: 740 out += comment_txt 741 out += define_txt 742 743 indented_out = self.Indent(out, tabs) 744 self.LogExit('Exit Define') 745 return indented_out 746 747 748# Clean a string representing an object definition and return then string 749# as a single space delimited set of tokens. 750def CleanString(instr): 751 instr = instr.strip() 752 instr = instr.split() 753 return ' '.join(instr) 754 755 756# Test a file, by comparing all it's objects, with their comments. 757def TestFile(filenode): 758 cgen = CGen() 759 760 errors = 0 761 for node in filenode.GetChildren()[2:]: 762 instr = node.GetOneOf('Comment') 763 if not instr: continue 764 instr.Dump() 765 instr = CleanString(instr.GetName()) 766 767 outstr = cgen.Define(node, releases=['M14']) 768 if GetOption('verbose'): 769 print outstr + '\n' 770 outstr = CleanString(outstr) 771 772 if instr != outstr: 773 ErrOut.Log('Failed match of\n>>%s<<\nto:\n>>%s<<\nFor:\n' % 774 (instr, outstr)) 775 node.Dump(1, comments=True) 776 errors += 1 777 return errors 778 779 780# Build and resolve the AST and compare each file individual. 781def TestFiles(filenames): 782 if not filenames: 783 idldir = os.path.split(sys.argv[0])[0] 784 idldir = os.path.join(idldir, 'test_cgen', '*.idl') 785 filenames = glob.glob(idldir) 786 787 filenames = sorted(filenames) 788 ast = ParseFiles(filenames) 789 790 total_errs = 0 791 for filenode in ast.GetListOf('File'): 792 errs = TestFile(filenode) 793 if errs: 794 ErrOut.Log('%s test failed with %d error(s).' % 795 (filenode.GetName(), errs)) 796 total_errs += errs 797 798 if total_errs: 799 ErrOut.Log('Failed generator test.') 800 else: 801 InfoOut.Log('Passed generator test.') 802 return total_errs 803 804def main(args): 805 filenames = ParseOptions(args) 806 if GetOption('test'): 807 return TestFiles(filenames) 808 ast = ParseFiles(filenames) 809 cgen = CGen() 810 for f in ast.GetListOf('File'): 811 if f.GetProperty('ERRORS') > 0: 812 print 'Skipping %s' % f.GetName() 813 continue 814 for node in f.GetChildren()[2:]: 815 print cgen.Define(node, ast.releases, comment=True, prefix='tst_') 816 817 818if __name__ == '__main__': 819 sys.exit(main(sys.argv[1:])) 820