1570af302Sopenharmony_ciimport argparse
2570af302Sopenharmony_ciimport os
3570af302Sopenharmony_ciimport sys
4570af302Sopenharmony_ciimport json
5570af302Sopenharmony_ciimport ccsyspath
6570af302Sopenharmony_ciimport time
7570af302Sopenharmony_ciimport clang.cindex
8570af302Sopenharmony_cifrom clang.cindex import Cursor
9570af302Sopenharmony_cifrom clang.cindex import CursorKind
10570af302Sopenharmony_cifrom pathlib import Path
11570af302Sopenharmony_cifrom clang.cindex import TranslationUnit
12570af302Sopenharmony_ci
13570af302Sopenharmony_ci
14570af302Sopenharmony_cidef get_args():
15570af302Sopenharmony_ci    '''
16570af302Sopenharmony_ci    Get and parse the parameters from the console
17570af302Sopenharmony_ci    '''
18570af302Sopenharmony_ci    parse = argparse.ArgumentParser(prog='python3.8 compare.py')
19570af302Sopenharmony_ci    parse.add_argument('-l',
20570af302Sopenharmony_ci                       type=str,
21570af302Sopenharmony_ci                       required=True,
22570af302Sopenharmony_ci                       help='input lib file path')
23570af302Sopenharmony_ci    parse.add_argument('-i',
24570af302Sopenharmony_ci                       type=str,
25570af302Sopenharmony_ci                       required=True,
26570af302Sopenharmony_ci                       help='input head file path')
27570af302Sopenharmony_ci    parse.add_argument('-m', type=str, help='input macros file path')
28570af302Sopenharmony_ci    parse.add_argument('-b', type=str, help='input blacklist file path')
29570af302Sopenharmony_ci    parse.add_argument('-o', type=str, help='result file output path')
30570af302Sopenharmony_ci    args = parse.parse_args()
31570af302Sopenharmony_ci    paramers_value = vars(args)
32570af302Sopenharmony_ci    global INPUT_HEAD_PATH
33570af302Sopenharmony_ci    INPUT_HEAD_PATH = paramers_value['i']
34570af302Sopenharmony_ci    global INPUT_LIB_PATH
35570af302Sopenharmony_ci    INPUT_LIB_PATH = paramers_value['l']
36570af302Sopenharmony_ci    global INPUT_MACROS_PATH
37570af302Sopenharmony_ci    INPUT_MACROS_PATH = paramers_value['m']
38570af302Sopenharmony_ci    INPUT_MACROS_PATH = '' if INPUT_MACROS_PATH == None else INPUT_MACROS_PATH
39570af302Sopenharmony_ci    global INPUT_BLACK_PATH
40570af302Sopenharmony_ci    INPUT_BLACK_PATH = paramers_value['b']
41570af302Sopenharmony_ci    INPUT_BLACK_PATH = '' if INPUT_BLACK_PATH == None else INPUT_BLACK_PATH
42570af302Sopenharmony_ci    global OUTPUT_RESULT_PATH
43570af302Sopenharmony_ci    OUTPUT_RESULT_PATH = paramers_value['o']
44570af302Sopenharmony_ci    OUTPUT_RESULT_PATH = '' if OUTPUT_RESULT_PATH == None else OUTPUT_RESULT_PATH
45570af302Sopenharmony_ci    check_parameters()
46570af302Sopenharmony_ci
47570af302Sopenharmony_ci
48570af302Sopenharmony_cidef check_parameters():
49570af302Sopenharmony_ci    '''
50570af302Sopenharmony_ci    Check whether the obtained parameters are correct
51570af302Sopenharmony_ci    '''
52570af302Sopenharmony_ci    my_file = Path(INPUT_LIB_PATH)
53570af302Sopenharmony_ci    if not my_file.is_file():
54570af302Sopenharmony_ci        print('please input correct lib file path')
55570af302Sopenharmony_ci        exit()
56570af302Sopenharmony_ci    my_file = Path(INPUT_HEAD_PATH)
57570af302Sopenharmony_ci    if not my_file.is_dir():
58570af302Sopenharmony_ci        print('please input correct head file path')
59570af302Sopenharmony_ci        exit()
60570af302Sopenharmony_ci    global INPUT_BLACK_PATH
61570af302Sopenharmony_ci    if not INPUT_BLACK_PATH == '':
62570af302Sopenharmony_ci        my_file = Path(INPUT_BLACK_PATH)
63570af302Sopenharmony_ci        if not my_file.is_file():
64570af302Sopenharmony_ci            print('warring:input correct blacklist file path is error')
65570af302Sopenharmony_ci            INPUT_BLACK_PATH = ''
66570af302Sopenharmony_ci    global INPUT_MACROS_PATH
67570af302Sopenharmony_ci    if not INPUT_MACROS_PATH == '':
68570af302Sopenharmony_ci        my_file = Path(INPUT_MACROS_PATH)
69570af302Sopenharmony_ci        if not my_file.is_file():
70570af302Sopenharmony_ci            print('warring:input correct macros file path is error')
71570af302Sopenharmony_ci            INPUT_MACROS_PATH = ''
72570af302Sopenharmony_ci    global OUTPUT_RESULT_PATH
73570af302Sopenharmony_ci    if not OUTPUT_RESULT_PATH == '':
74570af302Sopenharmony_ci        my_file = Path(OUTPUT_RESULT_PATH)
75570af302Sopenharmony_ci        if not my_file.is_dir():
76570af302Sopenharmony_ci            print('warring:input correct output file path is error')
77570af302Sopenharmony_ci            OUTPUT_RESULT_PATH = ''
78570af302Sopenharmony_ci
79570af302Sopenharmony_ci
80570af302Sopenharmony_cidef os_popen(stmt):
81570af302Sopenharmony_ci    '''
82570af302Sopenharmony_ci    Get the result of execution according to command parameter
83570af302Sopenharmony_ci    '''
84570af302Sopenharmony_ci    re = os.popen(stmt).readlines()
85570af302Sopenharmony_ci    result = []
86570af302Sopenharmony_ci    for i in range(0, len(re)):
87570af302Sopenharmony_ci        res = re[i].strip('\n')
88570af302Sopenharmony_ci        result.append(res)
89570af302Sopenharmony_ci    return result
90570af302Sopenharmony_ci
91570af302Sopenharmony_ci
92570af302Sopenharmony_cidef get_info_from_file(file_path):
93570af302Sopenharmony_ci    '''
94570af302Sopenharmony_ci    Get the list of contents in the file based on the path parameter
95570af302Sopenharmony_ci    '''
96570af302Sopenharmony_ci    temp_file_list = []
97570af302Sopenharmony_ci    with open(file_path) as file_object:
98570af302Sopenharmony_ci        for line in file_object:
99570af302Sopenharmony_ci            temp_file_list.append(line.rstrip())
100570af302Sopenharmony_ci    return temp_file_list
101570af302Sopenharmony_ci
102570af302Sopenharmony_ci
103570af302Sopenharmony_cidef get_lib_strs():
104570af302Sopenharmony_ci    '''
105570af302Sopenharmony_ci    Get a list of library file symbols
106570af302Sopenharmony_ci    '''
107570af302Sopenharmony_ci    temp_lib_name_list = os_popen(
108570af302Sopenharmony_ci        'nm -D ' + INPUT_LIB_PATH +
109570af302Sopenharmony_ci        '| grep -Ev " U " | grep -Ev " W " | grep -Ev " D " | grep -Ev " V " | grep -Ev " w " | awk \'{print $3}\' | xargs c++filt'
110570af302Sopenharmony_ci    )
111570af302Sopenharmony_ci    if len(temp_lib_name_list) == 0:
112570af302Sopenharmony_ci        print("canot find lib file  error")
113570af302Sopenharmony_ci        exit()
114570af302Sopenharmony_ci    global LIB_FILE_NAME_LIST
115570af302Sopenharmony_ci    for i in temp_lib_name_list:
116570af302Sopenharmony_ci        LIB_FILE_NAME_LIST.append(sub_str_name(i))
117570af302Sopenharmony_ci    LIB_FILE_NAME_LIST = list(set(LIB_FILE_NAME_LIST))
118570af302Sopenharmony_ci    LIB_FILE_NAME_LIST.sort()
119570af302Sopenharmony_ci
120570af302Sopenharmony_ci
121570af302Sopenharmony_cidef get_permission_num(permissions):
122570af302Sopenharmony_ci    '''
123570af302Sopenharmony_ci    Get the number of arguments of c++ function
124570af302Sopenharmony_ci    '''
125570af302Sopenharmony_ci    if not permissions.find('()') == -1:
126570af302Sopenharmony_ci        return 0
127570af302Sopenharmony_ci    else:
128570af302Sopenharmony_ci        count_premission = 1
129570af302Sopenharmony_ci        current_sym_num = 0
130570af302Sopenharmony_ci        for i in permissions:
131570af302Sopenharmony_ci            if i == '<':
132570af302Sopenharmony_ci                current_sym_num = current_sym_num + 1
133570af302Sopenharmony_ci            if i == '>':
134570af302Sopenharmony_ci                current_sym_num = current_sym_num - 1
135570af302Sopenharmony_ci            if i == ',' and current_sym_num == 0:
136570af302Sopenharmony_ci                count_premission = count_premission + 1
137570af302Sopenharmony_ci        return count_premission
138570af302Sopenharmony_ci
139570af302Sopenharmony_ci
140570af302Sopenharmony_cidef sub_str_name(iteam):
141570af302Sopenharmony_ci    '''
142570af302Sopenharmony_ci    Handling redundant information in library file symbol table
143570af302Sopenharmony_ci    '''
144570af302Sopenharmony_ci    if not iteam.find('non-virtual thunk to ') == -1:
145570af302Sopenharmony_ci        iteam = iteam.replace('non-virtual thunk to ', '')
146570af302Sopenharmony_ci    if not iteam.find('virtual thunk to ') == -1:
147570af302Sopenharmony_ci        iteam = iteam.replace('virtual thunk to ', '')
148570af302Sopenharmony_ci    if iteam.find('(') == -1:
149570af302Sopenharmony_ci        return iteam
150570af302Sopenharmony_ci    else:
151570af302Sopenharmony_ci        return iteam[:iteam.index('(')] + '@' + str(get_permission_num(iteam))
152570af302Sopenharmony_ci
153570af302Sopenharmony_ci
154570af302Sopenharmony_cidef get_head_strs():
155570af302Sopenharmony_ci    '''
156570af302Sopenharmony_ci    Get a list of header file symbols
157570af302Sopenharmony_ci    '''
158570af302Sopenharmony_ci    head_file_name_list = os_popen('find ' + INPUT_HEAD_PATH + ' -name "*.h"')
159570af302Sopenharmony_ci    if len(head_file_name_list) == 0:
160570af302Sopenharmony_ci        print('canot find head file error')
161570af302Sopenharmony_ci        exit()
162570af302Sopenharmony_ci    compile_args = ['-x', 'c++']
163570af302Sopenharmony_ci    marcros_list = []
164570af302Sopenharmony_ci    if not INPUT_MACROS_PATH == '':
165570af302Sopenharmony_ci        temp_macros = get_info_from_file(INPUT_MACROS_PATH)
166570af302Sopenharmony_ci        for i in temp_macros:
167570af302Sopenharmony_ci            marcros_list.append('-D' + i)
168570af302Sopenharmony_ci    for i in head_file_name_list:
169570af302Sopenharmony_ci        global CURRENT_FILE_TYPE
170570af302Sopenharmony_ci        CURRENT_FILE_TYPE = 0
171570af302Sopenharmony_ci        index = clang.cindex.Index.create()
172570af302Sopenharmony_ci        parser = index.parse(i, args=compile_args)
173570af302Sopenharmony_ci        cursor = parser.cursor
174570af302Sopenharmony_ci        traverse(cursor)
175570af302Sopenharmony_ci        if CURRENT_FILE_TYPE == 0:
176570af302Sopenharmony_ci            index_temp = clang.cindex.Index.create()
177570af302Sopenharmony_ci            syspath = ccsyspath.system_include_paths('clang')
178570af302Sopenharmony_ci            incargs = [b'-I' + inc for inc in syspath]
179570af302Sopenharmony_ci            incargs.append('-I'+INPUT_HEAD_PATH)
180570af302Sopenharmony_ci            parser_temp = index_temp.parse(i, args=marcros_list + incargs)
181570af302Sopenharmony_ci            cursor_temp = parser_temp.cursor
182570af302Sopenharmony_ci            traverse_c(cursor_temp, 0)
183570af302Sopenharmony_ci        else:
184570af302Sopenharmony_ci            index_temp = clang.cindex.Index.create()
185570af302Sopenharmony_ci            syspath = ccsyspath.system_include_paths('clang++')
186570af302Sopenharmony_ci            incargs = [b'-I' + inc for inc in syspath]
187570af302Sopenharmony_ci            incargs.append('-I'+INPUT_HEAD_PATH)
188570af302Sopenharmony_ci            parser_temp = index_temp.parse(i,
189570af302Sopenharmony_ci                                           args=marcros_list + compile_args +
190570af302Sopenharmony_ci                                           incargs)
191570af302Sopenharmony_ci            cursor_temp = parser_temp.cursor
192570af302Sopenharmony_ci            traverse_cpp(cursor_temp, '')
193570af302Sopenharmony_ci    global HEAD_FILE_NAME_LIST
194570af302Sopenharmony_ci    HEAD_FILE_NAME_LIST = list(set(HEAD_FILE_NAME_LIST))
195570af302Sopenharmony_ci    HEAD_FILE_NAME_LIST.sort()
196570af302Sopenharmony_ci
197570af302Sopenharmony_ci
198570af302Sopenharmony_cidef traverse_c(node, depth):
199570af302Sopenharmony_ci    '''
200570af302Sopenharmony_ci    Recursively obtain the symbol list from the c language header file and store it in HEAD_FILE_NAME_LIST
201570af302Sopenharmony_ci    '''
202570af302Sopenharmony_ci    global HEAD_FILE_NAME_LIST
203570af302Sopenharmony_ci    if node.kind == CursorKind.FUNCTION_DECL:
204570af302Sopenharmony_ci        if is_has_extern_in_node(node):
205570af302Sopenharmony_ci            HEAD_FILE_NAME_LIST.append(node.spelling)
206570af302Sopenharmony_ci    if node.kind == CursorKind.VAR_DECL and depth == 1:
207570af302Sopenharmony_ci        if is_has_extern_in_node(node):
208570af302Sopenharmony_ci            HEAD_FILE_NAME_LIST.append(node.spelling)
209570af302Sopenharmony_ci    for n in node.get_children():
210570af302Sopenharmony_ci        traverse_c(n, depth + 1)
211570af302Sopenharmony_ci
212570af302Sopenharmony_ci
213570af302Sopenharmony_cidef check_cpp_namespace(depth):
214570af302Sopenharmony_ci    if not depth.find('std') == -1:
215570af302Sopenharmony_ci        return False
216570af302Sopenharmony_ci    if not depth.find('__gnu_cxx') == -1:
217570af302Sopenharmony_ci        return False
218570af302Sopenharmony_ci    if not depth.find('__cxxabiv1') == -1:
219570af302Sopenharmony_ci        return False
220570af302Sopenharmony_ci    if not depth.find('__pthread_cleanup_class') == -1:
221570af302Sopenharmony_ci        return False
222570af302Sopenharmony_ci    return True
223570af302Sopenharmony_ci
224570af302Sopenharmony_ci
225570af302Sopenharmony_cidef traverse_cpp(node, depth):
226570af302Sopenharmony_ci    '''
227570af302Sopenharmony_ci    Recursively obtain the symbol list from the c++ language header file and store it in HEAD_FILE_NAME_LIST
228570af302Sopenharmony_ci    '''
229570af302Sopenharmony_ci    global HEAD_FILE_NAME_LIST
230570af302Sopenharmony_ci    if node.kind == CursorKind.NAMESPACE or node.kind == CursorKind.CLASS_DECL:
231570af302Sopenharmony_ci        if depth == '':
232570af302Sopenharmony_ci            depth = node.spelling
233570af302Sopenharmony_ci        else:
234570af302Sopenharmony_ci            depth = depth + '::' + node.spelling
235570af302Sopenharmony_ci    if node.kind == CursorKind.CXX_METHOD and check_cpp_namespace(
236570af302Sopenharmony_ci            depth) and not depth == '' and not node.is_pure_virtual_method():
237570af302Sopenharmony_ci        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
238570af302Sopenharmony_ci                                   str(get_permission_num(node.displayname)))
239570af302Sopenharmony_ci    if node.kind == CursorKind.FUNCTION_TEMPLATE and check_cpp_namespace(
240570af302Sopenharmony_ci            depth) and not depth == '':
241570af302Sopenharmony_ci        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
242570af302Sopenharmony_ci                                   str(get_permission_num(node.displayname)))
243570af302Sopenharmony_ci    if node.kind == CursorKind.DESTRUCTOR and check_cpp_namespace(depth):
244570af302Sopenharmony_ci        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@0')
245570af302Sopenharmony_ci    if node.kind == CursorKind.VAR_DECL and check_cpp_namespace(
246570af302Sopenharmony_ci            depth) and not depth == '':
247570af302Sopenharmony_ci        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling)
248570af302Sopenharmony_ci    if node.kind == CursorKind.CONSTRUCTOR and check_cpp_namespace(depth):
249570af302Sopenharmony_ci        HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
250570af302Sopenharmony_ci                                   str(get_permission_num(node.displayname)))
251570af302Sopenharmony_ci    for n in node.get_children():
252570af302Sopenharmony_ci        traverse_cpp(n, depth)
253570af302Sopenharmony_ci
254570af302Sopenharmony_ci
255570af302Sopenharmony_cidef is_has_extern_in_node(node):
256570af302Sopenharmony_ci    for i in node.get_tokens():
257570af302Sopenharmony_ci        if i.spelling == 'extern' or i.spelling == '__inline':
258570af302Sopenharmony_ci            return False
259570af302Sopenharmony_ci    return True
260570af302Sopenharmony_ci
261570af302Sopenharmony_ci
262570af302Sopenharmony_cidef traverse(node):
263570af302Sopenharmony_ci    '''
264570af302Sopenharmony_ci    Determine the type of the file parameter
265570af302Sopenharmony_ci    '''
266570af302Sopenharmony_ci    global CURRENT_FILE_TYPE
267570af302Sopenharmony_ci    if node.kind == CursorKind.CLASS_DECL:
268570af302Sopenharmony_ci        CURRENT_FILE_TYPE = 1
269570af302Sopenharmony_ci    for n in node.get_children():
270570af302Sopenharmony_ci        traverse(n)
271570af302Sopenharmony_ci
272570af302Sopenharmony_ci
273570af302Sopenharmony_cidef get_compare_result():
274570af302Sopenharmony_ci    '''
275570af302Sopenharmony_ci    Compare the symbol lists of header and library files and generate a file that stores the results of the comparison
276570af302Sopenharmony_ci    '''
277570af302Sopenharmony_ci    only_lib_have = list(
278570af302Sopenharmony_ci        set(LIB_FILE_NAME_LIST).difference(set(HEAD_FILE_NAME_LIST)))
279570af302Sopenharmony_ci    only_head_have = list(
280570af302Sopenharmony_ci        set(HEAD_FILE_NAME_LIST).difference(set(LIB_FILE_NAME_LIST)))
281570af302Sopenharmony_ci    not_compare_list = []
282570af302Sopenharmony_ci    if not INPUT_BLACK_PATH == '':
283570af302Sopenharmony_ci        not_compare_list = not_compare_list + get_info_from_file(
284570af302Sopenharmony_ci            INPUT_BLACK_PATH)
285570af302Sopenharmony_ci    only_lib_have = list(set(only_lib_have).difference(set(not_compare_list)))
286570af302Sopenharmony_ci    only_head_have = list(
287570af302Sopenharmony_ci        set(only_head_have).difference(set(not_compare_list)))
288570af302Sopenharmony_ci    only_lib_have.sort()
289570af302Sopenharmony_ci    only_head_have.sort()
290570af302Sopenharmony_ci    result = {}
291570af302Sopenharmony_ci    result['head_file_path'] = INPUT_HEAD_PATH
292570af302Sopenharmony_ci    result['lib_file_path'] = INPUT_LIB_PATH
293570af302Sopenharmony_ci    result['only_in_head_file'] = only_head_have
294570af302Sopenharmony_ci    result['only_in_lib_file'] = only_lib_have
295570af302Sopenharmony_ci    result['head_file_symble_list_num:lib_file_symble_list_nmu'] = str(
296570af302Sopenharmony_ci        len(HEAD_FILE_NAME_LIST)) + ':' + str(len(LIB_FILE_NAME_LIST))
297570af302Sopenharmony_ci    seconds = time.time()
298570af302Sopenharmony_ci    time_str = time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(seconds))
299570af302Sopenharmony_ci    out_difference_path = OUTPUT_RESULT_PATH + 'compare_result_' + time_str + '.json'
300570af302Sopenharmony_ci    out_lib_path = OUTPUT_RESULT_PATH + 'lib_file_list_' + time_str + '.txt'
301570af302Sopenharmony_ci    out_head_path = OUTPUT_RESULT_PATH + 'head_file_list_' + time_str + '.txt'
302570af302Sopenharmony_ci    with open(out_difference_path, 'w') as file_obj:
303570af302Sopenharmony_ci        json.dump(result, file_obj, indent=4, separators=(',', ':'))
304570af302Sopenharmony_ci        file_obj.close()
305570af302Sopenharmony_ci    f = open(out_lib_path, 'w')
306570af302Sopenharmony_ci    for line in LIB_FILE_NAME_LIST:
307570af302Sopenharmony_ci        f.write(line + '\n')
308570af302Sopenharmony_ci    f.close()
309570af302Sopenharmony_ci    f = open(out_head_path, 'w')
310570af302Sopenharmony_ci    for line in HEAD_FILE_NAME_LIST:
311570af302Sopenharmony_ci        f.write(line + '\n')
312570af302Sopenharmony_ci    f.close()
313570af302Sopenharmony_ci
314570af302Sopenharmony_ci
315570af302Sopenharmony_ciINPUT_LIB_PATH = '' #The path to the entered library file
316570af302Sopenharmony_ciINPUT_HEAD_PATH = '' #The path to the entered header file
317570af302Sopenharmony_ciINPUT_BLACK_PATH = '' #The path to the entered blacklist file
318570af302Sopenharmony_ciINPUT_MACROS_PATH = '' #The path to the entered macro definition file
319570af302Sopenharmony_ciOUTPUT_RESULT_PATH = '' #The output path of the result file
320570af302Sopenharmony_ciHEAD_FILE_NAME_LIST = [] #header file symbol list
321570af302Sopenharmony_ciLIB_FILE_NAME_LIST = [] #library file symbol list
322570af302Sopenharmony_ciCURRENT_FILE_TYPE = 0 #current file type
323570af302Sopenharmony_ciget_args()
324570af302Sopenharmony_ciget_head_strs()
325570af302Sopenharmony_ciget_lib_strs()
326570af302Sopenharmony_ciget_compare_result()
327