Files
phs_v1.0.1.0/third_party/musl/tools/api_cmp_tool/compare.py

327 lines
11 KiB
Python
Raw Normal View History

2024-09-27 19:21:56 +08:00
import argparse
import os
import sys
import json
import ccsyspath
import time
import clang.cindex
from clang.cindex import Cursor
from clang.cindex import CursorKind
from pathlib import Path
from clang.cindex import TranslationUnit
def get_args():
'''
Get and parse the parameters from the console
'''
parse = argparse.ArgumentParser(prog='python3.8 compare.py')
parse.add_argument('-l',
type=str,
required=True,
help='input lib file path')
parse.add_argument('-i',
type=str,
required=True,
help='input head file path')
parse.add_argument('-m', type=str, help='input macros file path')
parse.add_argument('-b', type=str, help='input blacklist file path')
parse.add_argument('-o', type=str, help='result file output path')
args = parse.parse_args()
paramers_value = vars(args)
global INPUT_HEAD_PATH
INPUT_HEAD_PATH = paramers_value['i']
global INPUT_LIB_PATH
INPUT_LIB_PATH = paramers_value['l']
global INPUT_MACROS_PATH
INPUT_MACROS_PATH = paramers_value['m']
INPUT_MACROS_PATH = '' if INPUT_MACROS_PATH == None else INPUT_MACROS_PATH
global INPUT_BLACK_PATH
INPUT_BLACK_PATH = paramers_value['b']
INPUT_BLACK_PATH = '' if INPUT_BLACK_PATH == None else INPUT_BLACK_PATH
global OUTPUT_RESULT_PATH
OUTPUT_RESULT_PATH = paramers_value['o']
OUTPUT_RESULT_PATH = '' if OUTPUT_RESULT_PATH == None else OUTPUT_RESULT_PATH
check_parameters()
def check_parameters():
'''
Check whether the obtained parameters are correct
'''
my_file = Path(INPUT_LIB_PATH)
if not my_file.is_file():
print('please input correct lib file path')
exit()
my_file = Path(INPUT_HEAD_PATH)
if not my_file.is_dir():
print('please input correct head file path')
exit()
global INPUT_BLACK_PATH
if not INPUT_BLACK_PATH == '':
my_file = Path(INPUT_BLACK_PATH)
if not my_file.is_file():
print('warring:input correct blacklist file path is error')
INPUT_BLACK_PATH = ''
global INPUT_MACROS_PATH
if not INPUT_MACROS_PATH == '':
my_file = Path(INPUT_MACROS_PATH)
if not my_file.is_file():
print('warring:input correct macros file path is error')
INPUT_MACROS_PATH = ''
global OUTPUT_RESULT_PATH
if not OUTPUT_RESULT_PATH == '':
my_file = Path(OUTPUT_RESULT_PATH)
if not my_file.is_dir():
print('warring:input correct output file path is error')
OUTPUT_RESULT_PATH = ''
def os_popen(stmt):
'''
Get the result of execution according to command parameter
'''
re = os.popen(stmt).readlines()
result = []
for i in range(0, len(re)):
res = re[i].strip('\n')
result.append(res)
return result
def get_info_from_file(file_path):
'''
Get the list of contents in the file based on the path parameter
'''
temp_file_list = []
with open(file_path) as file_object:
for line in file_object:
temp_file_list.append(line.rstrip())
return temp_file_list
def get_lib_strs():
'''
Get a list of library file symbols
'''
temp_lib_name_list = os_popen(
'nm -D ' + INPUT_LIB_PATH +
'| grep -Ev " U " | grep -Ev " W " | grep -Ev " D " | grep -Ev " V " | grep -Ev " w " | awk \'{print $3}\' | xargs c++filt'
)
if len(temp_lib_name_list) == 0:
print("canot find lib file error")
exit()
global LIB_FILE_NAME_LIST
for i in temp_lib_name_list:
LIB_FILE_NAME_LIST.append(sub_str_name(i))
LIB_FILE_NAME_LIST = list(set(LIB_FILE_NAME_LIST))
LIB_FILE_NAME_LIST.sort()
def get_permission_num(permissions):
'''
Get the number of arguments of c++ function
'''
if not permissions.find('()') == -1:
return 0
else:
count_premission = 1
current_sym_num = 0
for i in permissions:
if i == '<':
current_sym_num = current_sym_num + 1
if i == '>':
current_sym_num = current_sym_num - 1
if i == ',' and current_sym_num == 0:
count_premission = count_premission + 1
return count_premission
def sub_str_name(iteam):
'''
Handling redundant information in library file symbol table
'''
if not iteam.find('non-virtual thunk to ') == -1:
iteam = iteam.replace('non-virtual thunk to ', '')
if not iteam.find('virtual thunk to ') == -1:
iteam = iteam.replace('virtual thunk to ', '')
if iteam.find('(') == -1:
return iteam
else:
return iteam[:iteam.index('(')] + '@' + str(get_permission_num(iteam))
def get_head_strs():
'''
Get a list of header file symbols
'''
head_file_name_list = os_popen('find ' + INPUT_HEAD_PATH + ' -name "*.h"')
if len(head_file_name_list) == 0:
print('canot find head file error')
exit()
compile_args = ['-x', 'c++']
marcros_list = []
if not INPUT_MACROS_PATH == '':
temp_macros = get_info_from_file(INPUT_MACROS_PATH)
for i in temp_macros:
marcros_list.append('-D' + i)
for i in head_file_name_list:
global CURRENT_FILE_TYPE
CURRENT_FILE_TYPE = 0
index = clang.cindex.Index.create()
parser = index.parse(i, args=compile_args)
cursor = parser.cursor
traverse(cursor)
if CURRENT_FILE_TYPE == 0:
index_temp = clang.cindex.Index.create()
syspath = ccsyspath.system_include_paths('clang')
incargs = [b'-I' + inc for inc in syspath]
incargs.append('-I'+INPUT_HEAD_PATH)
parser_temp = index_temp.parse(i, args=marcros_list + incargs)
cursor_temp = parser_temp.cursor
traverse_c(cursor_temp, 0)
else:
index_temp = clang.cindex.Index.create()
syspath = ccsyspath.system_include_paths('clang++')
incargs = [b'-I' + inc for inc in syspath]
incargs.append('-I'+INPUT_HEAD_PATH)
parser_temp = index_temp.parse(i,
args=marcros_list + compile_args +
incargs)
cursor_temp = parser_temp.cursor
traverse_cpp(cursor_temp, '')
global HEAD_FILE_NAME_LIST
HEAD_FILE_NAME_LIST = list(set(HEAD_FILE_NAME_LIST))
HEAD_FILE_NAME_LIST.sort()
def traverse_c(node, depth):
'''
Recursively obtain the symbol list from the c language header file and store it in HEAD_FILE_NAME_LIST
'''
global HEAD_FILE_NAME_LIST
if node.kind == CursorKind.FUNCTION_DECL:
if is_has_extern_in_node(node):
HEAD_FILE_NAME_LIST.append(node.spelling)
if node.kind == CursorKind.VAR_DECL and depth == 1:
if is_has_extern_in_node(node):
HEAD_FILE_NAME_LIST.append(node.spelling)
for n in node.get_children():
traverse_c(n, depth + 1)
def check_cpp_namespace(depth):
if not depth.find('std') == -1:
return False
if not depth.find('__gnu_cxx') == -1:
return False
if not depth.find('__cxxabiv1') == -1:
return False
if not depth.find('__pthread_cleanup_class') == -1:
return False
return True
def traverse_cpp(node, depth):
'''
Recursively obtain the symbol list from the c++ language header file and store it in HEAD_FILE_NAME_LIST
'''
global HEAD_FILE_NAME_LIST
if node.kind == CursorKind.NAMESPACE or node.kind == CursorKind.CLASS_DECL:
if depth == '':
depth = node.spelling
else:
depth = depth + '::' + node.spelling
if node.kind == CursorKind.CXX_METHOD and check_cpp_namespace(
depth) and not depth == '' and not node.is_pure_virtual_method():
HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
str(get_permission_num(node.displayname)))
if node.kind == CursorKind.FUNCTION_TEMPLATE and check_cpp_namespace(
depth) and not depth == '':
HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
str(get_permission_num(node.displayname)))
if node.kind == CursorKind.DESTRUCTOR and check_cpp_namespace(depth):
HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@0')
if node.kind == CursorKind.VAR_DECL and check_cpp_namespace(
depth) and not depth == '':
HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling)
if node.kind == CursorKind.CONSTRUCTOR and check_cpp_namespace(depth):
HEAD_FILE_NAME_LIST.append(depth + '::' + node.spelling + '@' +
str(get_permission_num(node.displayname)))
for n in node.get_children():
traverse_cpp(n, depth)
def is_has_extern_in_node(node):
for i in node.get_tokens():
if i.spelling == 'extern' or i.spelling == '__inline':
return False
return True
def traverse(node):
'''
Determine the type of the file parameter
'''
global CURRENT_FILE_TYPE
if node.kind == CursorKind.CLASS_DECL:
CURRENT_FILE_TYPE = 1
for n in node.get_children():
traverse(n)
def get_compare_result():
'''
Compare the symbol lists of header and library files and generate a file that stores the results of the comparison
'''
only_lib_have = list(
set(LIB_FILE_NAME_LIST).difference(set(HEAD_FILE_NAME_LIST)))
only_head_have = list(
set(HEAD_FILE_NAME_LIST).difference(set(LIB_FILE_NAME_LIST)))
not_compare_list = []
if not INPUT_BLACK_PATH == '':
not_compare_list = not_compare_list + get_info_from_file(
INPUT_BLACK_PATH)
only_lib_have = list(set(only_lib_have).difference(set(not_compare_list)))
only_head_have = list(
set(only_head_have).difference(set(not_compare_list)))
only_lib_have.sort()
only_head_have.sort()
result = {}
result['head_file_path'] = INPUT_HEAD_PATH
result['lib_file_path'] = INPUT_LIB_PATH
result['only_in_head_file'] = only_head_have
result['only_in_lib_file'] = only_lib_have
result['head_file_symble_list_num:lib_file_symble_list_nmu'] = str(
len(HEAD_FILE_NAME_LIST)) + ':' + str(len(LIB_FILE_NAME_LIST))
seconds = time.time()
time_str = time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(seconds))
out_difference_path = OUTPUT_RESULT_PATH + 'compare_result_' + time_str + '.json'
out_lib_path = OUTPUT_RESULT_PATH + 'lib_file_list_' + time_str + '.txt'
out_head_path = OUTPUT_RESULT_PATH + 'head_file_list_' + time_str + '.txt'
with open(out_difference_path, 'w') as file_obj:
json.dump(result, file_obj, indent=4, separators=(',', ':'))
file_obj.close()
f = open(out_lib_path, 'w')
for line in LIB_FILE_NAME_LIST:
f.write(line + '\n')
f.close()
f = open(out_head_path, 'w')
for line in HEAD_FILE_NAME_LIST:
f.write(line + '\n')
f.close()
INPUT_LIB_PATH = '' #The path to the entered library file
INPUT_HEAD_PATH = '' #The path to the entered header file
INPUT_BLACK_PATH = '' #The path to the entered blacklist file
INPUT_MACROS_PATH = '' #The path to the entered macro definition file
OUTPUT_RESULT_PATH = '' #The output path of the result file
HEAD_FILE_NAME_LIST = [] #header file symbol list
LIB_FILE_NAME_LIST = [] #library file symbol list
CURRENT_FILE_TYPE = 0 #current file type
get_args()
get_head_strs()
get_lib_strs()
get_compare_result()