idl_c_proto.py revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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      'inout': '%s*',
152      'out': '%s*',
153      'return': '%s',
154      'store': '%s'
155    },
156  }
157
158
159  #
160  # RemapName
161  #
162  # A diction array of PPAPI types that are converted to language specific
163  # types before being returned by by the C generator
164  #
165  RemapName = {
166  'blob_t': 'void**',
167  'float_t': 'float',
168  'double_t': 'double',
169  'handle_t': 'int',
170  'mem_t': 'void*',
171  'mem_ptr_t': 'void**',
172  'str_t': 'char*',
173  'cstr_t': 'const char*',
174  'interface_t' : 'const void*'
175  }
176
177  def __init__(self):
178    self.dbg_depth = 0
179
180  #
181  # Debug Logging functions
182  #
183  def Log(self, txt):
184    if not GetOption('cgen_debug'): return
185    tabs = '  ' * self.dbg_depth
186    print '%s%s' % (tabs, txt)
187
188  def LogEnter(self, txt):
189    if txt: self.Log(txt)
190    self.dbg_depth += 1
191
192  def LogExit(self, txt):
193    self.dbg_depth -= 1
194    if txt: self.Log(txt)
195
196
197  def GetDefine(self, name, value):
198    out = '#define %s %s' % (name, value)
199    if len(out) > 80:
200      out = '#define %s \\\n    %s' % (name, value)
201    return '%s\n' % out
202
203  #
204  # Interface strings
205  #
206  def GetMacroHelper(self, node):
207    macro = node.GetProperty('macro')
208    if macro: return macro
209    name = node.GetName()
210    name = name.upper()
211    return "%s_INTERFACE" % name
212
213  def GetInterfaceMacro(self, node, version = None):
214    name = self.GetMacroHelper(node)
215    if version is None:
216      return name
217    return '%s_%s' % (name, str(version).replace('.', '_'))
218
219  def GetInterfaceString(self, node, version = None):
220    # If an interface name is specified, use that
221    name = node.GetProperty('iname')
222    if not name:
223      # Otherwise, the interface name is the object's name
224      # With '_Dev' replaced by '(Dev)' if it's a Dev interface.
225      name = node.GetName()
226      if name.endswith('_Dev'):
227        name = '%s(Dev)' % name[:-4]
228    if version is None:
229      return name
230    return "%s;%s" % (name, version)
231
232
233  #
234  # Return the array specification of the object.
235  #
236  def GetArraySpec(self, node):
237    assert(node.cls == 'Array')
238    fixed = node.GetProperty('FIXED')
239    if fixed:
240      return '[%s]' % fixed
241    else:
242      return '[]'
243
244  #
245  # GetTypeName
246  #
247  # For any valid 'typed' object such as Member or Typedef
248  # the typenode object contains the typename
249  #
250  # For a given node return the type name by passing mode.
251  #
252  def GetTypeName(self, node, release, prefix=''):
253    self.LogEnter('GetTypeName of %s rel=%s' % (node, release))
254
255    # For Members, Params, and Typedefs get the type it refers to otherwise
256    # the node in question is it's own type (struct, union etc...)
257    if node.IsA('Member', 'Param', 'Typedef'):
258      typeref = node.GetType(release)
259    else:
260      typeref = node
261
262    if typeref is None:
263      node.Error('No type at release %s.' % release)
264      raise CGenError('No type for %s' % node)
265
266    # If the type is a (BuiltIn) Type then return it's name
267    # remapping as needed
268    if typeref.IsA('Type'):
269      name = CGen.RemapName.get(typeref.GetName(), None)
270      if name is None: name = typeref.GetName()
271      name = '%s%s' % (prefix, name)
272
273    # For Interfaces, use the name + version
274    elif typeref.IsA('Interface'):
275      rel = typeref.first_release[release]
276      name = 'struct %s%s' % (prefix, self.GetStructName(typeref, rel, True))
277
278    # For structures, preceed with 'struct' or 'union' as appropriate
279    elif typeref.IsA('Struct'):
280      if typeref.GetProperty('union'):
281        name = 'union %s%s' % (prefix, typeref.GetName())
282      else:
283        name = 'struct %s%s' % (prefix, typeref.GetName())
284
285    # If it's an enum, or typedef then return the Enum's name
286    elif typeref.IsA('Enum', 'Typedef'):
287      if not typeref.LastRelease(release):
288        first = node.first_release[release]
289        ver = '_' + node.GetVersion(first).replace('.','_')
290      else:
291        ver = ''
292      # The enum may have skipped having a typedef, we need prefix with 'enum'.
293      if typeref.GetProperty('notypedef'):
294        name = 'enum %s%s%s' % (prefix, typeref.GetName(), ver)
295      else:
296        name = '%s%s%s' % (prefix, typeref.GetName(), ver)
297
298    else:
299      raise RuntimeError('Getting name of non-type %s.' % node)
300    self.LogExit('GetTypeName %s is %s' % (node, name))
301    return name
302
303
304  #
305  # GetRootType
306  #
307  # For a given node return basic type of that object.  This is
308  # either a 'Type', 'Callspec', or 'Array'
309  #
310  def GetRootTypeMode(self, node, release, mode):
311    self.LogEnter('GetRootType of %s' % node)
312    # If it has an array spec, then treat it as an array regardless of type
313    if node.GetOneOf('Array'):
314      rootType = 'Array'
315    # Or if it has a callspec, treat it as a function
316    elif node.GetOneOf('Callspec'):
317      rootType, mode = self.GetRootTypeMode(node.GetType(release), release,
318                                            'return')
319
320    # If it's a plain typedef, try that object's root type
321    elif node.IsA('Member', 'Param', 'Typedef'):
322      rootType, mode = self.GetRootTypeMode(node.GetType(release),
323                                            release, mode)
324
325    # If it's an Enum, then it's normal passing rules
326    elif node.IsA('Enum'):
327      rootType = node.cls
328
329    # If it's an Interface or Struct, we may be passing by value
330    elif node.IsA('Interface', 'Struct'):
331      if mode == 'return':
332        if node.GetProperty('returnByValue'):
333          rootType = 'TypeValue'
334        else:
335          rootType = node.cls
336      else:
337        if node.GetProperty('passByValue'):
338          rootType = 'TypeValue'
339        else:
340          rootType = node.cls
341
342    # If it's an Basic Type, check if it's a special type
343    elif node.IsA('Type'):
344      if node.GetName() in CGen.TypeMap:
345        rootType = node.GetName()
346      else:
347        rootType = 'TypeValue'
348    else:
349      raise RuntimeError('Getting root type of non-type %s.' % node)
350    self.LogExit('RootType is "%s"' % rootType)
351    return rootType, mode
352
353
354  def GetTypeByMode(self, node, release, mode):
355    self.LogEnter('GetTypeByMode of %s mode=%s release=%s' %
356                  (node, mode, release))
357    name = self.GetTypeName(node, release)
358    ntype, mode = self.GetRootTypeMode(node, release, mode)
359    out = CGen.TypeMap[ntype][mode] % name
360    self.LogExit('GetTypeByMode %s = %s' % (node, out))
361    return out
362
363
364  # Get the passing mode of the object (in, out, inout).
365  def GetParamMode(self, node):
366    self.Log('GetParamMode for %s' % node)
367    if node.GetProperty('in'): return 'in'
368    if node.GetProperty('out'): return 'out'
369    if node.GetProperty('inout'): return 'inout'
370    return 'return'
371
372  #
373  # GetComponents
374  #
375  # Returns the signature components of an object as a tuple of
376  # (rtype, name, arrays, callspec) where:
377  #   rtype - The store or return type of the object.
378  #   name - The name of the object.
379  #   arrays - A list of array dimensions as [] or [<fixed_num>].
380  #   args -  None if not a function, otherwise a list of parameters.
381  #
382  def GetComponents(self, node, release, mode):
383    self.LogEnter('GetComponents mode %s for %s %s' % (mode, node, release))
384
385    # Generate passing type by modifying root type
386    rtype = self.GetTypeByMode(node, release, mode)
387    if node.IsA('Enum', 'Interface', 'Struct'):
388      rname = node.GetName()
389    else:
390      rname = node.GetType(release).GetName()
391
392    if rname in CGen.RemapName:
393      rname = CGen.RemapName[rname]
394    if '%' in rtype:
395      rtype = rtype % rname
396    name = node.GetName()
397    arrayspec = [self.GetArraySpec(array) for array in node.GetListOf('Array')]
398    callnode = node.GetOneOf('Callspec')
399    if callnode:
400      callspec = []
401      for param in callnode.GetListOf('Param'):
402        if not param.IsRelease(release):
403          continue
404        mode = self.GetParamMode(param)
405        ptype, pname, parray, pspec = self.GetComponents(param, release, mode)
406        callspec.append((ptype, pname, parray, pspec))
407    else:
408      callspec = None
409
410    self.LogExit('GetComponents: %s, %s, %s, %s' %
411                 (rtype, name, arrayspec, callspec))
412    return (rtype, name, arrayspec, callspec)
413
414
415  def Compose(self, rtype, name, arrayspec, callspec, prefix, func_as_ptr,
416              include_name, unsized_as_ptr):
417    self.LogEnter('Compose: %s %s' % (rtype, name))
418    arrayspec = ''.join(arrayspec)
419
420    # Switch unsized array to a ptr. NOTE: Only last element can be unsized.
421    if unsized_as_ptr and arrayspec[-2:] == '[]':
422      prefix +=  '*'
423      arrayspec=arrayspec[:-2]
424
425    if not include_name:
426      name = prefix + arrayspec
427    else:
428      name = prefix + name + arrayspec
429    if callspec is None:
430      out = '%s %s' % (rtype, name)
431    else:
432      params = []
433      for ptype, pname, parray, pspec in callspec:
434        params.append(self.Compose(ptype, pname, parray, pspec, '', True,
435                                   include_name=True,
436                                   unsized_as_ptr=unsized_as_ptr))
437      if func_as_ptr:
438        name = '(*%s)' % name
439      if not params:
440        params = ['void']
441      out = '%s %s(%s)' % (rtype, name, ', '.join(params))
442    self.LogExit('Exit Compose: %s' % out)
443    return out
444
445  #
446  # GetSignature
447  #
448  # Returns the 'C' style signature of the object
449  #  prefix - A prefix for the object's name
450  #  func_as_ptr - Formats a function as a function pointer
451  #  include_name - If true, include member name in the signature.
452  #                 If false, leave it out. In any case, prefix is always
453  #                 included.
454  #  include_version - if True, include version in the member name
455  #
456  def GetSignature(self, node, release, mode, prefix='', func_as_ptr=True,
457                   include_name=True, include_version=False):
458    self.LogEnter('GetSignature %s %s as func=%s' %
459                  (node, mode, func_as_ptr))
460    rtype, name, arrayspec, callspec = self.GetComponents(node, release, mode)
461    if include_version:
462      name = self.GetStructName(node, release, True)
463
464    # If not a callspec (such as a struct) use a ptr instead of []
465    unsized_as_ptr = not callspec
466
467    out = self.Compose(rtype, name, arrayspec, callspec, prefix,
468                       func_as_ptr, include_name, unsized_as_ptr)
469
470    self.LogExit('Exit GetSignature: %s' % out)
471    return out
472
473  # Define a Typedef.
474  def DefineTypedef(self, node, releases, prefix='', comment=False):
475    __pychecker__ = 'unusednames=comment'
476    build_list = node.GetUniqueReleases(releases)
477
478    out = 'typedef %s;\n' % self.GetSignature(node, build_list[-1], 'return',
479                                              prefix, True,
480                                              include_version=False)
481    # Version mangle any other versions
482    for index, rel in enumerate(build_list[:-1]):
483      out += '\n'
484      out += 'typedef %s;\n' % self.GetSignature(node, rel, 'return',
485                                                 prefix, True,
486                                                 include_version=True)
487    self.Log('DefineTypedef: %s' % out)
488    return out
489
490  # Define an Enum.
491  def DefineEnum(self, node, releases, prefix='', comment=False):
492    __pychecker__ = 'unusednames=comment,releases'
493    self.LogEnter('DefineEnum %s' % node)
494    name = '%s%s' % (prefix, node.GetName())
495    notypedef = node.GetProperty('notypedef')
496    unnamed = node.GetProperty('unnamed')
497
498    if unnamed:
499      out = 'enum {'
500    elif notypedef:
501      out = 'enum %s {' % name
502    else:
503      out = 'typedef enum {'
504    enumlist = []
505    for child in node.GetListOf('EnumItem'):
506      value = child.GetProperty('VALUE')
507      comment_txt = GetNodeComments(child, tabs=1)
508      if value:
509        item_txt = '%s%s = %s' % (prefix, child.GetName(), value)
510      else:
511        item_txt = '%s%s' % (prefix, child.GetName())
512      enumlist.append('%s  %s' % (comment_txt, item_txt))
513    self.LogExit('Exit DefineEnum')
514
515    if unnamed or notypedef:
516      out = '%s\n%s\n};\n' % (out, ',\n'.join(enumlist))
517    else:
518      out = '%s\n%s\n} %s;\n' % (out, ',\n'.join(enumlist), name)
519    return out
520
521  def DefineMember(self, node, releases, prefix='', comment=False):
522    __pychecker__ = 'unusednames=prefix,comment'
523    release = releases[0]
524    self.LogEnter('DefineMember %s' % node)
525    if node.GetProperty('ref'):
526      out = '%s;' % self.GetSignature(node, release, 'ref', '', True)
527    else:
528      out = '%s;' % self.GetSignature(node, release, 'store', '', True)
529    self.LogExit('Exit DefineMember')
530    return out
531
532  def GetStructName(self, node, release, include_version=False):
533    suffix = ''
534    if include_version:
535      ver_num = node.GetVersion(release)
536      suffix = ('_%s' % ver_num).replace('.', '_')
537    return node.GetName() + suffix
538
539  def DefineStructInternals(self, node, release,
540                            include_version=False, comment=True):
541    channel = node.GetProperty('FILE').release_map.GetChannel(release)
542    if channel == 'dev':
543      channel_comment = ' /* dev */'
544    else:
545      channel_comment = ''
546    out = ''
547    if node.GetProperty('union'):
548      out += 'union %s {%s\n' % (
549          self.GetStructName(node, release, include_version), channel_comment)
550    else:
551      out += 'struct %s {%s\n' % (
552          self.GetStructName(node, release, include_version), channel_comment)
553
554    channel = node.GetProperty('FILE').release_map.GetChannel(release)
555    # Generate Member Functions
556    members = []
557    for child in node.GetListOf('Member'):
558      if channel == 'stable' and child.NodeIsDevOnly():
559        continue
560      member = self.Define(child, [release], tabs=1, comment=comment)
561      if not member:
562        continue
563      members.append(member)
564    out += '%s\n};\n' % '\n'.join(members)
565    return out
566
567
568  def DefineStruct(self, node, releases, prefix='', comment=False):
569    __pychecker__ = 'unusednames=comment,prefix'
570    self.LogEnter('DefineStruct %s' % node)
571    out = ''
572    build_list = node.GetUniqueReleases(releases)
573
574    newest_stable = None
575    newest_dev = None
576    for rel in build_list:
577      channel = node.GetProperty('FILE').release_map.GetChannel(rel)
578      if channel == 'stable':
579        newest_stable = rel
580      if channel == 'dev':
581        newest_dev = rel
582    last_rel = build_list[-1]
583
584    # TODO(noelallen) : Bug 157017 finish multiversion support
585    if node.IsA('Struct'):
586      if len(build_list) != 1:
587        node.Error('Can not support multiple versions of node.')
588      assert len(build_list) == 1
589      # Build the most recent one versioned, with comments
590      out = self.DefineStructInternals(node, last_rel,
591                                       include_version=False, comment=True)
592
593    if node.IsA('Interface'):
594      # Build the most recent one versioned, with comments
595      out = self.DefineStructInternals(node, last_rel,
596                                       include_version=True, comment=True)
597      if last_rel == newest_stable:
598        # Define an unversioned typedef for the most recent version
599        out += '\ntypedef struct %s %s;\n' % (
600            self.GetStructName(node, last_rel, include_version=True),
601            self.GetStructName(node, last_rel, include_version=False))
602
603      # Build the rest without comments and with the version number appended
604      for rel in build_list[0:-1]:
605        channel = node.GetProperty('FILE').release_map.GetChannel(rel)
606        # Skip dev channel interface versions that are
607        #   Not the newest version, and
608        #   Don't have an equivalent stable version.
609        if channel == 'dev' and rel != newest_dev:
610          if not node.DevInterfaceMatchesStable(rel):
611            continue
612        out += '\n' + self.DefineStructInternals(node, rel,
613                                                 include_version=True,
614                                                 comment=False)
615        if rel == newest_stable:
616          # Define an unversioned typedef for the most recent version
617          out += '\ntypedef struct %s %s;\n' % (
618              self.GetStructName(node, rel, include_version=True),
619              self.GetStructName(node, rel, include_version=False))
620
621    self.LogExit('Exit DefineStruct')
622    return out
623
624
625  #
626  # Copyright and Comment
627  #
628  # Generate a comment or copyright block
629  #
630  def Copyright(self, node, cpp_style=False):
631    lines = node.GetName().split('\n')
632    if cpp_style:
633      return '//' + '\n//'.join(filter(lambda f: f != '', lines)) + '\n'
634    return CommentLines(lines)
635
636
637  def Indent(self, data, tabs=0):
638    """Handles indentation and 80-column line wrapping."""
639    tab = '  ' * tabs
640    lines = []
641    for line in data.split('\n'):
642      # Add indentation
643      line = tab + line
644      space_break = line.rfind(' ', 0, 80)
645      if len(line) <= 80 or 'http' in line:
646        # Ignore normal line and URLs permitted by the style guide.
647        lines.append(line.rstrip())
648      elif not '(' in line and space_break >= 0:
649        # Break long typedefs on nearest space.
650        lines.append(line[0:space_break])
651        lines.append('    ' + line[space_break + 1:])
652      else:
653        left = line.rfind('(') + 1
654        args = line[left:].split(',')
655        orig_args = args
656        orig_left = left
657        # Try to split on '(arg1)' or '(arg1, arg2)', not '()'
658        while args[0][0] == ')':
659          left = line.rfind('(', 0, left - 1) + 1
660          if left == 0:  # No more parens, take the original option
661            args = orig_args
662            left = orig_left
663            break
664          args = line[left:].split(',')
665
666        line_max = 0
667        for arg in args:
668          if len(arg) > line_max: line_max = len(arg)
669
670        if left + line_max >= 80:
671          indent = '%s    ' % tab
672          args =  (',\n%s' % indent).join([arg.strip() for arg in args])
673          lines.append('%s\n%s%s' % (line[:left], indent, args))
674        else:
675          indent = ' ' * (left - 1)
676          args =  (',\n%s' % indent).join(args)
677          lines.append('%s%s' % (line[:left], args))
678    return '\n'.join(lines)
679
680
681  # Define a top level object.
682  def Define(self, node, releases, tabs=0, prefix='', comment=False):
683    # If this request does not match unique release, or if the release is not
684    # available (possibly deprecated) then skip.
685    unique = node.GetUniqueReleases(releases)
686    if not unique or not node.InReleases(releases):
687      return ''
688
689    self.LogEnter('Define %s tab=%d prefix="%s"' % (node,tabs,prefix))
690    declmap = dict({
691      'Enum': CGen.DefineEnum,
692      'Function': CGen.DefineMember,
693      'Interface': CGen.DefineStruct,
694      'Member': CGen.DefineMember,
695      'Struct': CGen.DefineStruct,
696      'Typedef': CGen.DefineTypedef
697    })
698
699    out = ''
700    func = declmap.get(node.cls, None)
701    if not func:
702      ErrOut.Log('Failed to define %s named %s' % (node.cls, node.GetName()))
703    define_txt = func(self, node, releases, prefix=prefix, comment=comment)
704
705    comment_txt = GetNodeComments(node, tabs=0)
706    if comment_txt and comment:
707      out += comment_txt
708    out += define_txt
709
710    indented_out = self.Indent(out, tabs)
711    self.LogExit('Exit Define')
712    return indented_out
713
714
715# Clean a string representing an object definition and return then string
716# as a single space delimited set of tokens.
717def CleanString(instr):
718  instr = instr.strip()
719  instr = instr.split()
720  return ' '.join(instr)
721
722
723# Test a file, by comparing all it's objects, with their comments.
724def TestFile(filenode):
725  cgen = CGen()
726
727  errors = 0
728  for node in filenode.GetChildren()[2:]:
729    instr = node.GetOneOf('Comment')
730    if not instr: continue
731    instr.Dump()
732    instr = CleanString(instr.GetName())
733
734    outstr = cgen.Define(node, releases=['M14'])
735    if GetOption('verbose'):
736      print outstr + '\n'
737    outstr = CleanString(outstr)
738
739    if instr != outstr:
740      ErrOut.Log('Failed match of\n>>%s<<\nto:\n>>%s<<\nFor:\n' %
741                 (instr, outstr))
742      node.Dump(1, comments=True)
743      errors += 1
744  return errors
745
746
747# Build and resolve the AST and compare each file individual.
748def TestFiles(filenames):
749  if not filenames:
750    idldir = os.path.split(sys.argv[0])[0]
751    idldir = os.path.join(idldir, 'test_cgen', '*.idl')
752    filenames = glob.glob(idldir)
753
754  filenames = sorted(filenames)
755  ast = ParseFiles(filenames)
756
757  total_errs = 0
758  for filenode in ast.GetListOf('File'):
759    errs = TestFile(filenode)
760    if errs:
761      ErrOut.Log('%s test failed with %d error(s).' %
762                 (filenode.GetName(), errs))
763      total_errs += errs
764
765  if total_errs:
766    ErrOut.Log('Failed generator test.')
767  else:
768    InfoOut.Log('Passed generator test.')
769  return total_errs
770
771def main(args):
772  filenames = ParseOptions(args)
773  if GetOption('test'):
774    return TestFiles(filenames)
775  ast = ParseFiles(filenames)
776  cgen = CGen()
777  for f in ast.GetListOf('File'):
778    if f.GetProperty('ERRORS') > 0:
779      print 'Skipping %s' % f.GetName()
780      continue
781    for node in f.GetChildren()[2:]:
782      print cgen.Define(node, ast.releases, comment=True, prefix='tst_')
783
784
785if __name__ == '__main__':
786  sys.exit(main(sys.argv[1:]))
787
788