-
Notifications
You must be signed in to change notification settings - Fork 1
/
udbcmp.py
130 lines (109 loc) · 5.47 KB
/
udbcmp.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
"""UDB Compare.
Usage:
udbcmp --before=<inputUDB> --after=<inputUDB> \r\n \
[--metrics=inlineCSVofNames]
[--dllDir=<dllDir>]\r\n \
[--verbose]
Options:
--before=<inputUDB> File path to a UDB with the "before" state of your sources
--after=<inputUDB> File path to a UDB with the "after" state of your sources
--metrics=inlineCSVofNames Inline string which is a CSV of names of metrics to compare, from a total listed at https://scitools.com/support/metrics_list/ [default: AvgCyclomaticModified,AvgLineCode]
--dllDir=<dllDir> Path to the dir with the DLL to the Understand Python SDK.[default: C:/Program Files/SciTools/bin/pc-win32/python]
--verbose If you want lots of messages printed. [default: false]
Errors:
DBAlreadyOpen - only one database may be open at once
DBCorrupt - bad database file
DBOldVersion - database needs to be rebuilt
DBUnknownVersion - database needs to be rebuilt
DBUnableOpen - database is unreadable or does not exist
NoApiLicense - Understand license required
Author:
Marcio Marchini ([email protected])
"""
import sys
import re
import datetime
import json
import os
from docopt import docopt
def open_udb (udb_path):
try:
db = understand.open(udb_path)
except understand.UnderstandError as exc:
print ("Error '%s' opening input file '%s'" % (exc, udb_path))
sys.exit(-2)
return db
def compare_udbs (udb_before, udb_after, metric_names):
before_after_by_file = {}
populate_file_metrics(udb_before, "before", metric_names, before_after_by_file)
populate_file_metrics(udb_after, "after", metric_names, before_after_by_file)
populate_diffs(before_after_by_file, "before", "after", "diff")
only_changed = prune_unchanged (before_after_by_file, "diff")
return only_changed
def prune_unchanged (before_after_by_file, diff_tag):
return {file:metrics_by_before_after_tag for file, metrics_by_before_after_tag in before_after_by_file.items() if diff_tag in metrics_by_before_after_tag}
def _compute_dict_diff (dict_a, dict_b):
result = {}
for key_a, value_a in dict_a.items():
value_b = dict_b.get(key_a, 0)
result[key_a] = value_b - value_a
for key_b, value_b in dict_b.items():
if key_b not in dict_a: # new metric, was not present in the file
result[key_b] = value_b
return result
def populate_diffs(before_after_by_file, tag_before, tag_after, tag_diff):
for file_path, dict_before_after in before_after_by_file.items():
metrics_before = dict_before_after[tag_before]
metrics_after = dict_before_after.get(tag_after,{})
if metrics_before == metrics_after:
continue
dict_before_after[tag_diff] = _compute_dict_diff(metrics_before, metrics_after)
def populate_file_metrics(udb, tag, metric_names, result):
# result: dict {key: <file-path>, value: dict {key: tag, value : dict (key: metric-name, value: metric-value} } }
file_entities = udb.ents("file ~Unresolved ~Unknown ~Unparsed")
for file_entity in file_entities:
if file_entity.library() is not "": # skip files that belong to external libraries
continue
file_path = file_entity.relname()
file_attribs = {}
if file_path not in result:
result[file_path] = file_attribs
else:
file_attribs = result[file_path]
metric_dict = file_entity.metric(metric_names)
unsupported_metrics_names = []
for k, v in metric_dict.items():
if metric_dict[k] is None:
unsupported_metrics_names.append(k)
if len(unsupported_metrics_names) > 0:
for k in unsupported_metrics_names:
del metric_dict[k]
sys.stderr.write("WARNING '%s' not supported by file '%s' \n" % (unsupported_metrics_names, file_path))
file_attribs[tag] = metric_dict
if __name__ == '__main__':
arguments = docopt(__doc__, version='0.1')
verbose = arguments["--verbose"]
dllDir = arguments["--dllDir"]
sys.path.insert(0,dllDir) # add the dir with the DLLs - Qt etc
os.environ["PATH"] = dllDir + os.pathsep + os.environ["PATH"] # prepend
sys.path.insert(0,os.path.join(dllDir,"Python")) # also needed, For interop
sys.path.insert(0,os.path.join(dllDir,"python")) # also needed, For interop with older versions of Understand (which used lowercase)
#hangs!!!!! os.environ["PYTHONPATH"] = os.path.join(dllDir,"python") + os.pathsep + os.environ.get("PYTHONPATH", "") # prepend
if verbose:
print ("\r\n====== udbcmp by Marcio Marchini: [email protected] ==========")
print(arguments)
try:
import understand
except:
print ("Can' find the Understand DLL. Use --dllDir=...")
print ("Please set PYTHONPATH to point to Understand's C:/Program Files/SciTools/bin/pc-win64/python or equivalent")
sys.exit(-1)
if verbose:
print ("\r\n...opening input udb %s" % arguments["--before"])
udb_before = open_udb(arguments["--before"])
if verbose:
print ("\r\n...opening output udb %s" % arguments["--before"])
udb_after = open_udb(arguments["--after"])
metric_names = arguments["--metrics"].split(",")
comparison = compare_udbs (udb_before, udb_after, metric_names)
print (json.dumps(comparison))