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