/
simplify-dependencies.py
128 lines (105 loc) · 3.4 KB
/
simplify-dependencies.py
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
#!/usr/bin/python3
#
'''
Simplify our deps.make file so each target does not repeat sub-dependencies.
This is useful to then create a clean .dot file.
Usage: simplify-dependencies.py < deps.make > dependencies.dot
This script probably uses several features of Python 3.x.
'''
import argparse
import os
class UnexpectedInput(Exception):
pass
class NotFound(Exception):
pass
class Project(object):
def __init__(self, name, deps):
self._name = name
self._dependencies = deps
#print ("name: ", self._name, " <- ", self._dependencies)
def get_name(self):
return self._name
def get_deps(self):
return self._dependencies
def remove_dep(self, d):
if d in self._dependencies:
self._dependencies.remove(d)
def safe_name(name):
if '-' in name:
return '"' + name + '"'
return name
def load_projects(path):
projects = []
with open(path + '/deps.make') as f:
for line in f:
line = line.strip()
if len(line) == 0:
continue
if line[0] == '#':
continue
target_deps = line.split(':')
if len(target_deps) > 2:
raise UnexpectedInput
deps = []
if len(target_deps) > 1:
deps = target_deps[1].split()
projects.append(Project(target_deps[0], deps))
return projects
def find_project(projects, name):
for p in projects:
if p.get_name() == name:
return p
raise NotFound
def simplify_projects(projects):
for p in projects:
deps = p.get_deps().copy()
idx = 0
while idx < len(deps):
q = find_project(projects, deps[idx])
subs = q.get_deps()
for s in subs:
p.remove_dep(s)
if s not in deps:
deps.append(s)
idx += 1
def output_dot(projects):
output = ''
output += 'digraph dependencies {\n'
for p in projects:
output += ' '
output += safe_name(p.get_name())
if len(p.get_deps()) > 0:
output += ' [shape=box];\n'
else:
output += ' [shape=ellipse];\n'
for p in projects:
deps = p.get_deps()
for d in deps:
output += ' '
output += safe_name(p.get_name())
output += ' -> '
output += safe_name(d)
output += ';\n'
output += '}\n'
return output
def main():
for description in __doc__.splitlines():
if description:
break
parser = argparse.ArgumentParser(description=description)
parser.add_argument('workdir', metavar='<working directory>', nargs='*', help='location of the deps.make and were we save the output')
options = parser.parse_args()
if len(options.workdir) != 1:
raise NotFound
output_path = options.workdir[0]
projects = load_projects(output_path)
#print('Found ' + str(len(projects)) + ' projects in dependency graph.')
simplify_projects(projects)
output = output_dot(projects)
with open(output_path + '/clean-dependencies.dot', 'w') as f:
f.write(output)
os.system('dot -Tsvg <' + output_path + '/clean-dependencies.dot >' + output_path + '/clean-dependencies.svg')
#print('Dependency graph generated in "' + output_path + '/clean-dependencies.svg".')
if __name__ == '__main__':
main()
# vim: ts=4 sw=4 et