-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanalyze.py
executable file
·192 lines (171 loc) · 6.49 KB
/
analyze.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
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
#!/usr/bin/python3
import argparse
import os
import sys
import yaml
from compare import Comparator, ComparisonResults, DiffType
from build import build_snapshot, clone_repository
from blame import CommitLinkFinder
def parse_args():
"""Prepare the parser of command-line arguments and parse them."""
parser = argparse.ArgumentParser(
description="Compare multiple versions of a C project using Diffkemp."
)
parser.add_argument(
"config",
help="path to the configuration file, see README.md for details",
)
parser.add_argument(
"--output",
default="results",
help="path to the directory where the results will be stored",
)
parser.add_argument(
"--diffkemp", default="diffkemp", help="path to the DiffKemp executable"
)
parser.add_argument(
"--sources",
default="sources",
help="path to the directory where project sources will be stored",
)
parser.add_argument(
"--snapshots",
default="snapshots",
help="path to the directory where project snapshots will be stored",
)
parser.add_argument(
"--builds",
default="builds",
help="path to the directory where built projects will be stored",
)
parser.add_argument(
"--rebuild",
action="store_true",
help="rebuild all project versions even if the snapshots already exist",
)
parser.add_argument(
"--no-compare",
action="store_true",
help="do not compare the project versions, only build them",
)
parser.add_argument(
"--verbose",
action="store_true",
help="print out all executed commands",
)
parser.add_argument(
"--review-template",
action="store_true",
help="prepare a template for manual evaluation",
)
parser.add_argument(
"--disable-patterns",
help="comma-separated list of built-in patterns to disable",
)
parser.add_argument(
"--custom-patterns", help="file with custom pattern configuration for Diffkemp"
)
return parser.parse_args()
def main():
args = parse_args()
# Load the configuration file
with open(args.config, "r") as config_file:
config = yaml.safe_load(config_file)
project_name = config["name"]
tags = config["tags"]
source_dir = os.path.join(args.sources, project_name)
output_dir = os.path.join(args.output, project_name)
snapshots_dir = os.path.join(args.snapshots, project_name)
os.makedirs(snapshots_dir, exist_ok=True)
# If the source directory does not exist, clone the repository
if not os.path.isdir(source_dir):
clone_repository(args.verbose, config["git"], source_dir)
# Build all snapshots
for tag in tags:
build_dir = os.path.join(args.builds, project_name, tag)
snapshot_dir = os.path.join(snapshots_dir, tag)
if os.path.isdir(snapshot_dir) and not args.rebuild:
print(f"Skipping the build of {project_name} @ {tag}.")
continue
print(f"Building {project_name} @ {tag}.")
build_snapshot(
args.verbose,
args.diffkemp,
config,
tag,
source_dir,
build_dir,
snapshot_dir,
)
# Create the output directory
output_dir = os.path.join(args.output, project_name)
os.makedirs(output_dir, exist_ok=True)
results_file_path = os.path.join(output_dir, "results.yml")
# If the user does not want to compare the snapshots, exit
if args.no_compare:
return
if os.path.exists(results_file_path):
print("Skipping comparison, results already exist.")
results = ComparisonResults.load(results_file_path)
else:
# Compare consecutive pairs of snapshots
comparator = Comparator(
args.verbose,
args.diffkemp,
config,
snapshots_dir,
output_dir,
args.custom_patterns,
args.disable_patterns,
)
for old_tag, new_tag in zip(tags, tags[1:]):
comparator.compare_snapshots(old_tag, new_tag)
results = comparator.get_results()
# Export the results
print(f"Exporting results to {results_file_path}.")
with open(results_file_path, "w") as results_file:
yaml.safe_dump(results.results, results_file)
# Export the statistics
stats_file_path = os.path.join(output_dir, "stats.yml")
print(f"Exporting statistics to {stats_file_path}.")
with open(stats_file_path, "w") as stats_file:
yaml.safe_dump(results.get_stats(), stats_file)
if not args.review_template:
return
# Prepare templates for manual evaluation
template_semantic = {}
template_syntactic = {}
for old_tag, new_tag in zip(tags, tags[1:]):
key = ComparisonResults.key(old_tag, new_tag)
template_semantic[key] = {}
template_syntactic[key] = {}
diffkemp_out_dir = os.path.join(output_dir, f"{old_tag}-{new_tag}")
diffkemp_out_file = os.path.join(diffkemp_out_dir, "diffkemp-out.yaml")
with open(diffkemp_out_file, "r") as diffkemp_out:
diffkemp_out = yaml.safe_load(diffkemp_out)
commit_link_finder = CommitLinkFinder(
source_dir, old_tag, new_tag, diffkemp_out
)
tag_results = results.get(old_tag, new_tag)
for function, function_result in tag_results.items():
if function_result == DiffType.SEMANTIC.value:
template_semantic[key][function] = {
"category": "",
"comment": "",
"commits": commit_link_finder.get_commit_links(function),
}
elif function_result == DiffType.SYNTACTIC.value:
template_syntactic[key][function] = {
"category": "",
"comment": "",
}
template_semantic_file_path = os.path.join(output_dir, "template-semantic.yml")
print(f"Exporting semantic review template to {template_semantic_file_path}.")
with open(template_semantic_file_path, "w") as template_file:
yaml.safe_dump(template_semantic, template_file)
template_syntactic_file_path = os.path.join(output_dir, "template-syntactic.yml")
print(f"Exporting syntactic review template to {template_syntactic_file_path}.")
with open(template_syntactic_file_path, "w") as template_file:
yaml.safe_dump(template_syntactic, template_file)
if __name__ == "__main__":
sys.exit(main())