diff options
Diffstat (limited to 'ppapi/generators')
-rw-r--r-- | ppapi/generators/idl_diff.py | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/ppapi/generators/idl_diff.py b/ppapi/generators/idl_diff.py new file mode 100644 index 0000000..d005a20 --- /dev/null +++ b/ppapi/generators/idl_diff.py @@ -0,0 +1,191 @@ +#!/usr/bin/python +# +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import glob +import os +import subprocess +import sys + +from idl_option import GetOption, Option, ParseOptions + +# +# IDLDiff +# +# IDLDiff is a tool for comparing sets of IDL generated header files +# with the standard checked in headers. It does this by capturing the +# output of the standard diff tool, parsing it into separate changes, then +# ignoring changes that are know to be safe, such as adding or removing +# blank lines, etc... +# + +Option('gen', 'IDL generated files', default='hdir') +Option('src', 'Original ".h" files', default='../c') + +# 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 Dump(self): + if not self.was: + print 'Adding %s' % self.mode + elif not self.now: + print 'Missing %s' % self.mode + else: + print 'Modifying %s' % self.mode + + for line in self.was: + print 'src: %s' % line + for line in self.now: + print 'gen: %s' % line + print + +# +# IsCopyright +# +# Return True if this change is only a one line change in the copyright notice +# 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 + return True + +# +# IsBlank +# +# Return True if this change only adds or removes blank lines +# +def IsBlank(change): + for line in change['now']: + if line: return False + for line in change['was']: + if line: return False + return True + + +# +# IsSpacing +# +# Return True if this change is only different in the way 'words' are spaced +# such as in an enum: +# ENUM_XXX = 1, +# ENUM_XYY_Y = 2, +# vs +# ENUM_XXX = 1, +# ENUM_XYY_Y = 2, +# +def IsSpacing(change): + 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] + offs = line.find('//') + if offs == -1: + offs = line.find('/*') + if offs >-1: + line = line[:offs-1] + + words1 = change['now'][i].split() + words2 = line.split() + if words1 != words2: return False + return True + +# +# IsInclude +# +# Return True if change has extra includes +# +def IsInclude(change): + if len(change['was']): return False + for line in change['now']: + if line and '#include' not in line: return False + return True + +# +# IsCppComment +# +# Return True if the change is only missing C++ comments +# +def IsCppComment(change): + if len(change['now']): return False + for line in change['was']: + line = line.strip() + if line[:2] != '//': return False + return True +# +# ValidChange +# +# Return True if none of the changes does not patch an above "bogus" change. +# +def ValidChange(change): + if IsCopyright(change): return False + if IsBlank(change): return False + if IsSpacing(change): return False + if IsInclude(change): return False + if IsCppComment(change): return False + return True + + + +# +# GetChanges +# +# Parse the output into discrete change blocks. +# +def GetChanges(output): + lines = output.split('\n') + changes = [] + change = { 'mode' : None, + 'was': [], + 'now': [] + } + for line in lines: + if not line: continue + elif line[0] == '<': + change['was'].append(line[2:]) + elif line[0] == '>': + change['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 + +def Main(args): + filenames = ParseOptions(args) + if not filenames: + gendir = os.path.join(GetOption('gen'), '*.h') + filenames = glob.glob(gendir) + + for filename in filenames: + gen = filename + filename = filename[len(GetOption('gen')) + 1:] + src = os.path.join(GetOption('src'), filename) + p = subprocess.Popen(['diff', src, gen], stdout=subprocess.PIPE) + output, errors = p.communicate() + + changes = GetChanges(output) + if changes: + print "\n\nDelta between:\n src=%s\n gen=%s\n" % (src, gen) + for change in GetChanges(output): + change.Dump() + else: + print "\nSAME:\n src=%s\n gen=%s\n" % (src, gen) + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) + |