Update the generator

Outstanding changes to the generator to:
  Support hashing
  Support versions
  Clean up 'C' header generation
  Update of 'C' header generation tests
  Fix line wrapping
  Pass error counts through to File objects


BUG= http://code.google.com/p/chromium/issues/detail?id=84272
TEST= python idl_c_header.py & gcl try
Generate the new headers, add them to a CL, and try

TBR= [email protected]
Review URL: http://codereview.chromium.org/7408002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92930 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/ppapi/generators/idl_ast.py b/ppapi/generators/idl_ast.py
index 71aaf93..af06de3 100644
--- a/ppapi/generators/idl_ast.py
+++ b/ppapi/generators/idl_ast.py
@@ -109,12 +109,23 @@
   def __init__(self, children):
     objs = []
 
+    builtin = None
+    extranodes = []
+    for filenode in children:
+      if filenode.GetProperty('NAME') == 'pp_stdint.idl':
+        builtin = filenode
+        break
+
+    if not builtin:
+      builtin = IDLFile('pp_stdint.idl', [])
+      extranodes = [builtin]
+
     for name in BuiltIn:
       nameattr = IDLAttribute('NAME', name)
-      objs.append(IDLNode('Type', 'BuiltIn', 1, 0, [nameattr]))
+      typenode = IDLNode('Type', 'BuiltIn', 1, 0, [nameattr])
+      builtin.AddChild(typenode)
 
-    builtin = IDLFile('pp_stdint.idl', objs)
-    IDLNode.__init__(self, 'AST', 'BuiltIn', 1, 0, [builtin] + children)
+    IDLNode.__init__(self, 'AST', 'BuiltIn', 1, 0, extranodes + children)
     self.SetProperty('LABEL', IDLVersionMapDefault())
     self.Resolve()
 
diff --git a/ppapi/generators/idl_c_header.py b/ppapi/generators/idl_c_header.py
index 39ced4a..feef088 100644
--- a/ppapi/generators/idl_c_header.py
+++ b/ppapi/generators/idl_c_header.py
@@ -9,41 +9,261 @@
 import glob
 import os
 import sys
+import subprocess
 
 from idl_log import ErrOut, InfoOut, WarnOut
-from idl_node import IDLAttribute, IDLAst, IDLNode
+from idl_node import IDLAttribute, IDLNode
+from idl_ast import IDLAst
 from idl_option import GetOption, Option, ParseOptions
 from idl_outfile import IDLOutFile
 from idl_parser import ParseFiles
-from idl_c_proto import *
+from idl_c_proto import CGen
 
-Option('hdir', 'Output directory', default='hdir')
+Option('dstroot', 'Base directory of output', default='../c')
+Option('guard', 'Include guard prefix', default='ppapi/c')
+Option('out', 'List of output files', default='')
+
+cgen = CGen()
+
+def IDLToHeader(filenode, relpath=None, prefix=None):
+  path, name = os.path.split(filenode.GetProperty('NAME'))
+  name = os.path.splitext(name)[0] + '.h'
+  if prefix: name = '%s%s' % (prefix, name)
+  if path: name = os.path.join(path, name)
+  if relpath: name = os.path.join(relpath, name)
+  return name
+
+
+def GenerateHeader(filenode, pref, inline=True):
+  name = filenode.GetProperty('NAME')
+  if name == 'pp_stdint.idl': return
+
+  # If we have an 'out' filter list, then check if we should output this file.
+  outlist = GetOption('out')
+  if outlist:
+    outlist = outlist.split(',')
+    if name not in outlist:
+      return
+
+  savename = IDLToHeader(filenode, relpath=GetOption('dstroot'), prefix=pref)
+  out = IDLOutFile(savename)
+
+  gpath = GetOption('guard')
+  def_guard = IDLToHeader(filenode, relpath=gpath, prefix=pref)
+  def_guard = def_guard.replace('/','_').replace('.','_').upper() + '_'
+  copyright = filenode.GetChildren()[0]
+  assert(copyright.IsA('Copyright'))
+
+  fileinfo = filenode.GetChildren()[1]
+  assert(fileinfo.IsA('Comment'))
+
+  out.Write('%s\n' % cgen.Copyright(copyright))
+  out.Write('/* From %s modified %s. */\n\n'% (
+      filenode.GetProperty('NAME'), filenode.GetProperty('DATETIME')))
+  out.Write('#ifndef %s\n#define %s\n\n' % (def_guard, def_guard))
+
+  for label in filenode.GetListOf('Label'):
+    if label.GetName() == GetOption('label'):
+      cgen.SetVersionMap(label)
+
+  deps = filenode.GetDeps('M14')
+  # Generate set of includes
+  includes = set([])
+  for dep in deps:
+    depfile = dep.GetProperty('FILE')
+    if depfile:
+      includes.add(depfile)
+  includes = [IDLToHeader(include, relpath=gpath) for include in includes]
+  includes.append('ppapi/c/pp_macros.h')
+
+  # Assume we need stdint if we "include" C or C++ code
+  if filenode.GetListOf('Include'):
+    includes.append('ppapi/c/pp_stdint.h')
+
+  includes = sorted(set(includes))
+  cur_include = IDLToHeader(filenode, relpath=gpath)
+  for include in includes:
+    if include == cur_include: continue
+    out.Write('#include "%s"\n' % include)
+
+  # Generate the @file comment
+  out.Write('\n%s\n' % cgen.Comment(fileinfo, prefix='*\n @file'))
+
+  # Generate definitions.
+  last_group = None
+  for node in filenode.GetChildren()[2:]:
+    # If we are part of a group comment marker...
+    if last_group and last_group != node.cls:
+      pre = cgen.CommentLines(['*',' @}', '']) + '\n'
+    else:
+      pre = '\n'
+
+    if node.cls in ['Typedef', 'Interface', 'Struct', 'Enum']:
+      if last_group != node.cls:
+        pre += cgen.CommentLines(['*',' @addtogroup %ss' % node.cls, ' @{', ''])
+      last_group = node.cls
+    else:
+      last_group = None
+
+    if node.IsA('Comment'):
+      item = '%s\n\n' % cgen.Comment(node)
+    elif node.IsA('Inline'):
+      if not inline: continue
+      if node.GetName() == 'cc':
+        item = cgen.Define(node, prefix=pref, comment=True)
+        item = '#ifdef __cplusplus\n%s\n#endif  // __cplusplus\n\n' % item
+      elif node.GetName() == 'c':
+        item = cgen.Define(node, prefix=pref, comment=True)
+      else:
+        continue
+      if not item: continue
+    else:
+      #
+      # Otherwise we are defining a file level object, so generate the
+      # correct document notation.
+      #
+      item = cgen.Define(node, prefix=pref, comment=True)
+      if not item: continue
+      asize = node.GetProperty('assert_size()')
+      if asize:
+        name = '%s%s' % (pref, node.GetName())
+        if node.IsA('Struct'):
+          form = 'PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(%s, %s);\n'
+        else:
+          form = 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n'
+        item += form % (name, asize[0])
+
+    if item: out.Write('%s%s' % (pre, item))
+  if last_group:
+    out.Write(cgen.CommentLines(['*',' @}', '']) + '\n')
+
+  out.Write('#endif  /* %s */\n\n' % def_guard)
+  out.Close()
+
+
+def GenerateFileTest(cfile, filenode, pref):
+  tests = []
+  original = IDLToHeader(filenode, relpath='')
+  savename = IDLToHeader(filenode, relpath='', prefix=pref)
+  cfile.Write('/* Test %s */\n' % filenode.GetProperty('NAME'))
+  cfile.Write('#include "%s"\n' % original)
+  cfile.Write('#include "%s"\n' % savename)
+
+  # For all children (excluding copyright notice)
+  for node in filenode.children[1:]:
+    # Test enums by assigning all enum items to themselves. Unfortunately this
+    # will not catch cases where the '.h' has enums that the IDL does not.
+    if node.IsA('Enum'):
+      tests.append('test_%s' % node.GetProperty('NAME'))
+      cfile.Write('int test_%s() {\n' % node.GetProperty('NAME'))
+      cfile.Write('  int errors = 0;\n')
+      for enum in node.GetListOf('EnumItem'):
+        cfile.Write('  errors += VERIFY_ENUM(%s, %s%s);\n' %
+            (enum.GetProperty('NAME'), pref, enum.GetProperty('NAME')))
+      cfile.Write('  if (errors) printf("Failure in %s:%s.\\n");\n' %
+          (filenode.GetName(), node.GetName()))
+      cfile.Write('  return errors;\n}\n\n')
+      continue
+
+    # Use a structure asignment to verify equivilency
+    if node.IsA('Interface', 'Struct'):
+      tests.append('test_%s' % node.GetName())
+      rtype1 = cgen.GetTypeName(node)
+      rtype2 = cgen.GetTypeName(node, prefix='tst_')
+      cfile.Write('int test_%s() {\n' % node.GetName())
+      cfile.Write('  int errors = 0;\n');
+      cmp_structs = '  %s A;\n  %s B;\n' % (rtype1, rtype2)
+      cfile.Write(cmp_structs)
+      cfile.Write('  memset(&A, 0, sizeof(A));\n')
+      cfile.Write('  memset(&B, 1, sizeof(B));\n')
+      for member in node.GetListOf('Member', 'Function'):
+        if member.GetOneOf('Array'):
+          cfile.Write('  memcpy(A.%s, B.%s, sizeof(A.%s));\n' %
+                      (member.GetName(), member.GetName(), member.GetName()))
+        else:
+          cfile.Write('  A.%s = B.%s;\n' % (member.GetName(), member.GetName()))
+      cfile.Write('  errors += (sizeof(A) != sizeof(B)) ? 1 : 0;\n')
+      cfile.Write('  errors += (memcmp(&A, &B, sizeof(A))) ? 1 : 0;\n')
+      cfile.Write('  return (sizeof(A) == sizeof(B));\n}\n')
+      continue
+
+  cfile.Write('\n\n')
+  return tests
+
+def GenerateBaseTest(cfile):
+  for inc in ['<stdio.h>','<string.h>','"pp_stdint.h"']:
+    cfile.Write('#include %s\n' % inc)
+  cfile.Write('\n')
+  cfile.Write('#define VERIFY_ENUM(orig, idl) Check(#orig, orig, idl)\n\n')
+  cfile.Write('int Check(const char *name, int v1, int v2) {\n')
+  cfile.Write('  if (v1 == v2) return 0;\n')
+  cfile.Write('  printf("Mismatch enum %s : %d vs %d\\n", name, v1, v2);\n')
+  cfile.Write('  return 1;\n}\n\n')
+
 
 def Main(args):
   filenames = ParseOptions(args)
-  ast_result = ParseFiles(filenames)
-  ast = ast_result.out
+  ast = ParseFiles(filenames)
+  testFuncs = []
+  skipList = []
 
-  ast.Dump()
+  outdir = GetOption('dstroot')
+
+  if GetOption('test'):
+    prefix = 'tst_'
+    cfile = IDLOutFile(os.path.join(outdir, 'test.c'))
+    GenerateBaseTest(cfile)
+  else:
+    prefix = ''
+    cfile = None
+
   for filenode in ast.GetListOf('File'):
-    savename = os.path.join(GetOption('hdir'), filenode.name)
-    out = IDLOutFile(savename)
+    if GetOption('verbose'):
+      print "Working on %s" % filenode
 
-    # Write the include guard, copyright, and file comment.
-    def_guard = filenode.name.replace('/','_').replace('.','_').upper()
-    out.Write('#ifndef %s\n#define %s\n\n' % (def_guard, def_guard))
-    out.Write('%s\n' % filenode.GetOneOf('Copyright').name)
-    for comment in filenode.GetListOf('Comment'):
-     out.Write('\n%s\n' % comment.name)
+    # If this file has errors, skip it
+    if filenode.GetProperty('ERRORS') > 0:
+      skipList.append(filenode)
+      continue
 
-    # Generate definitions.
-    for node in filenode.childlist[2:]:
-      item = Define(node, prefix='tst_')
-      print '>>%s<<' % item
-      out.Write(item)
+    GenerateHeader(filenode, prefix, inline=not cfile)
+    if cfile: GenerateFileTest(cfile, filenode, prefix)
 
-    out.Write('\n#endif /* %s */\n\n' % def_guard)
-    out.Close()
+  if cfile:
+    cfile.Write('int main(int argc, const char *args[]) {\n')
+    cfile.Write('  int errors = 0;\n');
+    for testname in testFuncs:
+      cfile.Write('  errors += %s();\n' % testname)
+
+    cfile.Write('  printf("Found %d error(s) in the IDL.\\n", errors);\n')
+    cfile.Write('  return errors;\n}\n\n')
+    cfile.Close()
+
+  # TODO(noelallen) Add a standard test
+    if not skipList:
+      args = ['gcc', '-o', 'tester', '%s/test.c' % outdir, '-I%s' % outdir,
+              '-I../c', '-I../..', '-DPPAPI_INSTANCE_REMOVE_SCRIPTING']
+      InfoOut.Log('Running: %s' % ' '.join(args))
+      ret = subprocess.call(args)
+      if ret:
+        ErrOut.Log('Failed to compile.')
+        return -1
+      InfoOut.Log('Running: ./tester')
+      ret = subprocess.call('./tester')
+      if ret > 0:
+        ErrOut.Log('Found %d errors.' % ret)
+        return ret
+      InfoOut.Log('Success!')
+      return 0
+
+  for filenode in skipList:
+    errcnt = filenode.GetProperty('ERRORS')
+    ErrOut.Log('%s : Skipped because of %d errors.' % (
+        filenode.GetName(), errcnt))
+
+  if not skipList:
+    InfoOut.Log('Processed %d files.' % len(ast.GetListOf('File')))
+  return len(skipList)
 
 if __name__ == '__main__':
   sys.exit(Main(sys.argv[1:]))
diff --git a/ppapi/generators/idl_c_proto.py b/ppapi/generators/idl_c_proto.py
index 145eff1d..3809795 100644
--- a/ppapi/generators/idl_c_proto.py
+++ b/ppapi/generators/idl_c_proto.py
@@ -11,243 +11,537 @@
 import sys
 
 from idl_log import ErrOut, InfoOut, WarnOut
-from idl_node import IDLAttribute, IDLAst, IDLNode
+from idl_node import IDLNode
+from idl_ast import IDLAst
 from idl_option import GetOption, Option, ParseOptions
-from idl_outfile import IDLOutFile
-from idl_parser import IDLParser, ParseFiles
+from idl_parser import ParseFiles
+
+Option('cgen_debug', 'Debug generate.')
+
+class CGenError(Exception):
+  def __init__(self, msg):
+    self.value = value
+
+  def __str__(self):
+    return repr(self.value)
 
 
-#
-# 'C' style parameter and return styles
-#
-
-# Normal parameters (int8_t, etc...)
-NormalType = {
-  'in': '$TYPEREF$',
-  'inout': '$TYPEREF$*',
-  'out': '$TYPEREF$*',
-  'store': '$TYPEREF$',
-  'return': '$TYPEREF$'
-}
-
-# Enum uses the enum's name, except when storing.
-EnumType = {
-  'in': '$TYPEREF$',
-  'inout': '$TYPEREF$*',
-  'out': '$TYPEREF$*',
-  'store': 'int',
-  'return': '$TYPEREF$'
-}
-
-# A mem_t is a 'void *' to memory.
-MemPtrType = {
-  'in': 'const void*',
-  'inout': 'void*',
-  'out': 'void*',
-  'store': 'void*',
-  'return': 'void*'
-}
-
-# A str_t is a string pointer 'char *'.
-CharPtrType = {
-  'in': 'const char*',
-  'inout': 'char*',
-  'out': 'char*',
-  'return': 'char*',
-  'store': 'char*'
-}
-
-# A function pointer type
-FuncType = {
-  'in': '$TYPEREF$',
-  'inout': '$TYPEREF$',
-  'out': '$TYPEREF$',
-  'return': '$TYPEREF$',
-  'store': '$TYPEREF$'
-}
-
-# A 'struct' is passed by pointer.
-StructType = {
-  'in': 'const $TYPEREF$*',
-  'inout': '$TYPEREF$*',
-  'out': '$TYPEREF$*',
-  'return': '$TYPEREF$*',
-  'store': '$TYPEREF$'
-}
-
-# A 'void' does not have a parameter or storage type.
-VoidType = {
-  'in': None,
-  'inout': None,
-  'out': None,
-  'return': 'void',
-  'store': None
-}
-
-TypeMap = {
-  'func': FuncType,
-  'enum': EnumType,
-  'mem_t': MemPtrType,
-  'str_t': CharPtrType,
-  'struct': StructType,
-  'void': VoidType
-}
-
-
-# Place the individual 'normal' types in the type map.
-for name in ['int8_t', 'int16_t' ,'int32_t', 'int64_t', 'uint8_t', 'uint16_t',
-             'uint32_t', 'uint64_t', 'double_t', 'float_t', 'handle_t', 'bool']:
-  TypeMap[name] = NormalType
-
-
-
-# Return the name of the node with suffix/prefix if availible.
-def GetName(node, **keyargs):
-  prefix = keyargs.get('prefix','')
-  suffix = keyargs.get('suffix', '')
-  named = keyargs.get('named', True)
-  name = node.name
-  if not named: name = ''
-  return '%s%s%s' % (prefix, name, suffix)
-
-
-# Return the array specification of the object.
-def GetArrayspec(node, name):
-  assert(node.cls == 'Array')
-  out = ''
-  fixed = node.GetProperty('FIXED')
-  if fixed:
-    out = '%s[%s]' % (name, fixed)
-  else:
-    out = '*%s' % name
-  # Add children
-  for child in node.GetListOf('Array'):
-    out += GetArrayspec(child, name)
-  return out
-
-
-# Return the <name>[<size>] form of the node.
-def GetNameArray(node, **keyargs):
-  name = GetName(node, **keyargs)
-  for child in node.GetListOf('Array'):
-    name = GetArrayspec(child, name)
-  return name
-
-
-def GetRootType(node):
-  root = node
-  while root.typeinfo:
-    typeinfo = root.typeinfo
-    if root.GetOneOf('Callspec'):
-      return TypeMap['func']
-    root = root.typeinfo
-  return TypeMap[root.name]
-
-
-# Get the passing form of the parameter.
-def GetParamType(node, **keyargs):
-  typedata = GetRootType(node)
-  if node.GetProperty('in'):
-    return node.Replace(typedata['in'])
-  if node.GetProperty('out'):
-    return node.Replace(typedata['out'])
-  if node.GetProperty('inout'):
-    return node.Replace(typedata['inout'])
-  return node.GetProperty('TYPEREF')
-
-
-# GetParam
-#
-# A Param signature is different than that of a Member because a Member is
-# stored in the structure as declared, but the Param's signature is affected
-# by how it gets passed and it's extended attributes like IN, OUT, and INOUT.
-#
-def GetParam(node, **keyargs):
-  # This may be a function.
-  callnode = node.GetOneOf('Callspec')
-
-  typeref  = GetParamType(node, **keyargs)
-  name = GetNameArray(node, **keyargs)
-  if callnode:
-    arglist = GetParamList(callnode, **keyargs)
-    return '%s (*%s)(%s)' % (typeref, name, ', '.join(arglist))
-  return '%s %s' % (typeref, name)
-
-
-# GetParamList
-def GetParamList(node, **keyargs):
-  assert(node.cls == 'Callspec')
-  out = []
-  for child in node.GetListOf('Param'):
-    out.append(GetParam(child, **keyargs))
-  return out
-
-
-# DefineSignature
-def DefineSignature(node, **keyargs):
-  # This may be a function.
-  callnode = node.GetOneOf('Callspec')
-
-  typeref  = node.GetProperty('TYPEREF')
-  name = GetNameArray(node, **keyargs)
-  if callnode:
-    arglist = GetParamList(callnode, **keyargs)
-    return '%s (*%s)(%s)' % (typeref, name, ', '.join(arglist))
-  return '%s %s' % (typeref, name)
-
-# Define a Member (or Function) in an interface.
-def DefineMember(node, **keyargs):
-  return '%s;\n' % DefineSignature(node, **keyargs)
-
-# Define an Typedef.
-def DefineTypedef(node, **keyargs):
-  return 'typedef %s;\n' % DefineSignature(node, **keyargs)
-
-# Define an Enum.
-def DefineEnum(node, **keyargs):
-  out = 'enum %s {' % GetName(node, **keyargs)
-
-  enumlist = []
-  for child in node.GetListOf('EnumItem'):
-    value = child.GetProperty('VALUE')
-    enumlist.append('  %s = %s' % (GetName(child), value))
-  return '%s\n%s\n};\n' % (out, ',\n'.join(enumlist))
-
-# Define a Struct.
-def DefineStruct(node, **keyargs):
-  out = 'struct %s {\n' % (GetName(node, **keyargs))
-
-  # Generate Member Functions
-  for child in node.GetListOf('Function'):
-    out += '  %s' % DefineMember(child, **keyargs)
-
-  # Generate Member Variables
-  for child in node.GetListOf('Member'):
-    out += '  %s' % DefineMember(child, **keyargs)
-  out += '};'
-  return out
-
-
-# Define a top level object.
-def Define(node, **keyargs):
-  declmap = {
-    'Enum' : DefineEnum,
-    'Interface' : DefineStruct,
-    'Struct' : DefineStruct,
-    'Typedef' : DefineTypedef,
+class CGen(object):
+  # TypeMap
+  #
+  # TypeMap modifies how an object is stored or passed, for example pointers
+  # are passed as 'const' if they are 'in' parameters, and structures are
+  # preceeded by the keyword 'struct' as well as using a pointer.
+  #
+  TypeMap = {
+    'Array': {
+      'in': 'const %s',
+      'inout': '%s*',
+      'out': '%s*',
+      'store': '%s',
+      'return': '%s'
+    },
+    'Callspec': {
+      'in': '%s',
+      'inout': '%s',
+      'out': '%s',
+      'store': '%s',
+      'return': '%s'
+    },
+    'Enum': {
+      'in': '%s',
+      'inout': '%s*',
+      'out': '%s*',
+      'store': '%s',
+      'return': '%s'
+    },
+    'Struct': {
+      'in': 'const %s*',
+      'inout': '%s*',
+      'out': '%s*',
+      'return': ' %s*',
+      'store': '%s'
+    },
+    'mem_t': {
+      'in': 'const %s',
+      'inout': '%s',
+      'out': '%s',
+      'return': '%s',
+      'store': '%s'
+    },
+    'str_t': {
+      'in': 'const %s',
+      'inout': '%s',
+      'out': '%s',
+      'return': 'const %s',
+      'store': '%s'
+    },
+    'TypeValue': {
+      'in': '%s',
+      'inout': '%s*',
+      'out': '%s*',
+      'return': '%s',
+      'store': '%s'
+    },
   }
 
-  func = declmap.get(node.cls)
-  if not func:
-    ErrLog.Log('Failed to define %s named %s' % (node.cls, node.name))
-  out = func(node, **keyargs)
 
-  tab = ''
-  for i in range(keyargs.get('tabs', 0)):
-    tab += '  '
+  #
+  # RemapName
+  #
+  # A diction array of PPAPI types that are converted to language specific
+  # types before being returned by by the C generator
+  #
+  RemapName = {
+  'float_t': 'float',
+  'double_t': 'double',
+  'handle_t': 'int',
+  'mem_t': 'void*',
+  'str_t': 'char*',
+  'interface_t' : 'const void*'
+  }
 
-  return ('%s\n' % tab).join(out.split('\n')) + '\n'
+  def __init__(self):
+    self.dbg_depth = 0
+    self.vmin = 0.0
+    self.vmax = 1e100
+    self.release = 'M14'
 
+  def SetVersionMap(self, node):
+    self.vmin = 0.0
+    self.vmax = 1e100
+    for version in node.GetListOf('LabelItem'):
+      if version.GetName() == GetOption('version'):
+        self.vmin = float(version.GetProperty('VALUE'))
+        self.vmax = float(version.GetProperty('VALUE'))
+
+  #
+  # Debug Logging functions
+  #
+  def Log(self, txt):
+    if not GetOption('cgen_debug'): return
+    tabs = ''
+    for tab in range(self.dbg_depth): tabs += '  '
+    print '%s%s' % (tabs, txt)
+
+  def LogEnter(self, txt):
+    if txt: self.Log(txt)
+    self.dbg_depth += 1
+
+  def LogExit(self, txt):
+    self.dbg_depth -= 1
+    if txt: self.Log(txt)
+
+  #
+  # Return the array specification of the object.
+  #
+  def GetArraySpec(self, node):
+    assert(node.cls == 'Array')
+    out = ''
+    fixed = node.GetProperty('FIXED')
+    if fixed:
+      return '[%s]' % fixed
+    else:
+      return '[]'
+
+  #
+  # GetTypeName
+  #
+  # For any valid 'typed' object such as Member or Typedef
+  # the typenode object contains the typename
+  #
+  # For a given node return the type name by passing mode.
+  #
+  def GetTypeName(self, node, prefix=''):
+    self.LogEnter('GetTypeName of %s' % node)
+
+    # For Members, Params, and Typedef's your want type it refers to
+    if node.IsA('Member', 'Param', 'Typedef'):
+      typeref = node.GetType(self.release)
+    else:
+      typeref = node
+
+    if typeref is None:
+      raise CGenError('No type for %s' % node)
+
+    # If the type is a (BuiltIn) Type then return it's name
+    # remapping as needed
+    if typeref.IsA('Type'):
+      name = CGen.RemapName.get(typeref.GetName(), None)
+      if name is None: name = typeref.GetName()
+      name = '%s%s' % (prefix, name)
+
+    # For structures, preceed with 'struct' or 'union' as appropriate
+    elif typeref.IsA('Interface', 'Struct'):
+      if typeref.GetProperty('union'):
+        name = 'union %s%s' % (prefix, typeref.GetName())
+      else:
+        name = 'struct %s%s' % (prefix, typeref.GetName())
+
+    # If it's an enum, or typedef then return the Enum's name
+    elif typeref.IsA('Enum', 'Typedef'):
+      name = '%s%s' % (prefix, typeref.GetName())
+
+    else:
+      raise RuntimeError('Getting name of non-type %s.' % node)
+    self.LogExit('GetTypeName %s is %s' % (node, name))
+    return name
+
+
+  #
+  # GetRootType
+  #
+  # For a given node return basic type of that object.  This is
+  # either a 'Type', 'Callspec', or 'Array'
+  #
+  def GetRootTypeMode(self, node, mode):
+    self.LogEnter('GetRootType of %s' % node)
+    # If it has an array spec, then treat it as an array regardless of type
+    if node.GetOneOf('Array'):
+      rootType = 'Array'
+    # Or if it has a callspec, treat it as a function
+    elif node.GetOneOf('Callspec'):
+      rootType, mode = self.GetRootTypeMode(node.GetType(self.release),
+                                            'return')
+
+    # If it's a plain typedef, try that object's root type
+    elif node.IsA('Member', 'Param', 'Typedef'):
+      rootType, mode = self.GetRootTypeMode(node.GetType(self.release), mode)
+
+    # If it's an Enum, then it's normal passing rules
+    elif node.IsA('Enum'):
+      rootType = node.cls
+
+    # If it's an Interface or Struct, we may be passing by value
+    elif node.IsA('Interface', 'Struct'):
+      if mode == 'return':
+        if node.GetProperty('returnByValue'):
+          rootType = 'TypeValue'
+        else:
+          rootType = node.cls
+      else:
+        if node.GetProperty('passByValue'):
+          rootType = 'TypeValue'
+        else:
+          rootType = node.cls
+
+    # If it's an Basic Type, check if it's a special type
+    elif node.IsA('Type'):
+      if node.GetName() in CGen.TypeMap:
+        rootType = node.GetName()
+      else:
+        rootType = 'TypeValue'
+    else:
+      raise RuntimeError('Getting root type of non-type %s.' % node)
+    self.LogExit('RootType is "%s"' % rootType)
+    return rootType, mode
+
+
+  def GetTypeByMode(self, node, mode):
+    self.LogEnter('GetTypeByMode of %s mode=%s' % (node, mode))
+    name = self.GetTypeName(node)
+    type, mode = self.GetRootTypeMode(node, mode)
+    out = CGen.TypeMap[type][mode] % name
+    self.LogExit('GetTypeByMode %s = %s' % (node, out))
+    return out
+
+
+  # Get the passing mode of the object (in, out, inout).
+  def GetParamMode(self, node):
+    self.Log('GetParamMode for %s' % node)
+    if node.GetProperty('in'): return 'in'
+    if node.GetProperty('out'): return 'out'
+    if node.GetProperty('inout'): return 'inout'
+    return 'return'
+
+  #
+  # GetComponents
+  #
+  # Returns the signature components of an object as a tuple of
+  # (rtype, name, arrays, callspec) where:
+  #   rtype - The store or return type of the object.
+  #   name - The name of the object.
+  #   arrays - A list of array dimensions as [] or [<fixed_num>].
+  #   args -  None of not a function, otherwise  a list of parameters.
+  #
+  def GetComponents(self, node, mode):
+    self.LogEnter('GetComponents mode %s for %s' % (mode, node))
+
+    # Generate passing type by modifying root type
+    rtype = self.GetTypeByMode(node, mode)
+    if node.IsA('Enum', 'Interface', 'Struct'):
+      rname = node.GetName()
+    else:
+      rname = node.GetType(self.release).GetName()
+
+    if rname in CGen.RemapName:
+      rname = CGen.RemapName[rname]
+    if '%' in rtype:
+      rtype = rtype % rname
+    name = node.GetName()
+    arrayspec = [self.GetArraySpec(array) for array in node.GetListOf('Array')]
+    callnode = node.GetOneOf('Callspec')
+    if callnode:
+      callspec = []
+      for param in callnode.GetListOf('Param'):
+        mode = self.GetParamMode(param)
+        ptype, pname, parray, pspec = self.GetComponents(param, mode)
+        callspec.append((ptype, pname, parray, pspec))
+    else:
+      callspec = None
+
+    self.LogExit('GetComponents: %s, %s, %s, %s' %
+                 (rtype, name, arrayspec, callspec))
+    return (rtype, name, arrayspec, callspec)
+
+
+  def Compose(self, rtype, name, arrayspec, callspec, prefix, func_as_ptr):
+    self.LogEnter('Compose: %s %s' % (rtype, name))
+    arrayspec = ''.join(arrayspec)
+    name = '%s%s%s' % (prefix, name, arrayspec)
+    if callspec is None:
+      out = '%s %s' % (rtype, name)
+    else:
+      params = []
+      for ptype, pname, parray, pspec in callspec:
+        params.append(self.Compose(ptype, pname, parray, pspec, '', True))
+      if func_as_ptr: name = '(*%s)' % name
+      out = '%s %s(%s)' % (rtype, name, ', '.join(params))
+    self.LogExit('Exit Compose: %s' % out)
+    return out
+
+  #
+  # GetSignature
+  #
+  # Returns the 'C' style signature of the object
+  #  prefix - A prefix for the object's name
+  #  func_as_ptr - Formats a function as a function pointer
+  #
+  def GetSignature(self, node, mode, prefix='', func_as_ptr=True):
+    self.LogEnter('GetSignature %s %s as func=%s' % (node, mode, func_as_ptr))
+    rtype, name, arrayspec, callspec = self.GetComponents(node, mode)
+    out = self.Compose(rtype, name, arrayspec, callspec, prefix, func_as_ptr)
+    self.LogExit('Exit GetSignature: %s' % out)
+    return out
+
+  def GetMacro(self, node):
+    name = node.GetName()
+    name = name.upper()
+    return "%s_INTERFACE" % name
+
+  def GetDefine(self, name, value):
+    out = '#define %s %s' % (name, value)
+    if len(out) > 80:
+      out = '#define %s \\\n    %s' % (name, value)
+    return '%s\n' % out
+
+  # Define an Typedef.
+  def DefineTypedef(self, node, prefix='', comment=False):
+    out = 'typedef %s;\n' % self.GetSignature(node, 'return', prefix, True)
+    self.Log('DefineTypedef: %s' % out)
+    return out
+
+  # Define an Enum.
+  def DefineEnum(self, node, prefix='', comment=False):
+    self.LogEnter('DefineEnum %s' % node)
+    unnamed =  node.GetProperty('unnamed')
+    if unnamed:
+      out = 'enum {'
+    else:
+      out = 'typedef enum {'
+    name = '%s%s' % (prefix, node.GetName())
+    enumlist = []
+    for child in node.GetListOf('EnumItem'):
+      value = child.GetProperty('VALUE')
+      comment_txt = ''
+      if comment:
+        for comment_node in child.GetListOf('Comment'):
+          comment_txt += self.Comment(comment_node, tabs=1)
+        if comment_txt:
+          comment_txt = '%s' % comment_txt
+      if value:
+        item_txt = '%s%s = %s' % (prefix, child.GetName(), value)
+      else:
+        item_txt = '%s%s' % (prefix, child.GetName())
+      enumlist.append('%s  %s' % (comment_txt, item_txt))
+    self.LogExit('Exit DefineEnum')
+
+    if unnamed:
+      out = '%s\n%s\n};\n' % (out, ',\n'.join(enumlist))
+    else:
+      out = '%s\n%s\n} %s;\n' % (out, ',\n'.join(enumlist), name)
+    return out
+
+  def DefineMember(self, node, prefix='', comment=False):
+    self.LogEnter('DefineMember %s' % node)
+
+#    out = ''
+#    if comment:
+#      for doc in node.GetListOf('Comment'):
+#        out += self.Comment(doc)
+    out = '%s;' % self.GetSignature(node, 'store', '', True)
+    self.LogExit('Exit DefineMember')
+    return out
+
+  # Define a Struct.
+  def DefineStruct(self, node, prefix='', comment=False):
+    out = ''
+    if node.IsA('Interface'):
+      release = 'M14'
+      name = node.GetName()
+      macro = node.GetProperty('macro')
+      if not macro:
+        macro = self.GetMacro(node)
+      label = node.GetLabel()
+      if label:
+        for vers in label.versions:
+          strver = str(vers).replace('.', '_')
+          out += self.GetDefine('%s_%s' % (macro, strver),
+                                '"%s;%s"' % (name, vers))
+          if label.GetRelease(vers) == release:
+            out += self.GetDefine(macro, '%s_%s' % (macro, strver))
+        out += '\n'
+
+    self.LogEnter('DefineStruct %s' % node)
+    if node.GetProperty('union'):
+      out += 'union %s%s {\n' % (prefix, node.GetName())
+    else:
+      out += 'struct %s%s {\n' % (prefix, node.GetName())
+
+    # Generate Member Functions
+    members = []
+    for child in node.GetListOf('Member'):
+      member = self.Define(child, tabs=1, comment=comment)
+      if not member:
+        continue
+      members.append(member)
+    out += '%s\n};\n' % '\n'.join(members)
+    self.LogExit('Exit DefineStruct')
+    return out
+
+  #
+  # Copyright and Comment
+  #
+  # Generate a comment or copyright block
+  #
+  def Copyright(self, node, tabs=0):
+    lines = node.GetName().split('\n')
+    return self.CommentLines(lines, tabs)
+
+  def Comment(self, node, prefix=None, tabs=0):
+    comment = node.GetName()
+
+    # Ignore comments that do not have a '*' marker
+#    if comment[0] != '*' and not prefix: return ''
+
+    lines = comment.split('\n')
+    if prefix:
+      prefix = prefix.split('\n')
+      if prefix[0] == '*' and lines[0] == '*':
+        lines = prefix + lines[1:]
+      else:
+        lines = prefix + lines;
+    return self.CommentLines(lines, tabs)
+
+  def CommentLines(self, lines, tabs=0):
+    tab = ''.join(['  ' for i in range(tabs)])
+    if lines[-1] == '':
+      return '%s/*' % tab + ('\n%s *' % tab).join(lines) + '/\n'
+    else:
+      return '%s/*' % tab + ('\n%s *' % tab).join(lines) + ' */\n'
+
+
+  # Define a top level object.
+  def Define(self, node, tabs=0, prefix='', comment=False):
+    if True:
+#    try:
+      self.LogEnter('Define %s tab=%d prefix="%s"' % (node,tabs,prefix))
+
+      min = node.GetProperty('version')
+      max = node.GetProperty('deprecate')
+
+      if min is not None:
+        min = float(min)
+      else:
+        min = 0.0
+
+      if max is not None:
+        max = float(max)
+      else:
+        max = 1.0e100
+
+      label = node.GetLabel()
+      if label:
+        lver = label.GetVersion('M14')
+
+        # Verify that we are in a valid version.
+        if max <= lver: return ''
+        if min > lver: return ''
+
+      declmap = {
+        'Enum' : CGen.DefineEnum,
+        'Function' : CGen.DefineMember,
+        'Interface' : CGen.DefineStruct,
+        'Member' : CGen.DefineMember,
+        'Struct' : CGen.DefineStruct,
+        'Typedef' : CGen.DefineTypedef,
+      }
+
+      if node.cls == 'Inline':
+        return node.GetProperty('VALUE')
+
+      if node.cls == 'Label':
+        return ''
+
+      out = ''
+      comment_txt = ''
+      if comment:
+        for doc in node.GetListOf('Comment'):
+          comment_txt += self.Comment(doc)
+
+      func = declmap.get(node.cls)
+      if not func:
+        ErrOut.Log('Failed to define %s named %s' % (node.cls, node.GetName()))
+
+      define_txt = func(self, node, prefix=prefix, comment=comment)
+      if comment_txt:
+        out += '%s%s' % (comment_txt, define_txt)
+      else:
+        out += define_txt
+
+      tab = ''
+      for i in range(tabs):
+        tab += '  '
+
+      lines = []
+      for line in out.split('\n'):
+        # Add indentation
+        line = '%s%s' % (tab, line)
+        if len(line) > 80:
+          left = line.rfind('(') + 1
+          args = line[left:].split(',')
+          max = 0
+          for arg in args:
+            if len(arg) > max: max = len(arg)
+
+          if left + max >= 80:
+            space = '%s    ' % tab
+            args =  (',\n%s' % space).join([arg.strip() for arg in args])
+            lines.append('%s\n%s%s' % (line[:left], space, args))
+          else:
+            space = ' '.join(['' for i in range(left)])
+            args =  (',\n%s' % space).join(args)
+            lines.append('%s%s' % (line[:left], args))
+        else:
+          lines.append(line.rstrip())
+
+  #    out = tab + ('\n%s' % tab).join(out.split('\n')) + '\n'
+      self.LogExit('Exit Define')
+      return '\n'.join(lines)
+#    except:
+    if False:
+      node.Error('Failed to resolve.')
+      return ''
 
 # Clean a string representing an object definition and return then string
 # as a single space delimited set of tokens.
@@ -259,15 +553,18 @@
 
 # Test a file, by comparing all it's objects, with their comments.
 def TestFile(filenode):
+  cgen = CGen()
+
   errors = 0
-  for node in filenode.Children()[2:]:
-    if GetOption('verbose'):
-      node.Dump(0, comments=True)
-
+  for node in filenode.GetChildren()[2:]:
     instr = node.GetOneOf('Comment')
-    instr = CleanString(instr.name)[3:-3]
+    if not instr: continue
+    instr.Dump()
+    instr = CleanString(instr.GetName())
 
-    outstr = Define(node)
+    outstr = cgen.Define(node)
+    if GetOption('verbose'):
+      print outstr + '\n'
     outstr = CleanString(outstr)
 
     if instr != outstr:
@@ -277,7 +574,6 @@
   return errors
 
 
-
 # Build and resolve the AST and compare each file individual.
 def TestFiles(filenames):
   if not filenames:
@@ -286,13 +582,14 @@
     filenames = glob.glob(idldir)
 
   filenames = sorted(filenames)
-  ast_result = ParseFiles(filenames)
+  ast = ParseFiles(filenames)
 
   total_errs = 0
-  for filenode in ast_result.out.GetListOf('File'):
+  for filenode in ast.GetListOf('File'):
     errs = TestFile(filenode)
     if errs:
-      ErrOut.Log('%s test failed with %d error(s).' % (filenode.name, errs))
+      ErrOut.Log('%s test failed with %d error(s).' %
+                 (filenode.GetName(), errs))
       total_errs += errs
 
   if total_errs:
@@ -301,10 +598,19 @@
     InfoOut.Log('Passed generator test.')
   return total_errs
 
-
 def Main(args):
   filenames = ParseOptions(args)
-  return TestFiles(filenames)
+  if GetOption('test'):
+    return TestFiles(filenames)
+  ast = ParseFiles(filenames)
+  for f in ast.GetListOf('File'):
+    if f.GetProperty('ERRORS') > 0:
+      print 'Skipping %s' % f.GetName()
+      continue
+    print DefineDepends(node)
+    for node in f.GetChildren()[2:]:
+      print Define(node, comment=True, prefix='tst_')
+
 
 if __name__ == '__main__':
   sys.exit(Main(sys.argv[1:]))
diff --git a/ppapi/generators/idl_diff.py b/ppapi/generators/idl_diff.py
index d005a205..9d1abbbe 100644
--- a/ppapi/generators/idl_diff.py
+++ b/ppapi/generators/idl_diff.py
@@ -10,7 +10,7 @@
 import sys
 
 from idl_option import GetOption, Option, ParseOptions
-
+from idl_outfile import IDLOutFile
 #
 # IDLDiff
 #
@@ -23,16 +23,18 @@
 
 Option('gen', 'IDL generated files', default='hdir')
 Option('src', 'Original ".h" files', default='../c')
-
+Option('halt', 'Stop if a difference is found')
+Option('diff', 'Directory holding acceptable diffs', default='diff')
+Option('ok', 'Write out the diff file.')
 # Change
 #
 # A Change object contains the previous lines, new news and change type.
 #
 class Change(object):
-  def __init__(self, change):
-    self.mode = str(change['mode'])
-    self.was = list(change['was'])
-    self.now = list(change['now'])
+  def __init__(self, mode, was, now):
+    self.mode = mode
+    self.was = was
+    self.now = now
 
   def Dump(self):
     if not self.was:
@@ -43,9 +45,9 @@
       print 'Modifying %s' % self.mode
 
     for line in self.was:
-      print 'src: %s' % line
+      print 'src: >>%s<<' % line
     for line in self.now:
-      print 'gen: %s' % line
+      print 'gen: >>%s<<' % line
     print
 
 #
@@ -55,9 +57,20 @@
 # such as non-matching years.
 #
 def IsCopyright(change):
-  if len(change['now']) != 1 or len(change['was']) != 1: return False
-  if 'Copyright (c)' not in change['now'][0]: return False
-  if 'Copyright (c)' not in change['was'][0]: return False
+  if len(change.now) != 1 or len(change.was) != 1: return False
+  if 'Copyright (c)' not in change.now[0]: return False
+  if 'Copyright (c)' not in change.was[0]: return False
+  return True
+
+#
+# IsBlankComment
+#
+# Return True if this change only removes a blank line from a comment
+#
+def IsBlankComment(change):
+  if change.now: return False
+  if len(change.was) != 1: return False
+  if change.was[0].strip() != '*': return False
   return True
 
 #
@@ -66,14 +79,46 @@
 # Return True if this change only adds or removes blank lines
 #
 def IsBlank(change):
-  for line in change['now']:
+  for line in change.now:
     if line: return False
-  for line in change['was']:
+  for line in change.was:
     if line: return False
   return True
 
 
 #
+# IsCppComment
+#
+# Return True if this change only going from C++ to C style
+#
+def IsToCppComment(change):
+  if not len(change.now) or len(change.now) != len(change.was):
+    return False
+  for index in range(len(change.now)):
+    was = change.was[index].strip()
+    if was[:2] != '//':
+      return False
+    was = was[2:].strip()
+    now = change.now[index].strip()
+    if now[:2] != '/*':
+      return False
+    now = now[2:-2].strip()
+    if now != was:
+      return False
+  return True
+
+
+  return True
+
+def IsMergeComment(change):
+  if len(change.was) != 1: return False
+  if change.was[0].strip() != '*': return False
+  for line in change.now:
+    stripped = line.strip()
+    if stripped != '*' and stripped[:2] != '/*' and stripped[-2:] != '*/':
+      return False
+  return True
+#
 # IsSpacing
 #
 # Return True if this change is only different in the way 'words' are spaced
@@ -85,17 +130,17 @@
 #   ENUM_XYY_Y = 2,
 #
 def IsSpacing(change):
-  if len(change['now']) != len(change['was']): return False
-  for i in range(len(change['now'])):
+  if len(change.now) != len(change.was): return False
+  for i in range(len(change.now)):
     # Also ignore right side comments
-    line = change['was'][i]
+    line = change.was[i]
     offs = line.find('//')
     if offs == -1:
       offs = line.find('/*')
     if offs >-1:
       line = line[:offs-1]
 
-    words1 = change['now'][i].split()
+    words1 = change.now[i].split()
     words2 = line.split()
     if words1 != words2: return False
   return True
@@ -106,8 +151,9 @@
 # Return True if change has extra includes
 #
 def IsInclude(change):
-  if len(change['was']): return False
-  for line in change['now']:
+  for line in change.was:
+    if line.strip().find('struct'): return False
+  for line in change.now:
     if line and '#include' not in line: return False
   return True
 
@@ -117,8 +163,8 @@
 # Return True if the change is only missing C++ comments
 #
 def IsCppComment(change):
-  if len(change['now']): return False
-  for line in change['was']:
+  if len(change.now): return False
+  for line in change.was:
     line = line.strip()
     if line[:2] != '//': return False
   return True
@@ -128,7 +174,10 @@
 # Return True if none of the changes does not patch an above "bogus" change.
 #
 def ValidChange(change):
+  if IsToCppComment(change): return False
   if IsCopyright(change): return False
+  if IsBlankComment(change): return False
+  if IsMergeComment(change): return False
   if IsBlank(change): return False
   if IsSpacing(change): return False
   if IsInclude(change): return False
@@ -136,55 +185,170 @@
   return True
 
 
+#
+# Swapped
+#
+# Check if the combination of last + next change signals they are both
+# invalid such as swap of line around an invalid block.
+#
+def Swapped(last, next):
+  if not last.now and not next.was and len(last.was) == len(next.now):
+    cnt = len(last.was)
+    for i in range(cnt):
+      match = True
+      for j in range(cnt):
+        if last.was[j] != next.now[(i + j) % cnt]:
+          match = False
+          break;
+      if match: return True
+  if not last.was and not next.now and len(last.now) == len(next.was):
+    cnt = len(last.now)
+    for i in range(cnt):
+      match = True
+      for j in range(cnt):
+        if last.now[i] != next.was[(i + j) % cnt]:
+          match = False
+          break;
+      if match: return True
+  return False
 
+
+def FilterLinesIn(output):
+  was = []
+  now = []
+  filter = []
+  for index in range(len(output)):
+    filter.append(False)
+    line = output[index]
+    if len(line) < 2: continue
+    if line[0] == '<':
+      if line[2:].strip() == '': continue
+      was.append((index, line[2:]))
+    elif line[0] == '>':
+      if line[2:].strip() == '': continue
+      now.append((index, line[2:]))
+  for windex, wline in was:
+    for nindex, nline in now:
+      if filter[nindex]: continue
+      if filter[windex]: continue
+      if wline == nline:
+        filter[nindex] = True
+        filter[windex] = True
+        if GetOption('verbose'):
+          print "Found %d, %d >>%s<<" % (windex + 1, nindex + 1, wline)
+  out = []
+  for index in range(len(output)):
+    if not filter[index]:
+      out.append(output[index])
+
+  return out
 #
 # GetChanges
 #
 # Parse the output into discrete change blocks.
 #
 def GetChanges(output):
+  # Split on lines, adding an END marker to simply add logic
   lines = output.split('\n')
+  lines = FilterLinesIn(lines)
+  lines.append('END')
+
   changes = []
-  change = { 'mode' : None,
-             'was': [],
-             'now': []
-            }
+  was = []
+  now = []
+  mode = ''
+  last = None
+
   for line in lines:
+#    print "LINE=%s" % line
     if not line: continue
+
     elif line[0] == '<':
-      change['was'].append(line[2:])
+      if line[2:].strip() == '': continue
+      # Ignore prototypes
+      if len(line) > 10:
+        words = line[2:].split()
+        if len(words) == 2 and words[1][-1] == ';':
+          if words[0] == 'struct' or words[0] == 'union':
+            continue
+      was.append(line[2:])
     elif line[0] == '>':
-      change['now'].append(line[2:])
+      if line[2:].strip() == '': continue
+      if line[2:10] == '#include': continue
+      now.append(line[2:])
     elif line[0] == '-':
       continue
     else:
-      if ValidChange(change): changes.append(Change(change))
-      change['mode'] = line
-      change['now'] = []
-      change['was'] = []
-  if ValidChange(change): changes.append(Change(change))
-  return changes
+      change = Change(line, was, now)
+      was = []
+      now = []
+      if ValidChange(change):
+          changes.append(change)
+      if line == 'END':
+        break
+
+  return FilterChanges(changes)
+
+def FilterChanges(changes):
+  if len(changes) < 2: return changes
+  out = []
+  filter = [False for change in changes]
+  for cur in range(len(changes)):
+    for cmp in range(cur+1, len(changes)):
+      if filter[cmp]:
+        continue
+      if Swapped(changes[cur], changes[cmp]):
+        filter[cur] = True
+        filter[cmp] = True
+  for cur in range(len(changes)):
+    if filter[cur]: continue
+    out.append(changes[cur])
+  return out
 
 def Main(args):
   filenames = ParseOptions(args)
   if not filenames:
     gendir = os.path.join(GetOption('gen'), '*.h')
-    filenames = glob.glob(gendir)
+    filenames = sorted(glob.glob(gendir))
+    srcdir = os.path.join(GetOption('src'), '*.h')
+    srcs = sorted(glob.glob(srcdir))
+    for name in srcs:
+      name = os.path.split(name)[1]
+      name = os.path.join(GetOption('gen'), name)
+      if name not in filenames:
+        print 'Missing: %s' % name
 
   for filename in filenames:
     gen = filename
     filename = filename[len(GetOption('gen')) + 1:]
     src = os.path.join(GetOption('src'), filename)
+    diff = os.path.join(GetOption('diff'), filename)
     p = subprocess.Popen(['diff', src, gen], stdout=subprocess.PIPE)
     output, errors = p.communicate()
 
-    changes = GetChanges(output)
+    try:
+      input = open(diff, 'rt').read()
+    except:
+      input = ''
+
+    if input != output:
+      changes = GetChanges(output)
+    else:
+      changes = []
+
     if changes:
       print "\n\nDelta between:\n  src=%s\n  gen=%s\n" % (src, gen)
-      for change in GetChanges(output):
+      for change in changes:
         change.Dump()
+      print 'Done with %s\n\n' % src
+      if GetOption('ok'):
+        open(diff, 'wt').write(output)
+      if GetOption('halt'):
+        return 1
     else:
-      print "\nSAME:\n  src=%s\n  gen=%s\n" % (src, gen)
+      print "\nSAME:\n  src=%s\n  gen=%s" % (src, gen)
+      if input: print '  ** Matched expected diff. **'
+      print '\n'
 
 if __name__ == '__main__':
   sys.exit(Main(sys.argv[1:]))
diff --git a/ppapi/generators/idl_namespace.py b/ppapi/generators/idl_namespace.py
index dbe6d11..b20e4f2 100644
--- a/ppapi/generators/idl_namespace.py
+++ b/ppapi/generators/idl_namespace.py
@@ -63,9 +63,9 @@
 
       # We should only be missing a 'version' tag for the first item.
       if not node.vmin:
+        raise RuntimeError('Missing version')
         node.Error('Missing version on overload of previous %s.' %
                    cver.Location())
-        raise RuntimeError('DSFSD')
         return False
 
       # If the node has no max, then set it to this one
@@ -142,36 +142,36 @@
       InfoOut.Log('')
 
   def FindVersion(self, name, version):
-    list = self.namespace.get(name, None)
-    if list == None:
+    verlist = self.namespace.get(name, None)
+    if verlist == None:
       if self.parent:
         return self.parent.FindVersion(name, version)
       else:
         return None
-    return list.FindVersion(version)
+    return verlist.FindVersion(version)
 
   def FindRange(self, name, vmin, vmax):
-    list = self.namespace.get(name, None)
-    if list == None:
+    verlist = self.namespace.get(name, None)
+    if verlist == None:
       if self.parent:
         return self.parent.FindRange(name, vmin, vmax)
       else:
         return []
-    return list.FindRange(vmin, vmax)
+    return verlist.FindRange(vmin, vmax)
 
   def FindList(self, name):
-    list = self.namespace.get(name, None)
-    if list == None:
+    verlist = self.namespace.get(name, None)
+    if verlist == None:
       if self.parent:
         return self.parent.FindList(name)
-    return list
+    return verlist
 
   def AddNode(self, node):
     name = node.GetName()
-    list = self.namespace.setdefault(name,IDLVersionList())
+    verlist = self.namespace.setdefault(name,IDLVersionList())
     if GetOption('namespace_debug'):
         print "Adding to namespace: %s" % node
-    return list.AddNode(node)
+    return verlist.AddNode(node)
 
 
 
@@ -303,6 +303,7 @@
   Bar23 = MockNode('bar', 2.0, 3.0)
   Bar34 = MockNode('bar', 3.0, 4.0)
 
+
   # Verify we succeed with fully qualified versions
   namespace = IDLNamespace(namespace)
   AddOkay(namespace, BarXX)
@@ -333,7 +334,9 @@
   VerifyFindAll(namespace, 'foo', 0.0, 3.0, [FooXX, Foo1X])
   VerifyFindAll(namespace, 'foo', 3.0, 100.0, [Foo3X])
 
-
+  FooBar = MockNode('foobar', 1.0, 2.0)
+  namespace = IDLNamespace(namespace)
+  AddOkay(namespace, FooBar)
 
   if errors:
     print 'Test failed with %d errors.' % errors
diff --git a/ppapi/generators/idl_node.py b/ppapi/generators/idl_node.py
index 285307a2..0c8c1807 100644
--- a/ppapi/generators/idl_node.py
+++ b/ppapi/generators/idl_node.py
@@ -81,8 +81,7 @@
       if child.cls == 'ExtAttribute':
         self.SetProperty(child.name, child.value)
       else:
-        child.SetParent(self)
-        self.children.append(child)
+        self.AddChild(child)
 
 #
 # String related functions
@@ -171,6 +170,10 @@
     self.property_node.AddParent(parent)
     self.parent = parent
 
+  def AddChild(self, node):
+    node.SetParent(self)
+    self.children.append(node)
+
   # Get a list of all children
   def GetChildren(self):
     return self.children
diff --git a/ppapi/generators/idl_option.py b/ppapi/generators/idl_option.py
index 9b87d429..5d75c28f 100644
--- a/ppapi/generators/idl_option.py
+++ b/ppapi/generators/idl_option.py
@@ -55,7 +55,7 @@
   else:
     out = '   -%-15.15s\t%s' % (option.name, option.desc)
   if option.default:
-    out = '    %-15.15s\t%s\n\t\tDefault: %s\n' % (' ', out, option.default)
+    out = '%s\n\t\t\t(Default: %s)\n' % (out, option.default)
   InfoOut.Log(out)
 
 def DumpHelp(option=None):
@@ -107,3 +107,4 @@
     sys.exit(-1)
 
   return filenames
+
diff --git a/ppapi/generators/idl_outfile.py b/ppapi/generators/idl_outfile.py
index e25c3cad..fd00c18 100644
--- a/ppapi/generators/idl_outfile.py
+++ b/ppapi/generators/idl_outfile.py
@@ -11,6 +11,7 @@
 import sys
 
 from idl_log import ErrOut, InfoOut, WarnOut
+from idl_option import GetOption, Option, ParseOptions
 from stat import *
 
 #
@@ -51,7 +52,8 @@
         intext = None
 
       if intext == outtext:
-        InfoOut.Log('Output %s unchanged.' % self.filename)
+        if GetOption('verbose'):
+          InfoOut.Log('Output %s unchanged.' % self.filename)
         return False
 
     try:
diff --git a/ppapi/generators/idl_parser.py b/ppapi/generators/idl_parser.py
index 0ee2200..a8a8ef2 100644
--- a/ppapi/generators/idl_parser.py
+++ b/ppapi/generators/idl_parser.py
@@ -28,6 +28,7 @@
 import os.path
 import re
 import sys
+import time
 
 from idl_ast import IDLAst
 from idl_log import ErrOut, InfoOut, WarnOut
@@ -42,7 +43,7 @@
 Option('parse_debug', 'Debug parse reduction steps.')
 Option('token_debug', 'Debug token generation.')
 Option('dump_tree', 'Dump the tree.')
-Option('srcroot', 'Working directory.', default='')
+Option('srcroot', 'Working directory.', default='../api')
 Option('wcomment', 'Disable warning for missing comment.')
 Option('wenum', 'Disable warning for missing enum value.')
 
@@ -814,6 +815,7 @@
 # Loads a new file into the lexer and attemps to parse it.
 #
   def ParseFile(self, filename):
+    date = time.ctime(os.path.getmtime(filename))
     data = open(filename).read()
     if self.verbose:
       InfoOut.Log("Parsing %s" % filename)
@@ -824,7 +826,9 @@
       srcroot = GetOption('srcroot')
       if srcroot and filename.find(srcroot) == 0:
         filename = filename[len(srcroot) + 1:]
-      return IDLFile(filename, out, self.parse_errors + self.lex_errors)
+      filenode = IDLFile(filename, out, self.parse_errors + self.lex_errors)
+      filenode.SetProperty('DATETIME', date)
+      return filenode
 
     except Exception as e:
       ErrOut.LogLine(filename, self.last.lineno, self.last.lexpos,
@@ -986,12 +990,19 @@
     InfoOut.Log("Passed namespace test.")
   return errs
 
-
+default_dirs = ['.', 'trusted']
 def ParseFiles(filenames):
   parser = IDLParser()
   filenodes = []
   errors = 0
 
+  if not filenames:
+    filenames = []
+    srcroot = GetOption('srcroot')
+    for dir in default_dirs:
+      srcdir = os.path.join(srcroot, dir, '*.idl')
+      filenames += sorted(glob.glob(gendir))
+
   for filename in filenames:
     filenode = parser.ParseFile(filename)
     filenodes.append(filenode)
diff --git a/ppapi/generators/test_cgen/enum_typedef.idl b/ppapi/generators/test_cgen/enum_typedef.idl
index cb47402..45f1472 100644
--- a/ppapi/generators/test_cgen/enum_typedef.idl
+++ b/ppapi/generators/test_cgen/enum_typedef.idl
@@ -6,7 +6,7 @@
 
 /* This file will test that the IDL snippet matches the comment */
 
-/* enum et1 { A = 1, B = 2, C = 3 }; */
+/* typedef enum { A = 1, B = 2, C = 3 } et1; */
 enum et1 { A=1, B=2, C=3 };
 
 /* typedef int32_t i; */
@@ -25,10 +25,10 @@
 typedef et1[4] et4;
 
 /*
-typedef int8_t (*PPB_Audio_Callback)(const void* sample_buffer,
-                                     uint32_t buffer_size_in_bytes,
-                                     const void* user_data);
-*/
+ * typedef int8_t (*PPB_Audio_Callback)(const void* sample_buffer,
+ *                                   uint32_t buffer_size_in_bytes,
+ *                                   const void* user_data);
+ */
 typedef int8_t PPB_Audio_Callback([in] mem_t sample_buffer,
                                   [in] uint32_t buffer_size_in_bytes,
                                   [in] mem_t user_data);
diff --git a/ppapi/generators/test_cgen/interface.idl b/ppapi/generators/test_cgen/interface.idl
index 5eec136e..0992614 100644
--- a/ppapi/generators/test_cgen/interface.idl
+++ b/ppapi/generators/test_cgen/interface.idl
@@ -11,12 +11,13 @@
   mem_t X;
 };
 
-/* struct iface1 { 
-  int8_t (*mem1)(int16_t x, int32_t y);
-  int32_t (*mem2)(const struct ist* a);
-  int32_t (*mem3)(struct ist* b);
-   
-}; */
+/*
+ * struct iface1 { 
+ * int8_t (*mem1)(int16_t x, int32_t y);
+ * int32_t (*mem2)(const struct ist* a);
+ * int32_t (*mem3)(struct ist* b);
+ * };
+ */
 interface iface1 {
   int8_t mem1([in] int16_t x, [in] int32_t y);
   int32_t mem2([in] ist a);
diff --git a/ppapi/generators/test_cgen/structs.idl b/ppapi/generators/test_cgen/structs.idl
index 486473c..e942f058 100644
--- a/ppapi/generators/test_cgen/structs.idl
+++ b/ppapi/generators/test_cgen/structs.idl
@@ -9,7 +9,7 @@
 /* typedef uint8_t s_array[3]; */
 typedef uint8_t[3] s_array;
 
-/* enum senum { esv1 = 1, esv2 = 2 }; */
+/* typedef enum { esv1 = 1, esv2 = 2 } senum; */
 enum senum {
   esv1=1,
   esv2=2
@@ -26,16 +26,18 @@
   s_array[640][480] pixels;
 };
 
-/* typedef float (*func_t)(s_array data); */
+/* typedef float (*func_t)(const s_array data); */
 typedef float_t func_t([in] s_array data); 
 
 /* typedef func_t (*findfunc_t)(const char* x); */
 typedef func_t findfunc_t([in] str_t x);
 
-/* struct sfoo {
-  s_array screen[480][640];
-  findfunc_t myfunc;
-}; */
+/*
+ * struct sfoo {
+ *  s_array screen[480][640];
+ *  findfunc_t myfunc;
+ * };
+ */
 struct sfoo {
   s_array[480][640] screen;
   findfunc_t myfunc;