Add namespace header test for all public headers.

* Extend header test to include all public headers, not just the ones in
  the same directory as the module CMakeLists.txt
* Fix/exclude headers and modules that could not trivially be
  patched to fix HeaderTest errors
  * Add `EXCLUDE_HEADER_TEST` option to vtk_module_add_module
  * Add finer-grained control to only exclude some header test checks
    not all.
* Add header test that verifies VTK_ABI_NAMESPACE_{BEGIN,END}
  exist in headers with visible symbols and have matching pairs.
HTGUnlimitedGradien
Ryan Krattiger 6 months ago
parent 65fc526a83
commit 4befee8263
  1. 6
      Accelerators/Vtkm/Core/vtkmConfigCore.h.in
  2. 28
      CMake/vtkModule.cmake
  3. 1
      Common/Core/SMP/Common/vtkSMPThreadLocalAPI.h
  4. 1
      Common/Core/SMP/Common/vtkSMPThreadLocalImplAbstract.h
  5. 1
      Common/Core/SMP/Common/vtkSMPToolsAPI.h
  6. 1
      Common/Core/SMP/Common/vtkSMPToolsImpl.h
  7. 1
      Common/Core/SMP/Common/vtkSMPToolsInternal.h
  8. 1
      Common/Core/SMP/OpenMP/vtkSMPThreadLocalBackend.h
  9. 1
      Common/Core/SMP/OpenMP/vtkSMPThreadLocalImpl.h
  10. 1
      Common/Core/SMP/STDThread/vtkSMPThreadLocalBackend.h
  11. 1
      Common/Core/SMP/STDThread/vtkSMPThreadLocalImpl.h
  12. 1
      Common/Core/SMP/STDThread/vtkSMPThreadPool.h
  13. 1
      Common/Core/SMP/Sequential/vtkSMPThreadLocalImpl.h
  14. 1
      GUISupport/Qt/QVTKInteractor.h
  15. 1
      GUISupport/Qt/QVTKInteractorAdapter.h
  16. 3
      IO/PIO/CMakeLists.txt
  17. 3
      Rendering/External/CMakeLists.txt
  18. 1
      Rendering/VR/vtkVRCamera.h
  19. 227
      Testing/Core/HeaderTesting.py
  20. 3
      Utilities/DICOMParser/CMakeLists.txt
  21. 3
      Utilities/octree/CMakeLists.txt
  22. 1
      Wrapping/PythonCore/PyVTKObject.h
  23. 1
      Wrapping/PythonCore/PyVTKSpecialObject.h
  24. 1
      Wrapping/PythonCore/PyVTKTemplate.h

@ -12,8 +12,8 @@
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#ifndef vtkConfigCore_h
#define vtkConfigCore_h
#ifndef vtkmConfigCore_h
#define vtkmConfigCore_h
/*--------------------------------------------------------------------------*/
/* Other Configuration Options */
@ -52,4 +52,4 @@
#endif
#endif // vtkConfigCore_h
#endif // vtkmConfigCore_h

@ -3502,7 +3502,7 @@ function (vtk_module_add_module name)
endforeach ()
cmake_parse_arguments(PARSE_ARGV 1 _vtk_add_module
"FORCE_STATIC;HEADER_ONLY;HEADER_DIRECTORIES"
"FORCE_STATIC;HEADER_ONLY;HEADER_DIRECTORIES;EXCLUDE_HEADER_TEST"
"EXPORT_MACRO_PREFIX;HEADERS_SUBDIR;LIBRARY_NAME_SUFFIX"
"${_vtk_add_module_source_keywords};SOURCES;NOWRAP_CLASSES;NOWRAP_TEMPLATE_CLASSES;NOWRAP_HEADERS")
@ -3898,7 +3898,10 @@ function (vtk_module_add_module name)
_vtk_module_write_wrap_hierarchy()
endif ()
set(_vtk_add_module_module_content)
# Make sure this file is excluded from the header tests
set(_vtk_add_module_module_content "
/* VTK-HeaderTest-Exclude: ${_vtk_add_module_library_name}Module.h */
")
if (NOT _vtk_add_module_AUTOINIT_INCLUDE)
get_property(_vtk_add_module_AUTOINIT_INCLUDE GLOBAL
@ -4056,13 +4059,20 @@ function (_vtk_module_add_header_tests)
return ()
endif ()
add_test(
NAME "${_vtk_build_module}-HeaderTest"
COMMAND "${Python${VTK_PYTHON_VERSION}_EXECUTABLE}"
# TODO: What to do when using this from a VTK install?
"${VTK_SOURCE_DIR}/Testing/Core/HeaderTesting.py"
"${CMAKE_CURRENT_SOURCE_DIR}"
"${_vtk_add_module_EXPORT_MACRO}")
if (NOT _vtk_add_module_EXCLUDE_HEADER_TEST)
add_test(
NAME "${_vtk_build_module}-HeaderTest"
COMMAND "${Python${VTK_PYTHON_VERSION}_EXECUTABLE}"
# TODO: What to do when using this from a VTK install?
"${VTK_SOURCE_DIR}/Testing/Core/HeaderTesting.py"
"${CMAKE_CURRENT_SOURCE_DIR}"
"--export-macro"
"${_vtk_add_module_EXPORT_MACRO}"
"--headers"
"${_vtk_add_module_HEADERS}"
"${_vtk_add_module_NOWRAP_HEADERS}"
"${_vtk_add_module_TEMPLATES}")
endif ()
endfunction ()
#[==[

@ -221,3 +221,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadLocalAPI.h */

@ -78,3 +78,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadLocalImplAbstract.h */

@ -304,3 +304,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPToolsAPI.h */

@ -117,3 +117,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPToolsImpl.h */

@ -110,3 +110,4 @@ VTK_ABI_NAMESPACE_END
#endif // DOXYGEN_SHOULD_SKIP_THIS
#endif
/* VTK-HeaderTest-Exclude: vtkSMPToolsInternal.h */

@ -185,3 +185,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadLocalBackend.h */

@ -133,3 +133,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadLocalImpl.h */

@ -186,3 +186,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: INCLUDES:CLASSES */

@ -133,3 +133,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadLocalImpl.h */

@ -67,3 +67,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadPool.h */

@ -177,3 +177,4 @@ VTK_ABI_NAMESPACE_END
} // namespace vtk
#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadLocalImpl.h */

@ -137,3 +137,4 @@ private:
VTK_ABI_NAMESPACE_END
#endif
// VTK-HeaderTest-Exclude: QVTKInteractor.h

@ -79,3 +79,4 @@ protected:
VTK_ABI_NAMESPACE_END
#endif
// VTK-HeaderTest-Exclude: QVTKInteractorAdapter.h

@ -5,4 +5,5 @@ set(classes
vtkPIOReader)
vtk_module_add_module(VTK::IOPIO
CLASSES ${classes})
CLASSES ${classes}
EXCLUDE_HEADER_TEST)

@ -6,7 +6,8 @@ set(classes
vtkExternalOpenGLRenderer)
vtk_module_add_module(VTK::RenderingExternal
CLASSES ${classes})
CLASSES ${classes}
EXCLUDE_HEADER_TEST)
vtk_module_definitions(VTK::RenderingExternal
PRIVATE
VTK_OPENGL2)

@ -101,3 +101,4 @@ private:
VTK_ABI_NAMESPACE_END
#endif
/* VTK-HeaderTest-Exclude: vtkVRCamera.h */

@ -32,6 +32,7 @@ import sys
import re
import os
import stat
import argparse as cli
# Get the path to the directory containing this script.
if __name__ == '__main__':
@ -113,6 +114,13 @@ class TestVTKFiles:
self.FileLines = []
self.ClassName = ""
self.ParentName = ""
self.HasIncludes = False
self.HasTypedef = False
self.HasClass = False
self.HasFunction = False
classre = "^class(\s+VTK_DEPRECATED)?(\s+[^\s]*_EXPORT)?\s+(vtkm?[A-Z0-9_][^ :\n]*)\s*:\s*public\s+(vtk[^ \n\{]*)"
regx = re.compile(classre)
try:
if sys.hexversion >= 0x03000000:
file = open(filename, encoding='ascii', errors='ignore')
@ -120,6 +128,19 @@ class TestVTKFiles:
file = open(filename)
self.FileLines = file.readlines()
file.close()
funcre = "[a-zA-Z0-9_]*\s+[a-zA-Z0-9_]*\s*\([^\n\{]*\);"
funcregex = re.compile(funcre)
for l in self.FileLines:
line = l.strip()
if "#include" == line[:8]:
self.HasIncludes = True
if regx.match(line):
self.HasClass = True
if "typedef" == line[:7] or "using" == line[:5]:
self.HasTypedef = True
if funcregex.match(line):
self.HasFunction = True
except:
self.Print("Problem reading file %s:\n%s" %
(filename, str(sys.exc_info()[1])))
@ -127,28 +148,74 @@ class TestVTKFiles:
return not self.CheckExclude()
def CheckExclude(self):
self.ExcludeNamespaceCheck = False
prefix = '// VTK-HeaderTest-Exclude:'
prefix_c = '/* VTK-HeaderTest-Exclude:'
suffix_c = ' */'
exclude = 0
for l in self.FileLines:
e = None
if l.startswith(prefix):
e = l[len(prefix):].strip()
if e == os.path.basename(self.FileName):
exclude += 1
else:
self.Error("Wrong exclusion: "+l.rstrip())
elif l.startswith(prefix_c) and l.rstrip().endswith(suffix_c):
e = l[len(prefix_c):-len(suffix_c)].strip()
if not e == None:
if e == os.path.basename(self.FileName):
exclude += 1
else:
self.Error("Wrong exclusion: "+l.rstrip())
hasExclusions = False
if "INCLUDES" in e:
hasExclusions = True
self.HasIncludes = False
if "ABINAMESPACE" in e:
hasExclusions = True
self.ExcludeNamespaceCheck = True
if "CLASSES" in e:
hasExclusions = True
self.HasClass = False
if not hasExclusions:
self.Error("Wrong exclusion: "+l.rstrip())
if exclude > 1:
self.Error("Multiple VTK-HeaderTest-Exclude lines")
return exclude > 0
def CheckABINamespace(self):
if self.ExcludeNamespaceCheck:
return
if not self.HasClass and not self.HasFunction:
return
# Note: This check does not ensure the ABI namespace is not nested
# in/around anonymous namespaces or that it is inside of named
# namespaces. These checks may be good to add later.
open_namespace = 'VTK_ABI_NAMESPACE_BEGIN'
close_namespace = 'VTK_ABI_NAMESPACE_END'
is_open = False
has_abi_namespace = False
for l in self.FileLines:
if l.startswith(open_namespace):
if is_open:
self.Error('Nested ABI namespace is not allowed.')
is_open = True
has_abi_namespace = True
if l.startswith(close_namespace):
if not is_open:
self.Error('Mismatched ABI namespace macros.')
is_open = False
if not has_abi_namespace:
self.Print( "File: %s has no ABI namespace: " % self.FileName )
self.Error('Missing VTK ABI namespace macros')
if is_open:
self.Print( "File: %s does not close an ABI namespace: " % self.FileName )
self.Error('Missing VTK_ABI_NAMESPACE_END.')
pass
def CheckIncludes(self):
if not self.HasIncludes or not self.HasClass:
return
count = 0
lines = []
nplines = []
@ -159,12 +226,26 @@ class TestVTKFiles:
regx1 = re.compile(ignincludere)
cc = 0
includeparent = 0
stdIncludes = {'any', 'bitset', 'chrono', 'csetjmp', 'csignal',
'cstddef', 'ctime', 'functional', 'initializer_list',
'tuple', 'type_traits', 'typeindex', 'typeinfo', 'utility', 'memory',
'new', 'scoped_allocator', 'cfloat', 'cinttypes', 'climits', 'limits',
'cstdint', 'cassert', 'cerrno', 'exception', 'stdexcept',
'system_error', 'cctype', 'cuchar', 'cwchar', 'cwctyp',
'string', 'array', 'deque', 'forward_list', 'list', 'map', 'queue',
'set', 'stack', 'unordered_map', 'unordered_set', 'vector',
'iterator', 'algorithm', 'cfenv', 'cmath', 'complex', 'numeric',
'random', 'ratio', 'valarray', 'clocale', 'codecvt', 'locale',
'ostream', 'istream', 'thread', 'mutex', 'future',
'condition_variable'}
for a in self.FileLines:
line = a.strip()
rm = regx.match(line)
if rm and not regx1.match(line):
lines.append(" %4d: %s" % (cc, line))
file = rm.group(1)
if file in stdIncludes:
continue
lines.append(" %4d: %s" % (cc, line))
if file == (self.ParentName + ".h"):
includeparent = 1
if not StringEndsWith(file, ".h"):
@ -225,12 +306,15 @@ class TestVTKFiles:
elif not guard == guard_set:
self.Print("File: %s is not guarded properly." % self.FileName)
self.Error("Guard does is not set properly")
elif not ('%s.h' % guard) == os.path.basename(self.FileName):
elif not os.path.basename(self.FileName) in ('%s.h' % guard):
self.Print("File: %s has a guard (%s) which does not match its filename." % (self.FileName, guard))
self.Error("Guard does not match the filename")
def CheckParent(self):
classre = "^class(\s+VTK_DEPRECATED)?(\s+[^\s]*_EXPORT)?\s+(vtkm?[A-Z0-9_][^ :\n]*)\s*:\s*public\s+(vtk[^ \n\{]*)"
if not self.HasClass:
return
classre = "^class(\s+VTK_DEPRECATED)?(\s+[^\s]*_EXPORT)?\s+(vtkm?[A-Z0-9_][^ :\n]*)\s*<?[^\n\{]*>?\s*:\s*public\s+(vtk[^ \n\{<>]*)<?[^\n\{]*>?"
cname = ""
pname = ""
classlines = []
@ -276,7 +360,11 @@ class TestVTKFiles:
self.ClassName = cname
self.ParentName = pname
pass
def CheckTypeMacro(self):
if not self.HasClass:
return
count = 0
lines = []
oldlines = []
@ -334,6 +422,7 @@ class TestVTKFiles:
(self.ClassName, self.ParentName))
self.Error("No type macro")
pass
def CheckForCopyAndAssignment(self):
if not self.ClassName:
return
@ -389,7 +478,11 @@ class TestVTKFiles:
self.FileName )
self.Error("Multiple assignment operators")
pass
def CheckWeirdConstructors(self):
if not self.HasClass:
return
count = 0
lines = []
oldlines = []
@ -462,42 +555,105 @@ class TestVTKFiles:
pass
##
test = TestVTKFiles()
parser = cli.ArgumentParser()
parser.add_argument(
"root",
metavar="<root>",
help="Root directory to glob headers from.")
parser.add_argument(
"--headers",
metavar="<header>.h",
nargs='*',
help="List of headers to test. Relative from \
<root> if not absolute paths.")
parser.add_argument(
"--extra-headers",
metavar="<header>.h",
nargs='*',
help="List of headers to test. Relative from "
"--root or PWD if not absolute paths.")
parser.add_argument(
"--exceptions",
nargs='*',
metavar="<header>.h",
help="List of headers not to check "
"(used when --headers is not specified).")
parser.add_argument(
"--export-macro",
metavar="VTK_<ModuleName>_EXPORT",
help="Export macro.")
args = parser.parse_args()
## Check command line arguments
if len(sys.argv) < 2:
# Check command line arguments
if not args.root:
print("Testing directory not specified...")
print("Usage: %s <directory> [ exception(s) ]" % sys.argv[0])
parser.print_usage()
sys.exit(1)
# Make sure the root exsits
if not os.path.exists(args.root):
print("Root path does not exists: %s" % (args.root))
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":
test = TestVTKFiles()
print(args)
dirname = os.path.abspath(args.root)
exceptions = []
if args.exceptions:
exceptions.extend(args.exceptions)
export = args.export_macro
# Extract the headers to check
headers = []
if args.headers:
for header in args.headers:
headers.extend(header.split(';'))
if args.extra_headers:
for header in args.extra_headers:
headers.extend(header.split(';'))
if export and 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 non-vtk files
if not a.startswith('vtk'):
continue
## Skip exceptions
if a in exceptions:
continue
pathname = '%s/%s' % (dirname, a)
if pathname in exceptions:
if args.headers is None:
## Traverse through the list of files
for a in os.listdir(dirname):
## Skip non-header files
if not StringEndsWith(a, ".h"):
continue
## Skip non-vtk files
if not a.startswith('vtk'):
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
headers.append(pathname)
for header in headers:
pathname = header
# If the path is not absolute, it is relative to the test directory
if not os.path.isabs(pathname):
pathname = '%s/%s' % (dirname, header)
## Skip non-existing
if not os.path.exists(pathname):
test.Warning("Header not found: %s" % (pathname))
continue
mode = os.stat(pathname)[stat.ST_MODE]
## Skip directories
if stat.S_ISDIR(mode):
## Skip .txx/.hxx/.cxx/etc. files that may be listed.
elif not StringEndsWith(pathname, ".h"):
continue
elif stat.S_ISREG(mode) and test.TestFile(pathname):
elif test.TestFile(pathname):
## Do all the tests
test.CheckGuard()
test.CheckParent()
@ -507,6 +663,7 @@ for a in os.listdir(dirname):
test.CheckWeirdConstructors()
test.CheckPrintSelf()
test.CheckWindowsMangling()
test.CheckABINamespace()
## Summarize errors
test.PrintWarnings()

@ -29,7 +29,8 @@ unset(CMAKE_VISIBILITY_INLINES_HIDDEN)
vtk_module_add_module(VTK::DICOMParser
SOURCES ${sources}
HEADERS ${headers})
HEADERS ${headers}
EXCLUDE_HEADER_TEST)
vtk_module_compile_features(VTK::DICOMParser
PUBLIC
cxx_std_11)

@ -12,4 +12,5 @@ vtk_module_add_module(VTK::octree
TEMPLATE_CLASSES ${template_classes}
HEADERS ${headers}
HEADERS_SUBDIR "octree"
HEADER_ONLY)
HEADER_ONLY
EXCLUDE_HEADER_TEST)

@ -116,3 +116,4 @@ extern "C"
}
#endif
/* VTK-HeaderTest-Exclude: PyVTKObject.h */

@ -89,3 +89,4 @@ extern "C"
}
#endif
/* VTK-HeaderTest-Exclude: PyVTKSpecialObject.h */

@ -42,3 +42,4 @@ extern "C"
}
#endif
/* VTK-HeaderTest-Exclude: PyVTKTemplate.h */

Loading…
Cancel
Save