-
Notifications
You must be signed in to change notification settings - Fork 6
/
cpp2cxx-validate.py
executable file
·311 lines (270 loc) · 11.5 KB
/
cpp2cxx-validate.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
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
#!/usr/bin/python
'''
This is the validator script.
Read the user guide supplied with this tool for detailed information.
Pre-requisites:
\item Python 2.7.3 should be installed (It should work with higher versions of Python but
the script was not tested against them)
\item The directory dm_dir should have the translated files.
\item A file named demacrofied_macro_stat.yaml should be present in the directory dm_dir,
which is generated by the translator.
User guide:
Before running this script the user should have the knowledge of the build system
of the software/library that has to be demacrofied. Please look at the global variable build_command
and modify it with your own command if "make" is not the default
build command for your software.
Once the build system is known and the Validator is configured properly, it can be executed simply
by issuing the following command.
$./validator.py <Mode>
where <Mode> can be either of the following three.
translate
no-translate
resume
Read the comments and the supplied user-manual for detailed information about which mode to use.
N.B. Make sure the file is executable and a Python interpreter is installed on your system.
Some libraries are written in such a way that their build system does not re-builds
with the issue of build command when only header files have been modified. If your build system
is like that then you need to generate a file include_dependency.yaml which will contain
a mapping of header files versus the files which include them. A C++ program that would help
you out in this regard has been provided along with the demacrofier. Please read the comments
in the file "HeaderDependencyBuild.cpp" for further information.
'''
import yaml
import sys
import subprocess
import os
#keeps log and errors
err_log = open("error_log","w")
#file containing list of header file and dependent (source/header)files
#dep_file = open("dm_dir/include_dependency.yaml","r")
#dep_file_list = yaml.load(dep_file)
#dep_file.close()
########################################################################
########################## GLOBAL VARIABLES ############################
file_headerGuard_map = {}
def_string = ""
#command issued to build the software
build_command = "make"
#directory where the translated files are kept
demac_dir = "dm_dir"
#root directory of the software being rejuvenated
project_dir = "."
#header file which would have the list of macros already validated
defined_include_guard = "Defined.h"
#intermediate file generated by the translator which has the information
#of all the translated macros
demacrofied_macro_stat = demac_dir+"/demacrofied_macro_stat.yaml"
#path of the executable `tranlator'
translator = "../build/bin/translator"
#the backup directory where the files should be backed up
backup_dir = "demac_backup"
########################################################################
#helper function to determine if the file is a header file or a source file
def file_type(file_name, file_type):
file_no_ext, file_ext = os.path.splitext(file_name)
#alternatively we can use file.endswith('ext')
if file_ext == file_type:
return True
#helper function, not used currently
def is_header_file(file_name):
if file_type(file_name,'h') or file_type(file_name,'hpp'):
return True
#helper function, not used currently
def is_source_file(file_name):
if file_type(file_name,'c') or file_type(file_name,'cpp'):
return True
#when the build system takes care of dependent files
#touching only the files currently being processed is sufficient
def touch_dependent_files(file_name):
# for f in get_dependent_file_list(file_name):
print "\ntouching file: ", file_name
subprocess.call(['touch', file_name])
return 0
#when the build system does not take care of dependent files
def get_dependent_file_list(file_name):
if file_name in dep_file_list:
return dep_file_list[file_name]
return None
#load the file and prepare defined strings for each include guard
#see the format of demacrofied_macro_stat for understanding the map
def make_file_headerGuard_map():
f = open(demacrofied_macro_stat, "r")
dataMap = yaml.load(f)
f.close()
for curr_file in dataMap:
ind_list = []
if dataMap[curr_file] == None:
continue
for macro_list in dataMap[curr_file]:
for macro_id in macro_list:
#print macro_list[macro_id][2]['header_guard_string']
def_string = '#define ' + macro_list[macro_id][2]['header_guard_string']+ ' 1\n'
ind_list.append(def_string)
file_headerGuard_map[curr_file] = ind_list
#print file_headerGuard_map
return
def load_DefinedIncludeGuards():
f = open(defined_include_guard, 'r')
list_includes = set()
for i in f.readlines():
list_includes.add(i)
f.close()
return list_includes
def copy_translated_files():
#call is blocking while Popen is not so using call here
#be careful with the space ' '
copy_cmd = "cp "
copy_header = copy_cmd + demac_dir + "/*.h " + demac_dir + "/*.hpp " + project_dir
copy_source = copy_cmd + demac_dir + "/*.C " + demac_dir + "/*.cpp " + project_dir
print copy_header
print copy_source
pid = subprocess.call(copy_header, shell=True)
pid = subprocess.call(copy_source,shell=True)
return pid
def backup_files(backup_dir):
#this is a very naive implementation, please make your own to suit your project
copy_cmd = "cp "
copy_header = copy_cmd + project_dir + "/*.h " + backup_dir
copy_source = copy_cmd + project_dir + "/*.cpp " + backup_dir
print "copying files into backup directory...\n"
retcode1 = subprocess.call(copy_header, shell=True)
retcode2 = subprocess.call(copy_source, shell=True)
if(retcode1 == 0 and retcode2 == 0):
print "\ncopying completed...\n"
return 0
return 1
'''
copy the files from the dm_dir to current directory build using build command.
if build is not successful then quit by reporting appropriate error message
'''
def preprocess():
try:
#truncate the file size to zero
#if you want to resume from the current file issue a different command
f_define = open(defined_include_guard, 'w')
f_define.close()
print "\n\nbuilding the project before copying the new files\n"
retcode = subprocess.call(build_command, shell=True)
#retcode = pid.communicate()[0]
if retcode < 0:
print >>sys.stderr, "\nEven before copying Child was terminated by signal", retcode
else:
if retcode == 0:
#if successful then add the new include condition
print >>sys.stderr, "\nsuccessfully built before copying the project"
else:
print >>sys.stderr, "\nunsuccessful build before copying with return code: ", retcode
sys.exit(-11)
print "build before copying finshed\n"
#
#this is the place if you to write the copying function
retcode = copy_translated_files()
#
print "copying finished \nbuilding the project after copying"
retcode = subprocess.call(build_command, shell=False)
#retcode = pid.communicate()[0]
if retcode < 0:
print >>sys.stderr, "after copying Child was terminated by signal", retcode
else:
if retcode == 0:
#if successful then add the new include condition
print >>sys.stderr, "successfully built after copying "
else:
print >>sys.stderr, "unsuccessful built after copying with return code: ", retcode
sys.exit(-12)
except OSError, e:
print >>sys.stderr, "Execution failed:", e
sys.exit(-13)
return 0
'''
Summary of the working of validator
1. first copy all the header and the cpp files from the dm_dir to the current directory
2. take one file
a. get the list of all the header_guards in that file (from demacrofied_macro_stat.yaml)
b. enable one header_guard string
3. recompile, if successful,
add that include guard to final list
`touch' this file as well as all the files dependent on this file (if any)
else remove that header guard from getting updated to the continuing list
'''
def main(argv):
new_list = set()
if len(argv) < 2:
sys.stderr.write("Usage: %s <action>" % (argv[0],))
return 1
############### FIRST MODE ###################
#the Validator is capable of running the Translator from here itself.
#This condition should be modified depending upon the place where the backup
#is required, and where the translator executable is located
if argv[1] == "suggest":
print "suggesting and validating the translations"
#first backup the files in a directory
retcode = backup_files(backup_dir)
if(retcode == 1):
sys.stderr.write("backup failed\n exiting...")
return 1
print "\nTranslation started...\n"
subprocess.call([translator, "ConfigFile.cfg"])
print "\n\ntranslation completed\n"
print "\n\npreprocessing started\n"
preprocess()
err_log.write("preprocessing done\n")
############### SECOND MODE ###################
if argv[1] == "no-suggest": #donot start the translator, just validate
print "\n\nvalidating the translations suggested\n"
preprocess()
err_log.write("preprocessing done\n")
############### THIRD MODE ###################
#this is a very useful feature. It starts the Validator by reading the file Defined.h
#and continues validation from there. The file Defined.h should have only those macros
#which were successfully validated previously, otherwise the Validator will keep getting
#compilation errors and no further additions shall be made to the file Defined.h
if argv[1] == "resume": #loads the header macros that are already not giving errors
print "\n\nvalidating the translations suggested"
print "(also reading the header file for already validated translations)\n"
new_list = load_DefinedIncludeGuards()
make_file_headerGuard_map()
err_log.write("header guards collected\n")
for file_in_process in file_headerGuard_map:
err = "processig file: " + file_in_process +"\n"
print "processig file: " + file_in_process +"\n"
err_log.write(err)
#for include_guard_list in file_headerGuard_map[file_in_process]:
for k in file_headerGuard_map[file_in_process]:
#if this macro has not been defined yet
if k not in new_list:
f_define = open(defined_include_guard,'w')
#write the contents of old list
for i in new_list:
f_define.write(i)
#write one new include condition
f_define.write(k)
f_define.close()
touch_dependent_files(file_in_process)
else:
continue
#compile the library
try:
#by default the script issues the 'make' command to the console.
#This should be modified depending upon the command the user wants to issue
#in order to build a software.
retcode = subprocess.call(build_command, shell=True)
if retcode < 0:
print >>sys.stderr, "inside main Child was terminated by signal", -retcode
sys.exit(retcode)
if retcode == 0:
#if successful then add the new include condition
print >>sys.stderr, "successful build for ", k
err = "successfully build one file: " + file_in_process + "\n"
err_log.write(err)
new_list.add(k)
else:
print >>sys.stderr, "unsuccessful build for ", k, ", with return code: ", retcode
err = "failed for file: " + file_in_process + "\n"
err_log.write(err)
except OSError, e:
print >>sys.stderr, "Execution failed:", e
sys.exit(-2)
# print new_list
if __name__ == "__main__":
sys.exit(main(sys.argv))