You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
16 KiB
454 lines
16 KiB
2 years ago
|
#!/usr/bin/env python
|
||
|
|
||
|
## /*=========================================================================
|
||
|
|
||
|
## Program: Visualization Toolkit
|
||
|
## Module: $RCSfile: HeaderTesting.py,v $
|
||
|
|
||
|
## Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
||
|
## All rights reserved.
|
||
|
## See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
||
|
|
||
|
## This software is distributed WITHOUT ANY WARRANTY; without even
|
||
|
## the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||
|
## PURPOSE. See the above copyright notice for more information.
|
||
|
|
||
|
## =========================================================================*/
|
||
|
## .NAME HeaderTesting - a VTK style and validity checking utility
|
||
|
## .SECTION Description
|
||
|
## HeaderTesting is a script which checks the list of header files for
|
||
|
## validity based on VTK coding standard. It checks for proper super
|
||
|
## classes, number and style of include files, type macro, private
|
||
|
## copy constructor and assignment operator, broken constructors, and
|
||
|
## exsistence of PrintSelf method. This script should be run as a part
|
||
|
## of the dashboard checking of the Visualization Toolkit and related
|
||
|
## projects.
|
||
|
|
||
|
## .SECTION See Also
|
||
|
## http://www.vtk.org http://public.kitware.com/Dart/HTML/Index.shtml
|
||
|
## http://www.vtk.org/contribute.php#coding-standards
|
||
|
|
||
|
import sys
|
||
|
import re
|
||
|
import os
|
||
|
import stat
|
||
|
import string
|
||
|
|
||
|
# Get the path to the directory containing this script.
|
||
|
if __name__ == '__main__':
|
||
|
selfpath = os.path.abspath(sys.path[0] or os.curdir)
|
||
|
else:
|
||
|
selfpath = os.path.abspath(os.path.dirname(__file__))
|
||
|
|
||
|
# Load the list of names mangled by windows.h.
|
||
|
execfile(os.path.join(selfpath, 'WindowsMangleList.py'))
|
||
|
|
||
|
## If tested from dart, make sure to fix all the output strings
|
||
|
test_from_dart = 0
|
||
|
if os.environ.has_key("DART_TEST_FROM_DART"):
|
||
|
test_from_dart = 1
|
||
|
|
||
|
## For backward compatibility
|
||
|
def StringEndsWith(str1, str2):
|
||
|
l1 = len(str1)
|
||
|
l2 = len(str2)
|
||
|
if l1 < l2:
|
||
|
return 0
|
||
|
return (str1[(l1-l2):] == str2)
|
||
|
|
||
|
##
|
||
|
class TestVTKFiles:
|
||
|
def __init__(self):
|
||
|
self.FileName = ""
|
||
|
self.ErrorValue = 0;
|
||
|
self.Errors = {}
|
||
|
self.WarningValue = 0;
|
||
|
self.Warnings = {}
|
||
|
self.FileLines = []
|
||
|
self.Export = ""
|
||
|
self.UnnecessaryIncludes = [
|
||
|
"stdio.h",
|
||
|
"stdlib.h",
|
||
|
"string.h",
|
||
|
"iostream",
|
||
|
"iostream.h",
|
||
|
"strstream",
|
||
|
"strstream.h",
|
||
|
"fstream",
|
||
|
"fstream.h",
|
||
|
"windows.h"
|
||
|
]
|
||
|
pass
|
||
|
def SetExport(self, export):
|
||
|
self.Export = export
|
||
|
def Print(self, text=""):
|
||
|
rtext = text
|
||
|
if test_from_dart:
|
||
|
rtext = string.replace(rtext, "<", "<")
|
||
|
rtext = string.replace(rtext, ">", ">")
|
||
|
print rtext
|
||
|
def Error(self, error):
|
||
|
self.ErrorValue = 1
|
||
|
self.Errors[error] = 1
|
||
|
pass
|
||
|
def Warning(self, warning):
|
||
|
self.WarningValue = 1
|
||
|
self.Warnings[warning] = 1
|
||
|
pass
|
||
|
def PrintErrors(self):
|
||
|
if self.ErrorValue:
|
||
|
self.Print( )
|
||
|
self.Print( "There were errors:" )
|
||
|
for a in self.Errors.keys():
|
||
|
self.Print( "* %s" % a )
|
||
|
def PrintWarnings(self):
|
||
|
if self.WarningValue:
|
||
|
self.Print( )
|
||
|
self.Print( "There were warnings:" )
|
||
|
for a in self.Warnings.keys():
|
||
|
self.Print( "* %s" % a )
|
||
|
|
||
|
def TestFile(self, filename):
|
||
|
self.FileName = filename
|
||
|
self.FileLines = []
|
||
|
self.ClassName = ""
|
||
|
self.ParentName = ""
|
||
|
try:
|
||
|
file = open(filename)
|
||
|
self.FileLines = file.readlines()
|
||
|
file.close()
|
||
|
except:
|
||
|
self.Print( "Problem reading file: %s" % filename )
|
||
|
sys.exit(1)
|
||
|
pass
|
||
|
|
||
|
def CheckIncludes(self):
|
||
|
count = 0
|
||
|
lines = []
|
||
|
nplines = []
|
||
|
unlines = []
|
||
|
includere = "^\s*#\s*include\s*[\"<]([^>\"]+)"
|
||
|
ignincludere = ".*\/\/.*"
|
||
|
regx = re.compile(includere)
|
||
|
regx1 = re.compile(ignincludere)
|
||
|
cc = 0
|
||
|
includeparent = 0
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
rm = regx.match(line)
|
||
|
if rm and not regx1.match(line):
|
||
|
lines.append(" %4d: %s" % (cc, line))
|
||
|
file = rm.group(1)
|
||
|
if file == (self.ParentName + ".h"):
|
||
|
includeparent = 1
|
||
|
if not StringEndsWith(file, ".h"):
|
||
|
nplines.append(" %4d: %s" % (cc, line))
|
||
|
if file in self.UnnecessaryIncludes:
|
||
|
unlines.append(" %4d: %s" % (cc, line))
|
||
|
cc = cc + 1
|
||
|
if len(lines) > 1:
|
||
|
self.Print()
|
||
|
self.Print( "File: %s has %d includes: " %
|
||
|
( self.FileName, len(lines)) )
|
||
|
for a in lines:
|
||
|
self.Print( a )
|
||
|
self.Error("Multiple includes")
|
||
|
if len(nplines) > 0:
|
||
|
self.Print( )
|
||
|
self.Print( "File: %s has non-portable include(s): " % self.FileName )
|
||
|
for a in nplines:
|
||
|
self.Print( a )
|
||
|
self.Error("Non-portable includes")
|
||
|
if len(unlines) > 0:
|
||
|
self.Print( )
|
||
|
self.Print( "File: %s has unnecessary include(s): " % self.FileName )
|
||
|
for a in unlines:
|
||
|
self.Print( a )
|
||
|
self.Error("Unnecessary includes")
|
||
|
if not includeparent and self.ParentName:
|
||
|
self.Print()
|
||
|
self.Print( "File: %s does not include parent \"%s.h\"" %
|
||
|
( self.FileName, self.ParentName ) )
|
||
|
self.Error("Does not include parent")
|
||
|
pass
|
||
|
|
||
|
def CheckParent(self):
|
||
|
classre = "^class\s*(.*_EXPORT|\s*) (vtk[A-Z0-9_][^ :\n]*)\s*:\s*public\s*(vtk[^ \n\{]*)"
|
||
|
cname = ""
|
||
|
pname = ""
|
||
|
classlines = []
|
||
|
regx = re.compile(classre)
|
||
|
cc = 0
|
||
|
lastline = ""
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
rm = regx.match(line)
|
||
|
if not rm and not cname:
|
||
|
rm = regx.match(lastline + line)
|
||
|
if rm:
|
||
|
export = rm.group(1)
|
||
|
export = string.strip(export)
|
||
|
cname = rm.group(2)
|
||
|
pname = rm.group(3)
|
||
|
classlines.append(" %4d: %s" % (cc, line))
|
||
|
if not export:
|
||
|
self.Print("File: %s defines 1 class with no export macro:" % self.FileName)
|
||
|
self.Print(" %4d: %s" % (cc, line))
|
||
|
self.Error("No export macro")
|
||
|
elif self.Export and self.Export != export:
|
||
|
self.Print("File: %s defines 1 class with wrong export macro:" % self.FileName)
|
||
|
self.Print(" %4d: %s" % (cc, line))
|
||
|
self.Print(" The export macro should be: %s" % (self.Export))
|
||
|
self.Error("Wrong export macro")
|
||
|
cc = cc + 1
|
||
|
lastline = a
|
||
|
if len(classlines) > 1:
|
||
|
self.Print()
|
||
|
self.Print( "File: %s defines %d classes: " %
|
||
|
(self.FileName, len(classlines)) )
|
||
|
for a in classlines:
|
||
|
self.Print( a )
|
||
|
self.Error("Multiple classes defined")
|
||
|
if len(classlines) < 1:
|
||
|
self.Print()
|
||
|
self.Print( "File: %s does not define any classes" % self.FileName )
|
||
|
self.Error("No class defined")
|
||
|
return
|
||
|
#self.Print( "Classname: %s ParentName: %s" % (cname, pname)
|
||
|
self.ClassName = cname
|
||
|
self.ParentName = pname
|
||
|
pass
|
||
|
def CheckTypeMacro(self):
|
||
|
count = 0
|
||
|
lines = []
|
||
|
oldlines = []
|
||
|
typere = "^\s*vtkType(Revision)*Macro\s*\(\s*(vtk[^ ,]+)\s*,\s*(vtk[^ \)]+)\s*\)\s*;"
|
||
|
typesplitre = "^\s*vtkType(Revision)*Macro\s*\("
|
||
|
|
||
|
regx = re.compile(typere)
|
||
|
regxs = re.compile(typesplitre)
|
||
|
cc = 0
|
||
|
found = 0
|
||
|
for a in range(len(self.FileLines)):
|
||
|
line = string.strip(self.FileLines[a])
|
||
|
rm = regx.match(line)
|
||
|
if rm:
|
||
|
found = 1
|
||
|
if rm.group(1) != "Revision":
|
||
|
oldlines.append(" %4d: %s" % (cc, line))
|
||
|
cname = rm.group(2)
|
||
|
pname = rm.group(3)
|
||
|
if cname != self.ClassName or pname != self.ParentName:
|
||
|
lines.append(" %4d: %s" % (cc, line))
|
||
|
else:
|
||
|
# Maybe it is in two lines
|
||
|
rm = regxs.match(line)
|
||
|
if rm:
|
||
|
nline = line + " " + string.strip(self.FileLines[a+1])
|
||
|
line = string.strip(nline)
|
||
|
rm = regx.match(line)
|
||
|
if rm:
|
||
|
found = 1
|
||
|
if rm.group(1) != "Revision":
|
||
|
oldlines.append(" %4d: %s" % (cc, line))
|
||
|
cname = rm.group(2)
|
||
|
pname = rm.group(3)
|
||
|
if cname != self.ClassName or pname != self.ParentName:
|
||
|
lines.append(" %4d: %s" % (cc, line))
|
||
|
cc = cc + 1
|
||
|
if len(lines) > 0:
|
||
|
self.Print( "File: %s has broken type macro(s):" % self.FileName )
|
||
|
for a in lines:
|
||
|
self.Print( a )
|
||
|
self.Print( "Should be:\n vtkTypeRevisionMacro(%s, %s)" %
|
||
|
(self.ClassName, self.ParentName) )
|
||
|
self.Error("Broken type macro")
|
||
|
if len(oldlines) > 0:
|
||
|
self.Print( "File: %s has old type macro(s):" % self.FileName )
|
||
|
for a in oldlines:
|
||
|
self.Print( a )
|
||
|
self.Print( "Should be:\n vtkTypeRevisionMacro(%s, %s);" %
|
||
|
(self.ClassName, self.ParentName))
|
||
|
self.Error("Old style type macro")
|
||
|
if not found:
|
||
|
self.Print( "File: %s does not have type macro" % self.FileName )
|
||
|
self.Print( "Should be:\n vtkTypeRevisionMacro(%s, %s);" %
|
||
|
(self.ClassName, self.ParentName))
|
||
|
self.Error("No type macro")
|
||
|
pass
|
||
|
def CheckForCopyAndAssignment(self):
|
||
|
if not self.ClassName:
|
||
|
return
|
||
|
count = 0
|
||
|
lines = []
|
||
|
oldlines = []
|
||
|
copyoperator = "^\s*%s\s*\(\s*const\s*%s\s*&\s*\)\s*;\s*\/\/\s*Not\s*[iI]mplemented(\.)*" % ( self.ClassName, self.ClassName)
|
||
|
asgnoperator = "^\s*void\s*operator\s*=\s*\(\s*const\s*%s\s*&\s*\)\s*;\s*\/\/\s*Not\s*[iI]mplemented(\.)*" % self.ClassName
|
||
|
#self.Print( copyoperator
|
||
|
regx1 = re.compile(copyoperator)
|
||
|
regx2 = re.compile(asgnoperator)
|
||
|
foundcopy = 0
|
||
|
foundasgn = 0
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
if regx1.match(line):
|
||
|
foundcopy = foundcopy + 1
|
||
|
if regx2.match(line):
|
||
|
foundasgn = foundasgn + 1
|
||
|
lastline = ""
|
||
|
if foundcopy < 1:
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
if regx1.match(lastline + line):
|
||
|
foundcopy = foundcopy + 1
|
||
|
lastline = a
|
||
|
lastline = ""
|
||
|
if foundasgn < 1:
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
if regx2.match(lastline + line):
|
||
|
foundasgn = foundasgn + 1
|
||
|
lastline = a
|
||
|
|
||
|
if foundcopy < 1:
|
||
|
self.Print( "File: %s does not define copy constructor" %
|
||
|
self.FileName )
|
||
|
self.Print( "Should be:\n%s(const %s&); // Not implemented" %
|
||
|
(self.ClassName, self.ClassName) )
|
||
|
self.Error("No private copy constructor")
|
||
|
if foundcopy > 1:
|
||
|
self.Print( "File: %s defines multiple copy constructors" %
|
||
|
self.FileName )
|
||
|
self.Error("Multiple copy constructor")
|
||
|
if foundasgn < 1:
|
||
|
self.Print( "File: %s does not define assignment operator" %
|
||
|
self.FileName )
|
||
|
self.Print( "Should be:\nvoid operator=(const %s&); // Not implemented"
|
||
|
% self.ClassName )
|
||
|
self.Error("No private assignment operator")
|
||
|
if foundcopy > 1:
|
||
|
self.Print( "File: %s defines multiple assignment operators" %
|
||
|
self.FileName )
|
||
|
self.Error("Multiple assignment operators")
|
||
|
pass
|
||
|
def CheckWeirdConstructors(self):
|
||
|
count = 0
|
||
|
lines = []
|
||
|
oldlines = []
|
||
|
constructor = "^\s*%s\s*\(([^ )]*)\)" % self.ClassName
|
||
|
copyoperator = "^\s*%s\s*\(\s*const\s*%s\s*&\s*\)\s*;\s*\/\/\s*Not\s*implemented(\.)*" % ( self.ClassName, self.ClassName)
|
||
|
regx1 = re.compile(constructor)
|
||
|
regx2 = re.compile(copyoperator)
|
||
|
cc = 0
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
rm = regx1.match(line)
|
||
|
if rm:
|
||
|
arg = string.strip(rm.group(1))
|
||
|
if arg and not regx2.match(line):
|
||
|
lines.append(" %4d: %s" % (cc, line))
|
||
|
cc = cc + 1
|
||
|
if len(lines) > 0:
|
||
|
self.Print( "File: %s has weird constructor(s):" % self.FileName )
|
||
|
for a in lines:
|
||
|
self.Print( a )
|
||
|
self.Print( "There should be only:\n %s();" % self.ClassName )
|
||
|
self.Error("Weird constructor")
|
||
|
pass
|
||
|
|
||
|
def CheckPrintSelf(self):
|
||
|
if not self.ClassName:
|
||
|
return
|
||
|
typere = "^\s*void\s*PrintSelf\s*\(\s*ostream\s*&\s*os*\s*,\s*vtkIndent\s*indent\s*\)"
|
||
|
newtypere = "^\s*virtual\s*void\s*PrintSelf\s*\(\s*ostream\s*&\s*os*\s*,\s*vtkIndent\s*indent\s*\)"
|
||
|
regx1 = re.compile(typere)
|
||
|
regx2 = re.compile(newtypere)
|
||
|
found = 0
|
||
|
oldstyle = 0
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
rm1 = regx1.match(line)
|
||
|
rm2 = regx2.match(line)
|
||
|
if rm1 or rm2:
|
||
|
found = 1
|
||
|
if rm1:
|
||
|
oldstyle = 1
|
||
|
if not found:
|
||
|
self.Print( "File: %s does not define PrintSelf method:" %
|
||
|
self.FileName )
|
||
|
self.Warning("No PrintSelf method")
|
||
|
pass
|
||
|
|
||
|
def CheckWindowsMangling(self):
|
||
|
lines = []
|
||
|
regx1 = WindowsMangleRegEx
|
||
|
regx2 = re.compile("^.*VTK_LEGACY.*$")
|
||
|
# This version will leave out comment lines but we probably do
|
||
|
# not want to refer to mangled (hopefully deprecated) methods
|
||
|
# in comments.
|
||
|
# regx2 = re.compile("^(\s*//|\s*\*|.*VTK_LEGACY).*$")
|
||
|
cc = 1
|
||
|
for a in self.FileLines:
|
||
|
line = string.strip(a)
|
||
|
rm = regx1.match(line)
|
||
|
if rm:
|
||
|
arg = string.strip(rm.group(1))
|
||
|
if arg and not regx2.match(line):
|
||
|
lines.append(" %4d: %s" % (cc, line))
|
||
|
cc = cc + 1
|
||
|
if len(lines) > 0:
|
||
|
self.Print( "File: %s has windows.h mangling violations:" % self.FileName )
|
||
|
for a in lines:
|
||
|
self.Print(a)
|
||
|
self.Error("Windows Mangling Violation")
|
||
|
pass
|
||
|
|
||
|
##
|
||
|
test = TestVTKFiles()
|
||
|
|
||
|
## Check command line arguments
|
||
|
if len(sys.argv) < 2:
|
||
|
print "Testing directory not specified..."
|
||
|
print "Usage: %s <directory> [ exception(s) ]" % sys.argv[0]
|
||
|
sys.exit(1)
|
||
|
|
||
|
dirname = sys.argv[1]
|
||
|
exceptions = sys.argv[2:]
|
||
|
if len(sys.argv) > 2:
|
||
|
export = sys.argv[2]
|
||
|
if export[:3] == "VTK" and export[len(export)-len("EXPORT"):] == "EXPORT":
|
||
|
print "Use export macro: %s" % export
|
||
|
exceptions = sys.argv[3:]
|
||
|
test.SetExport(export)
|
||
|
|
||
|
## Traverse through the list of files
|
||
|
for a in os.listdir(dirname):
|
||
|
## Skip non-header files
|
||
|
if not StringEndsWith(a, ".h"):
|
||
|
continue
|
||
|
## Skip exceptions
|
||
|
if a in exceptions:
|
||
|
continue
|
||
|
pathname = '%s/%s' % (dirname, a)
|
||
|
if pathname in exceptions:
|
||
|
continue
|
||
|
mode = os.stat(pathname)[stat.ST_MODE]
|
||
|
## Skip directories
|
||
|
if stat.S_ISDIR(mode):
|
||
|
continue
|
||
|
elif stat.S_ISREG(mode):
|
||
|
## Do all the tests
|
||
|
test.TestFile(pathname)
|
||
|
test.CheckParent()
|
||
|
test.CheckIncludes()
|
||
|
test.CheckTypeMacro()
|
||
|
test.CheckForCopyAndAssignment()
|
||
|
test.CheckWeirdConstructors()
|
||
|
test.CheckPrintSelf()
|
||
|
test.CheckWindowsMangling()
|
||
|
|
||
|
## Summarize errors
|
||
|
test.PrintWarnings()
|
||
|
test.PrintErrors()
|
||
|
sys.exit(test.ErrorValue)
|