• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import argparse
2import os
3import sys
4import json
5import ccsyspath
6import time
7import clang.cindex
8from clang.cindex import Cursor
9from clang.cindex import CursorKind
10from pathlib import Path
11from clang.cindex import TranslationUnit
12
13
14def get_args():
15    '''
16    Get and parse the parameters from the console
17    '''
18    parse = argparse.ArgumentParser(prog='python3.8 compare.py')
19    parse.add_argument('-l',
20                       type=str,
21                       required=True,
22                       help='input lib file path')
23    parse.add_argument('-i',
24                       type=str,
25                       required=True,
26                       help='input head file path')
27    parse.add_argument('-m', type=str, help='input macros file path')
28    parse.add_argument('-b', type=str, help='input blacklist file path')
29    parse.add_argument('-o', type=str, help='result file output path')
30    args = parse.parse_args()
31    paramers_value = vars(args)
32    global INPUT_HEAD_PATH
33    INPUT_HEAD_PATH = paramers_value['i']
34    global INPUT_LIB_PATH
35    INPUT_LIB_PATH = paramers_value['l']
36    global INPUT_MACROS_PATH
37    INPUT_MACROS_PATH = paramers_value['m']
38    INPUT_MACROS_PATH = '' if INPUT_MACROS_PATH == None else INPUT_MACROS_PATH
39    global INPUT_BLACK_PATH
40    INPUT_BLACK_PATH = paramers_value['b']
41    INPUT_BLACK_PATH = '' if INPUT_BLACK_PATH == None else INPUT_BLACK_PATH
42    global OUTPUT_RESULT_PATH
43    OUTPUT_RESULT_PATH = paramers_value['o']
44    OUTPUT_RESULT_PATH = '' if OUTPUT_RESULT_PATH == None else OUTPUT_RESULT_PATH
45    check_parameters()
46
47
48def check_parameters():
49    '''
50    Check whether the obtained parameters are correct
51    '''
52    my_file = Path(INPUT_LIB_PATH)
53    if not my_file.is_file():
54        print('please input correct lib file path')
55        exit()
56    my_file = Path(INPUT_HEAD_PATH)
57    if not my_file.is_dir():
58        print('please input correct head file path')
59        exit()
60    global INPUT_BLACK_PATH
61    if not INPUT_BLACK_PATH == '':
62        my_file = Path(INPUT_BLACK_PATH)
63        if not my_file.is_file():
64            print('warring:input correct blacklist file path is error')
65            INPUT_BLACK_PATH = ''
66    global INPUT_MACROS_PATH
67    if not INPUT_MACROS_PATH == '':
68        my_file = Path(INPUT_MACROS_PATH)
69        if not my_file.is_file():
70            print('warring:input correct macros file path is error')
71            INPUT_MACROS_PATH = ''
72    global OUTPUT_RESULT_PATH
73    if not OUTPUT_RESULT_PATH == '':
74        my_file = Path(OUTPUT_RESULT_PATH)
75        if not my_file.is_dir():
76            print('warring:input correct output file path is error')
77            OUTPUT_RESULT_PATH = ''
78
79
80def os_popen(stmt):
81    '''
82    Get the result of execution according to command parameter
83    '''
84    re = os.popen(stmt).readlines()
85    result = []
86    for i in range(0, len(re)):
87        res = re[i].strip('\n')
88        result.append(res)
89    return result
90
91
92def get_info_from_file(file_path):
93    '''
94    Get the list of contents in the file based on the path parameter
95    '''
96    temp_file_list = []
97    with open(file_path) as file_object:
98        for line in file_object:
99            temp_file_list.append(line.rstrip())
100    return temp_file_list
101
102
103def get_lib_strs():
104    '''
105    Get a list of library file symbols
106    '''
107    temp_lib_name_list = os_popen(
108        'nm -D ' + INPUT_LIB_PATH +
109        '| grep -Ev " U " | grep -Ev " W " | grep -Ev " D " | grep -Ev " V " | grep -Ev " w " | awk \'{print $3}\' | xargs c++filt'
110    )
111    if len(temp_lib_name_list) == 0:
112        print("canot find lib file  error")
113        exit()
114    global LIB_FILE_NAME_LIST
115    for i in temp_lib_name_list:
116        LIB_FILE_NAME_LIST.append(sub_str_name(i))
117    LIB_FILE_NAME_LIST = list(set(LIB_FILE_NAME_LIST))
118    LIB_FILE_NAME_LIST.sort()
119
120
121def get_permission_num(permissions):
122    '''
123    Get the number of arguments of c++ function
124    '''
125    if not permissions.find('()') == -1:
126        return 0
127    else:
128        count_premission = 1
129        current_sym_num = 0
130        for i in permissions:
131            if i == '<':
132                current_sym_num = current_sym_num + 1
133            if i == '>':
134                current_sym_num = current_sym_num - 1
135            if i == ',' and current_sym_num == 0:
136                count_premission = count_premission + 1
137        return count_premission
138
139
140def sub_str_name(iteam):
141    '''
142    Handling redundant information in library file symbol table
143    '''
144    if not iteam.find('non-virtual thunk to ') == -1:
145        iteam = iteam.replace('non-virtual thunk to ', '')
146    if not iteam.find('virtual thunk to ') == -1:
147        iteam = iteam.replace('virtual thunk to ', '')
148    if iteam.find('(') == -1:
149        return iteam
150    else:
151        return iteam[:iteam.index('(')] + '@' + str(get_permission_num(iteam))
152
153
154def get_head_strs():
155    '''
156    Get a list of header file symbols
157    '''
158    head_file_name_list = os_popen('find ' + INPUT_HEAD_PATH + ' -name "*.h"')
159    if len(head_file_name_list) == 0:
160        print('canot find head file error')
161        exit()
162    compile_args = ['-x', 'c++']
163    marcros_list = []
164    if not INPUT_MACROS_PATH == '':
165        temp_macros = get_info_from_file(INPUT_MACROS_PATH)
166        for i in temp_macros:
167            marcros_list.append('-D' + i)
168    for i in head_file_name_list:
169        global CURRENT_FILE_TYPE
170        CURRENT_FILE_TYPE = 0
171        index = clang.cindex.Index.create()
172        parser = index.parse(i, args=compile_args)
173        cursor = parser.cursor
174        traverse(cursor)
175        if CURRENT_FILE_TYPE == 0:
176            index_temp = clang.cindex.Index.create()
177            syspath = ccsyspath.system_include_paths('clang')
178            incargs = [b'-I' + inc for inc in syspath]
179            incargs.append('-I'+INPUT_HEAD_PATH)
180            parser_temp = index_temp.parse(i, args=marcros_list + incargs)
181            cursor_temp = parser_temp.cursor
182            traverse_c(cursor_temp, 0)
183        else:
184            index_temp = clang.cindex.Index.create()
185            syspath = ccsyspath.system_include_paths('clang++')
186            incargs = [b'-I' + inc for inc in syspath]
187            incargs.append('-I'+INPUT_HEAD_PATH)
188            parser_temp = index_temp.parse(i,
189                                           args=marcros_list + compile_args +
190                                           incargs)
191            cursor_temp = parser_temp.cursor
192            traverse_cpp(cursor_temp, '')
193    global HEAD_FILE_NAME_LIST
194    HEAD_FILE_NAME_LIST = list(set(HEAD_FILE_NAME_LIST))
195    HEAD_FILE_NAME_LIST.sort()
196
197
198def traverse_c(node, depth):
199    '''
200    Recursively obtain the symbol list from the c language header file and store it in HEAD_FILE_NAME_LIST
201    '''
202    global HEAD_FILE_NAME_LIST
203    if node.kind == CursorKind.FUNCTION_DECL:
204        if is_has_extern_in_node(node):
205            HEAD_FILE_NAME_LIST.append(node.spelling)
206    if node.kind == CursorKind.VAR_DECL and depth == 1:
207        if is_has_extern_in_node(node):
208            HEAD_FILE_NAME_LIST.append(node.spelling)
209    for n in node.get_children():
210        traverse_c(n, depth + 1)
211
212
213def check_cpp_namespace(depth):
214    if not depth.find('std') == -1:
215        return False
216    if not depth.find('__gnu_cxx') == -1:
217        return False
218    if not depth.find('__cxxabiv1') == -1:
219        return False
220    if not depth.find('__pthread_cleanup_class') == -1:
221        return False
222    return True
223
224
225def traverse_cpp(node, depth):
226    '''
227    Recursively obtain the symbol list from the c++ language header file and store it in HEAD_FILE_NAME_LIST
228    '''
229    global HEAD_FILE_NAME_LIST
230    if node.kind == CursorKind.NAMESPACE or node.kind == CursorKind.CLASS_DECL:
231        if depth == '':
232            depth = node.spelling
233        else:
234            depth = depth + '::' + node.spelling
235    if node.kind == CursorKind.CXX_METHOD and check_cpp_namespace(
236            depth) and not depth == '' and not node.is_pure_virtual_method():
237        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
238                                   str(get_permission_num(node.displayname)))
239    if node.kind == CursorKind.FUNCTION_TEMPLATE and check_cpp_namespace(
240            depth) and not depth == '':
241        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
242                                   str(get_permission_num(node.displayname)))
243    if node.kind == CursorKind.DESTRUCTOR and check_cpp_namespace(depth):
244        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@0')
245    if node.kind == CursorKind.VAR_DECL and check_cpp_namespace(
246            depth) and not depth == '':
247        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling)
248    if node.kind == CursorKind.CONSTRUCTOR and check_cpp_namespace(depth):
249        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
250                                   str(get_permission_num(node.displayname)))
251    for n in node.get_children():
252        traverse_cpp(n, depth)
253
254
255def is_has_extern_in_node(node):
256    for i in node.get_tokens():
257        if i.spelling == 'extern' or i.spelling == '__inline':
258            return False
259    return True
260
261
262def traverse(node):
263    '''
264    Determine the type of the file parameter
265    '''
266    global CURRENT_FILE_TYPE
267    if node.kind == CursorKind.CLASS_DECL:
268        CURRENT_FILE_TYPE = 1
269    for n in node.get_children():
270        traverse(n)
271
272
273def get_compare_result():
274    '''
275    Compare the symbol lists of header and library files and generate a file that stores the results of the comparison
276    '''
277    only_lib_have = list(
278        set(LIB_FILE_NAME_LIST).difference(set(HEAD_FILE_NAME_LIST)))
279    only_head_have = list(
280        set(HEAD_FILE_NAME_LIST).difference(set(LIB_FILE_NAME_LIST)))
281    not_compare_list = []
282    if not INPUT_BLACK_PATH == '':
283        not_compare_list = not_compare_list + get_info_from_file(
284            INPUT_BLACK_PATH)
285    only_lib_have = list(set(only_lib_have).difference(set(not_compare_list)))
286    only_head_have = list(
287        set(only_head_have).difference(set(not_compare_list)))
288    only_lib_have.sort()
289    only_head_have.sort()
290    result = {}
291    result['head_file_path'] = INPUT_HEAD_PATH
292    result['lib_file_path'] = INPUT_LIB_PATH
293    result['only_in_head_file'] = only_head_have
294    result['only_in_lib_file'] = only_lib_have
295    result['head_file_symble_list_num:lib_file_symble_list_nmu'] = str(
296        len(HEAD_FILE_NAME_LIST)) + ':' + str(len(LIB_FILE_NAME_LIST))
297    seconds = time.time()
298    time_str = time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(seconds))
299    out_difference_path = OUTPUT_RESULT_PATH + 'compare_result_' + time_str + '.json'
300    out_lib_path = OUTPUT_RESULT_PATH + 'lib_file_list_' + time_str + '.txt'
301    out_head_path = OUTPUT_RESULT_PATH + 'head_file_list_' + time_str + '.txt'
302    with open(out_difference_path, 'w') as file_obj:
303        json.dump(result, file_obj, indent=4, separators=(',', ':'))
304        file_obj.close()
305    f = open(out_lib_path, 'w')
306    for line in LIB_FILE_NAME_LIST:
307        f.write(line + '\n')
308    f.close()
309    f = open(out_head_path, 'w')
310    for line in HEAD_FILE_NAME_LIST:
311        f.write(line + '\n')
312    f.close()
313
314
315INPUT_LIB_PATH = '' #The path to the entered library file
316INPUT_HEAD_PATH = '' #The path to the entered header file
317INPUT_BLACK_PATH = '' #The path to the entered blacklist file
318INPUT_MACROS_PATH = '' #The path to the entered macro definition file
319OUTPUT_RESULT_PATH = '' #The output path of the result file
320HEAD_FILE_NAME_LIST = [] #header file symbol list
321LIB_FILE_NAME_LIST = [] #library file symbol list
322CURRENT_FILE_TYPE = 0 #current file type
323get_args()
324get_head_strs()
325get_lib_strs()
326get_compare_result()
327