File: VisualizeModuleDependencies.py

package info (click to toggle)
vtk6 6.3.0%2Bdfsg2-8.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 118,972 kB
  • sloc: cpp: 1,442,790; ansic: 113,395; python: 72,383; tcl: 46,998; xml: 8,119; yacc: 4,525; java: 4,239; perl: 3,108; lex: 1,694; sh: 1,093; asm: 154; makefile: 68; objc: 17
file content (331 lines) | stat: -rwxr-xr-x 11,003 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#!/usr/bin/env python

'''
This program takes a list of module files and creates a (possibly disjoint)
directed graph of the modules and their dependencies. Arrows on the
directed graph point to the dependent module.

Typical usage would be as follows:
 VisualizeModuleDependencies.py VTKSourceDir vtkFiltersSources,vtkInteractionStyle,vtkRenderingOpenGL

'''

import os, sys
from collections import defaultdict
import vtk

def GetProgramParameters():
    import argparse
    description = 'Creates a directed graph of the modules and their dependencies.'
    epilogue = '''
        This program takes a list of module files and creates a
        (possibly disjoint) directed graph of the modules and their
        dependencies. Arrows on the directed graph point to the dependent module.
        By default, dependencies of a given module are followed to their maximum
        depth. However you can restrict the depth by specifying the depth to
        which dependent modules are searched.
        The moduleList is a comma-separated list of module names with no
        spaces between the names.
        The treeDepth defaults to 0, this means that for a given module all
        dependent modules will be found. If non-zero, then trees will be only
        searched to that depth.
    '''
    parser = argparse.ArgumentParser(description=description, epilog=epilogue)
    parser.add_argument('vtkSourceDir', help='The path to the vtk Source Directory.')
    parser.add_argument('moduleList', help='The list of modules.')
    parser.add_argument('moduleTreeDepth', help='The depth of the module trees', nargs='?', default=0, type=int)
    args = parser.parse_args()
    vtkSourceDir = args.vtkSourceDir
    moduleList = [x.strip() for x in args.moduleList.split(',')]
    moduleTreeDepth = args.moduleTreeDepth
    return (vtkSourceDir, moduleList, moduleTreeDepth)

def GetProgramParametersOld():
    '''
    Used for Python versions < 2.7
    '''
    if len(sys.argv) < 3:
        s = 'Usage: ' + sys.argv[0] + ' vtkSourceDir moduleList [moduleTreeDepth]'
        print(s)
        exit(0)
    args = dict()
    args['vtkSourceDir'] = sys.argv[1]
    args['moduleList'] = sys.argv[2]
    args['moduleTreeDepth'] = 0
    if len(sys.argv) > 3:
        args['moduleTreeDepth'] = int(sys.argv[3])
    vtkSourceDir = args['vtkSourceDir']
    moduleList = [x.strip() for x in args['moduleList'].split(',')]
    moduleTreeDepth = args['moduleTreeDepth']
    return (vtkSourceDir, moduleList, moduleTreeDepth)

def FindModuleFiles(path):
    '''
    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
                 if name == ("module.cmake")]
    return moduleFiles

def ParseModuleFile(fileName):
    '''
    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()
        if line.startswith('$'):  # Skip CMake variable names
            continue
        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)
    languages = ['PYTHON', 'TCL', 'JAVA']
    keywords = ['BACKEND', 'COMPILE_DEPENDS', 'DEPENDS', 'EXCLUDE_FROM_ALL',
                'EXCLUDE_FROM_WRAPPING', 'GROUPS', 'IMPLEMENTS', 'KIT',
                'PRIVATE_DEPENDS', 'TEST_DEPENDS'] + \
               map(lambda l: 'EXCLUDE_FROM_%s_WRAPPING' % l, languages)
    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 != ')':
            depends.append(item)
            continue
        if state == 'IMPLEMENTS' and item != ')':
            implements.append(item)
            continue
    return [moduleName, depends + implements]

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 MakeModuleTree(module, index, tree, moduleDependencies, treeDepth, level=0):
    '''
    For a given module make a tree with the module as the root and the
    dependent modules as children.
    '''
    if module:
        index = index + [module]
        if treeDepth == 0 or level < treeDepth:
            for m in moduleDependencies[module]:
                level += 1
                MakeModuleTree(m, index, tree, moduleDependencies, treeDepth, level)
                level -= 1
    Add(tree, index)

# One-line Tree in Python
# See: https:gist.github.com/hrldcpr/2012250
def Tree(): return defaultdict(Tree)

def Add(tree, keys):
    for key in keys:
        tree = tree[key]

def PrettyPrint(tree, level=0):
    '''
    Useful to visualize the tree.
    '''
    result = ''
    for k, v in tree.iteritems():
        s = '  ' * level + k + '\n'
        result += s
        level += 1
        result += PrettyPrint(v, level)
        level -= 1
    return result

def GetAllKeys(tree):
    '''
    Return all the modules in the tree as a set.
    '''
    modules = set()
    for key in tree:
        modules = set(list(modules) + [key] + list(GetAllKeys(tree[key])))
    return modules

def MakeEdgeList(t):
    '''
    Return a set that represents the edges in the tree.
    '''
    edgeList = set()
    for k, v in t.iteritems():
        subKeys = v.keys()
        if subKeys:
            for kk in subKeys:
                edgeList.add((k, kk))
        edg = MakeEdgeList(v)
        if edg:
            edgeList.update(edg)
    return edgeList

def MakeGraph(t, parent='', level=0):
    '''
    Returns a list that has two elements, the vertices and the edge list.
    '''
    return [GetAllKeys(t), MakeEdgeList(t)]

def GenerateGraph(moduleList, moduleDepencencies, moduleTreeDepth):
    '''
    Generate a graph from the module list.
    The resultant graph is a list consisting of two sets, the first set
    is the set of vertices and the second set is the edge list.
    '''
    graph = [set(), set()]
    for m in moduleList:
        t = Tree()
        MakeModuleTree(m, [], t, moduleDepencencies, moduleTreeDepth)
        g = MakeGraph(t)
        graph[0].update(g[0])
        if g[1]:
            graph[1].update(g[1])
    return graph

def GenerateVTKGraph(graph):
    '''
    Take the vertices and edge list in the graph parameter
    and return a VTK graph.
    '''
    g = vtk.vtkMutableDirectedGraph()
    # Label the vertices
    labels = vtk.vtkStringArray()
    labels.SetNumberOfComponents(1)
    labels.SetName("Labels")

    index = dict()
    l = list(graph[0])
    # Make the vertex labels and create a dictionary with the
    # keys as labels and the vertex ids as the values.
    for i in range(0, len(l)):
        # Set the vertex labels
        labels.InsertNextValue(l[i])
        index[l[i]] = g.AddVertex()
    g.GetVertexData().AddArray(labels)
    # Add edges
    l = list(graph[1])
    for i in range(0, len(l)):
        ll = list(l[i])
        g.AddGraphEdge(index[ll[0]], index[ll[1]])
#    g.Dump()
    return g

def DisplayGraph(graph):
    '''
    Display the graph.
    '''
    theme = vtk.vtkViewTheme()
    theme.SetBackgroundColor(0, 0, .1)
    theme.SetBackgroundColor2(0, 0, .5)

    # Layout the graph
    # Pick a strategy you like.
    # strategy = vtk.vtkCircularLayoutStrategy()
    strategy = vtk.vtkSimple2DLayoutStrategy()
    # strategy = vtk.vtkRandomLayoutStrategy()
    layout = vtk.vtkGraphLayout()
    layout.SetLayoutStrategy(strategy)
    layout.SetInputData(graph)

    view = vtk.vtkGraphLayoutView()
    view.AddRepresentationFromInputConnection(layout.GetOutputPort())
    # Tell the view to use the vertex layout we provide.
    view.SetLayoutStrategyToPassThrough()
    view.SetEdgeLabelVisibility(True)
    view.SetVertexLabelArrayName("Labels")
    view.SetVertexLabelVisibility(True)
    view.ApplyViewTheme(theme)

    # Manually create an actor containing the glyphed arrows.
    # Get the edge geometry
    edgeGeom = vtk.vtkGraphToPolyData()
    edgeGeom.SetInputConnection(layout.GetOutputPort())
    edgeGeom.EdgeGlyphOutputOn()

    # Set the position (0: edge start, 1: edge end) where
    # the edge arrows should go.
#        edgeGeom.SetEdgeGlyphPosition(0.8)
    edgeGeom.SetEdgeGlyphPosition(0.85)

    # Make a simple edge arrow for glyphing.
#        arrowSource = vtk.vtkGlyphSource2D()
#        arrowSource.SetGlyphTypeToEdgeArrow()
#        arrowSource.SetScale(0.075)
    # Or use a cone.
    coneSource = vtk.vtkConeSource()
    coneSource.SetRadius(0.025)
    coneSource.SetHeight(0.1)
    coneSource.SetResolution(12)

    # Use Glyph3D to repeat the glyph on all edges.
    arrowGlyph = vtk.vtkGlyph3D()
    arrowGlyph.SetInputConnection(0, edgeGeom.GetOutputPort(1))
#        arrowGlyph.SetInputConnection(1, arrowSource.GetOutputPort())
    arrowGlyph.SetInputConnection(1, coneSource.GetOutputPort())

    # Add the edge arrow actor to the view.
    arrowMapper = vtk.vtkPolyDataMapper()
    arrowMapper.SetInputConnection(arrowGlyph.GetOutputPort())
    arrowActor = vtk.vtkActor()
    arrowActor.SetMapper(arrowMapper)
    view.GetRenderer().AddActor(arrowActor)

    view.ResetCamera()
    view.Render()

    view.SetInteractionModeTo3D()
    view.GetInteractor().Initialize()
    view.GetInteractor().Start()

def main():
    ver = list(sys.version_info[0:2])
    ver = ver[0] + ver[1] / 10.0
    if ver >= 2.7:
        vtkSourceDir, moduleList, moduleTreeDepth = GetProgramParameters()
    else:
        vtkSourceDir, moduleList, moduleTreeDepth = GetProgramParametersOld()

    # Parse the module files making a dictionary of each module and its
    # dependencies or what it implements.
    moduleDepencencies = dict()
    moduleFiles = FindModuleFiles(vtkSourceDir + "/")
    for fname in moduleFiles:
        m = ParseModuleFile(fname)
        moduleDepencencies[m[0]] = m[1]

    # Generate a graph from the module list.
    graph = GenerateGraph(moduleList, moduleDepencencies, moduleTreeDepth)

    # Now build a vtk graph.
    g = GenerateVTKGraph(graph)

    # Display it.
    DisplayGraph(g)

if __name__ == '__main__':
    main()