WhatModulesVTK.py 10.2 KB
Newer Older
1 2 3 4
#!/usr/bin/env python
import os, sys
import re

5 6
RENDERING_BACKENDS = ['OpenGL', 'OpenGL2']

7
def displayHelp():
8
    print """
Andrew Maclean's avatar
Andrew Maclean committed
9
Usage: WhatModulesVTK.py vtkSourceTree applicationFile|applicationFolder
10
  Generate a FindPackage(VTK COMPONENTS) that lists all modules
Andrew Maclean's avatar
Andrew Maclean committed
11 12 13 14 15
    referenced by a set of files.
  Additionally, two extra find_package( VTK COMPONENTS) lists of modules
  are produced. One is a minimal set and the other chases down all the
  dependencies to produce a maximal set of modules. This is done by
  parsing the module.cmake files.
16 17 18 19 20

    For example:
      Running from the VTK source,
        ./Utilities/Maintenance/WhatModulesVTK.py . Filters/Modeling/Testing/Cxx/TestRotationalExtrusion.cxx
      Produces
Andrew Maclean's avatar
Andrew Maclean committed
21 22 23
        Modules and their dependencies:
        find_package(VTK COMPONENTS
          vtkCommonComputationalGeometry
24
          vtkCommonCore
Andrew Maclean's avatar
Andrew Maclean committed
25 26 27 28 29 30
          vtkCommonDataModel
          vtkCommonExecutionModel
          vtkCommonMath
          vtkCommonMisc
          vtkCommonSystem
          vtkCommonTransforms
31
          vtkFiltersCore
Andrew Maclean's avatar
Andrew Maclean committed
32
          vtkFiltersGeneral
33 34
          vtkFiltersModeling
          vtkFiltersSources
Andrew Maclean's avatar
Andrew Maclean committed
35
          vtkImagingCore
36
          vtkRenderingCore
Andrew Maclean's avatar
Andrew Maclean committed
37
          vtkRenderingOpenGL
38 39
          vtkTestingCore
          vtkTestingRendering
Andrew Maclean's avatar
Andrew Maclean committed
40 41 42 43 44 45 46 47 48 49
        )
        Your application code includes 17 of 170 vtk modules.

        All modules referenced in the files:
        find_package(VTK COMPONENTS
          vtkCommonCore
          vtkFiltersCore
          vtkFiltersModeling
          vtkFiltersSources
          vtkRenderingCore
50
          vtkRenderingOpenGL
Andrew Maclean's avatar
Andrew Maclean committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
          vtkTestingCore
          vtkTestingRendering
        )
        Your application code includes 8 of 170 vtk modules.

        Minimal set of modules:
        find_package(VTK COMPONENTS
          vtkCommonCore
          vtkFiltersCore
          vtkFiltersModeling
          vtkRenderingOpenGL
          vtkTestingRendering
        )
        Your application code includes 5 of 170 vtk modules.

66 67 68
"""
    exit(0)

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
def EndsWithBackendName(moduleName):
    '''
    Return ``True`` if ``moduleName`` ends with any of the RENDERING_BACKENDS.
    '''
    for backend in RENDERING_BACKENDS:
        if moduleName.endswith(backend):
            return True
    return False

def ExcludeModuleName(moduleName, renderingBackend):
   '''
   Return ``True`` if ``moduleName`` should not be considered.
   '''
   return EndsWithBackendName(moduleName) and not moduleName.endswith(renderingBackend)

def IncludesToPaths(path, renderingBackend='OpenGL'):
Andrew Maclean's avatar
Andrew Maclean committed
85 86 87
    '''
    Build a dict that maps include files to paths.
    '''
88
    includeToPath = dict()
89
    prog = re.compile(r"((?:vtk|QVTK).*\.h)")
90 91 92 93 94 95
    for root, dirs, files in os.walk(path):
        for f in files:
            if prog.match(f):
                includeFile = prog.findall(f)[0]
                parts = root.split("/")
                module = parts[len(parts)-2] + parts[len(parts)-1]
96 97
                if ExcludeModuleName(module, renderingBackend):
                    continue
98 99 100
                includeToPath[includeFile] = module
    return includeToPath

101
def FindModules(path, renderingBackend='OpenGL'):
Andrew Maclean's avatar
Andrew Maclean committed
102 103 104
    '''
    Build a dict that maps paths to modules.
    '''
105 106 107 108 109 110 111 112 113 114 115
    pathToModule = dict()
    fileProg = re.compile(r"module.cmake")
    moduleProg = re.compile(r".*module[^(]*\(\s*(\w*)",re.S)
    for root, dirs, files in os.walk(path):
        for f in files:
            if fileProg.match(f):
                fid = open(os.path.join(root, f), "r")
                contents = fid.read()
                m = moduleProg.match(contents)
                if m:
                    moduleName = m.group(1)
116 117
                    if ExcludeModuleName(moduleName, renderingBackend):
                        continue
118 119 120 121 122 123
                    parts = root.split("/")
                    pathToModule[parts[len(parts)-2] + parts[len(parts)-1]] = moduleName
                fid.close()
    return pathToModule

def FindIncludes(path):
Andrew Maclean's avatar
Andrew Maclean committed
124 125 126
    '''
    Build a set that contains vtk includes.
    '''
127
    includes = set()
128
    includeProg = re.compile(r"((?:vtk|QVTK).*\.h)")
129 130 131 132 133 134 135
    fid = open(path, "r")
    contents = fid.read()
    incs = includeProg.findall(contents)
    includes.update(incs)
    fid.close()
    return includes

136
def FindModuleFiles(path, renderingBackend='OpenGL'):
Andrew Maclean's avatar
Andrew Maclean committed
137 138 139 140 141 142
    '''
    Get a list of module files in the VTK directory.
    '''
    moduleFiles = [os.path.join(root, name)
                 for root, dirs, files in os.walk(path)
                 for name in files
143 144
                 if name == ("module.cmake")
                 and not ExcludeModuleName(name, renderingBackend)]
Andrew Maclean's avatar
Andrew Maclean committed
145 146
    return moduleFiles

147
def ParseModuleFile(fileName, renderingBackend='OpenGL'):
Andrew Maclean's avatar
Andrew Maclean committed
148 149 150 151 152 153 154 155
    '''
    Read each module file returning the module name and what
    it depends on or implements.
    '''
    fh = open(fileName, 'rb')
    lines = []
    for line in fh:
        line = line.strip()
156 157
        if line.startswith('$'): # Skip CMake variable names
            continue
Andrew Maclean's avatar
Andrew Maclean committed
158 159 160 161 162 163 164 165 166 167
        if line.startswith('#'):
            continue
        line = line.split('#')[0].strip() # inline comments
        if line == "":
            continue
        line = line.split(')')[0].strip() # closing brace with no space
        if line == "":
            continue
        for l in line.split(" "):
            lines.append(l)
168
    languages = ['PYTHON', 'TCL', 'JAVA']
169
    keywords = ['BACKEND', 'COMPILE_DEPENDS', 'DEPENDS', 'EXCLUDE_FROM_ALL',
170
                'EXCLUDE_FROM_WRAPPING', 'GROUPS', 'IMPLEMENTS', 'KIT',
171
                'PRIVATE_DEPENDS', 'TEST_DEPENDS', 'IMPLEMENTATION_REQUIRED_BY_BACKEND'] + \
172
               map(lambda l: 'EXCLUDE_FROM_%s_WRAPPING' % l, languages)
Andrew Maclean's avatar
Andrew Maclean committed
173 174 175 176 177 178 179 180 181 182 183 184
    moduleName = ""
    depends = []
    implements = []
    state = "START";
    for item in lines:
        if state == "START" and item.startswith("vtk_module("):
            moduleName = item.split("(")[1]
            continue
        if item in keywords:
            state = item
            continue
        if state == 'DEPENDS' and item !=  ')':
185
            item = item.replace("${VTK_RENDERING_BACKEND}", renderingBackend)
Andrew Maclean's avatar
Andrew Maclean committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
            depends.append(item)
            continue
        if state == 'IMPLEMENTS' and item !=  ')':
            implements.append(item)
            continue
    return [moduleName, depends + implements]

def FindMinimalSetOfModules(modules, moduleDepencencies):
    '''
    Find the minimal set of modules needed.
    '''
    dependencies = set()
    for m in modules:
        dependencies = dependencies | set(moduleDepencencies[m]) # Set union
    return modules - dependencies # Set difference


def FindAllNeededModules(modules, foundModules, moduleDepencencies):
    '''
    Recursively search moduleDependencies finding all modules.
    '''
    if modules != None and len(modules) > 0:
        for m in modules:
            foundModules.add(m)
            foundModules = foundModules | set(moduleDepencencies[m]) # Set union
            foundModules = FindAllNeededModules(moduleDepencencies[m],
                                                foundModules,moduleDepencencies)
    return foundModules

def MakeFindPackage(modules):
    '''
    Make a useful find_package command.
    '''
    # Print a useful cmake command
    res = "find_package(VTK COMPONENTS\n"
    for module in sorted(modules):
        res +=  "  " + module + "\n"
    res +=  ")"
    return res

226 227 228
from pprint import pprint as pp

def main(vtkSourceDir, sourceFiles, renderingBackend='OpenGL'):
Andrew Maclean's avatar
Andrew Maclean committed
229 230 231 232
    '''
    Start the program
    '''
    # Generate dict's for mapping includes to modules
233 234
    includesToPaths = IncludesToPaths(vtkSourceDir + "/", renderingBackend)
    pathsToModules = FindModules(vtkSourceDir + "/", renderingBackend)
Andrew Maclean's avatar
Andrew Maclean committed
235 236 237

    # Test to see if VTK source is provided
    if len(pathsToModules) == 0:
238
        raise IOError, vtkSourceDir +\
Andrew Maclean's avatar
Andrew Maclean committed
239 240 241 242 243
        " is not a VTK source directory. It does not contain any module.cmake files."

    # Parse the module files making a dictionary of each module and its
    # dependencies or what it implements.
    moduleDepencencies = dict()
244 245
    moduleFiles = FindModuleFiles(vtkSourceDir + "/", renderingBackend)

Andrew Maclean's avatar
Andrew Maclean committed
246
    for fname in moduleFiles:
247
        m = ParseModuleFile(fname, renderingBackend)
Andrew Maclean's avatar
Andrew Maclean committed
248 249 250 251
        moduleDepencencies[m[0]] = m[1]

    # Build a set of includes for all command line files
    allIncludes = set()
252
    for f in sourceFiles:
Andrew Maclean's avatar
Andrew Maclean committed
253 254 255 256 257 258 259 260
        if os.path.isfile(f):
            allIncludes.update(FindIncludes(f))
        else:
            # We have a folder so look through all the files.
            for path, dirs, files in os.walk(f):
                for fn in files:
                    allIncludes.update(FindIncludes(os.path.join(path,fn)))
    if len(allIncludes) == 0:
261
        raise IOError, f + " does not exist"
262

Andrew Maclean's avatar
Andrew Maclean committed
263 264 265 266 267 268 269 270
    # Build a set that contains all modules referenced in command line files
    allModules = set()
    for inc in allIncludes:
        if inc in includesToPaths:
            module = includesToPaths[inc]
            if module in pathsToModules:
                allModules.add(pathsToModules[includesToPaths[inc]])

271 272 273 274 275
    # Add vtkInteractionStyle if required.
    if "vtkRenderWindowInteractor.h" in allIncludes:
        allModules.add("vtkInteractionStyle")

    # Add OpenGL factory classes if required.
Andrew Maclean's avatar
Andrew Maclean committed
276 277 278
    if "vtkRenderingFreeType" in allModules:
        allModules.add("vtkRenderingFreeTypeFontConfig")
    if "vtkRenderingCore" in allModules:
279
        allModules.add("vtkRendering%s" % renderingBackend)
Andrew Maclean's avatar
Andrew Maclean committed
280
    if "vtkRenderingVolume" in allModules:
281
        allModules.add("vtkRenderingVolume%s" % renderingBackend)
Andrew Maclean's avatar
Andrew Maclean committed
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

    # Find the minimal set of modules.
    minimalSetOfModules =\
        FindMinimalSetOfModules(allModules, moduleDepencencies)
    # Find all the modules, chasing down all the dependencies.
    allNeededModules =\
        FindAllNeededModules(minimalSetOfModules, set(), moduleDepencencies)

    modules = {'All modules referenced in the files:': allModules,
                'Minimal set of modules:': minimalSetOfModules,
                'Modules and their dependencies:': allNeededModules
              }
    for k, v in modules.iteritems():
        print k
        print MakeFindPackage(v)
        print "Your application code includes " + str(len(v)) +\
              " of " + str(len(pathsToModules)) + " vtk modules.\n"
    print

if __name__ == '__main__':
302 303 304 305
    if len(sys.argv) != 3:
      displayHelp()
      exit(0)
    main(sys.argv[1], sys.argv[2:])