4
Show Botan module dependencies as a list or graph.
6
Requires graphviz from pip when graphical output is selected:
7
https://pypi.python.org/pypi/graphviz
9
(C) 2015 Simon Warta (Kullo GmbH)
11
Botan is released under the Simplified BSD License (see license.txt)
19
from collections import OrderedDict
23
# Assume this script is in botan/src/scripts
24
botan_root = os.path.join(os.path.dirname(sys.argv[0]), "..", "..")
27
sys.path.append(botan_root)
28
from configure import ModuleInfo
30
parser = argparse.ArgumentParser(description=
31
'Show Botan module dependencies. '
32
'The output is reduced by indirect dependencies, '
33
'i.e. you must look at the result recursively to get all dependencies.')
35
parser.add_argument('mode',
36
choices=["list", "draw"],
37
help='The output mode')
38
parser.add_argument('--format',
40
choices=["pdf", "png"],
42
help='The file format (drawing mode only)')
43
parser.add_argument('--engine',
45
choices=["fdp", "dot"],
47
help='The graph engine (drawing mode only)')
48
parser.add_argument('--all', dest='all', action='store_const',
49
const=True, default=False,
50
help='Show all dependencies. Default: direct dependencies only. (list mode only)')
51
parser.add_argument('--verbose', dest='verbose', action='store_const',
52
const=True, default=False,
53
help='Verbose output (default: false)')
54
args = parser.parse_args()
57
files += glob.glob(botan_root + '/src/lib/*/*/*/*/*/*/info.txt')
58
files += glob.glob(botan_root + '/src/lib/*/*/*/*/*/info.txt')
59
files += glob.glob(botan_root + '/src/lib/*/*/*/*/info.txt')
60
files += glob.glob(botan_root + '/src/lib/*/*/*/info.txt')
61
files += glob.glob(botan_root + '/src/lib/*/*/info.txt')
62
files += glob.glob(botan_root + '/src/lib/*/info.txt')
63
files += glob.glob(botan_root + '/src/lib/info.txt')
67
print("No info.txt files found.")
72
def dicts(t): return {k: dicts(t[k]) for k in t}
74
def paths(t, path = [], level=0):
77
ret.append(path + [key])
78
ret += paths(t[key], path + [key], level+1)
82
print("Getting dependencies from into.txt files ...")
84
for filename in files:
85
(rest, info_txt) = os.path.split(filename)
86
(rest, modname) = os.path.split(rest)
87
module = ModuleInfo(filename)
88
modules.append(module)
90
print(module.basename)
91
print("\t" + str(set(module.dependencies())))
94
print(str(len(modules)) + " modules:")
95
names=[m.basename for m in modules]
101
print("resolving dependencies ...")
103
def cartinality(depdict):
104
return sum([len(depdict[k]) for k in depdict])
106
registered_dependencies = dict()
107
all_dependencies = dict()
108
direct_dependencies = dict()
110
for module in modules:
111
lst = module.dependencies()
112
registered_dependencies[module.basename] = set(lst) - set([module.basename])
114
# Get all_dependencies from registered_dependencies
115
def add_dependency():
116
for key in all_dependencies:
117
potentially_new_modules_for_key = None
118
new_modules_for_key = None
119
for currently_in in all_dependencies[key]:
120
if currently_in in all_dependencies:
121
potentially_new_modules_for_key = all_dependencies[currently_in] - set([key])
122
if not potentially_new_modules_for_key <= all_dependencies[key]:
123
new_modules_for_key = potentially_new_modules_for_key.copy()
125
if new_modules_for_key:
126
all_dependencies[key] |= new_modules_for_key
130
all_dependencies = copy.deepcopy(registered_dependencies)
131
direct_dependencies = copy.deepcopy(registered_dependencies)
134
all_dependencies = OrderedDict(sorted(all_dependencies.items()))
135
direct_dependencies = OrderedDict(sorted(direct_dependencies.items()))
137
#print(direct_dependencies)
141
card = cartinality(all_dependencies)
143
if card == last_card:
148
# Return true iff a depends on b,
149
# i.e. b is in the dependencies of a
150
def depends_on(a, b):
151
if not a in direct_dependencies:
154
return b in direct_dependencies[a]
156
def remove_indirect_dependencies():
157
for mod in direct_dependencies:
158
for one in direct_dependencies[mod]:
159
others = direct_dependencies[mod] - set([one])
161
if depends_on(other, one):
162
direct_dependencies[mod].remove(one)
168
card = cartinality(direct_dependencies)
170
if card == last_card:
173
remove_indirect_dependencies()
176
if sys.platform.startswith('linux'):
177
subprocess.call(["xdg-open", f])
182
print("Done resolving dependencies.")
184
if args.mode == "list":
186
for key in all_dependencies:
187
print(key.ljust(17) + " : " + ", ".join(sorted(all_dependencies[key])))
189
for key in direct_dependencies:
190
print(key.ljust(17) + " : " + ", ".join(sorted(direct_dependencies[key])))
192
if args.mode == "draw":
193
import graphviz as gv
196
tmpdir = tempfile.mkdtemp(prefix="botan-")
198
g2 = gv.Digraph(format=args.format, engine=args.engine)
199
for key in direct_dependencies:
201
for dep in direct_dependencies[key]:
205
print("Rendering graph ...")
206
filename = g2.render(filename='graph', directory=tmpdir)
209
print("Opening " + filename + " ...")