未验证 提交 a0883813 编写于 作者: O openharmony_ci 提交者: Gitee

!447 RM.002.提供API头文件API检测功能

Merge pull request !447 from dhy308/branch_rm002
### 一、环境配置
#### 1、clang
若ubuntu中无clang则需要安装clang
以下为安装命令:
~~~~shell
sudo apt install clang
~~~~
#### 2、python3.8
本程序需要使用python3.8程序和pip3
以下为安装命令:
~~~~shell
sudo apt install python3.8
sudo apt install python3-pip
~~~~
#### 3、安装项目依赖包
使用以下命令来安装项目依赖包
~~~~shell
pip3 install -r requirements.txt
~~~~
### 二、黑名单文件与宏定义文件格式
#### 1、黑名单文件格式
**黑名单文件:**在执行脚本时可以指定文本文件为黑名单文件,在比较时就不会将黑名单文件中出现的符号体现在比较结果中。黑名单文件中每一行为一个忽略比较的符号信息。以下为黑名单文件的样例:
~~~~txt
mq_getattr
getutxent
.....
~~~~
本工具自带一个默认的黑名单文件default_black.txt,后续添加黑名单内容可在此基础上进行添加
#### 2、宏定义文件格式
**宏定义文件:**在执行脚本时可以指定文本文件为宏定义文件,以便于在获取头文件符号列表时可以按照用户希望的方式展开制定宏。宏定义文件中每一行为一个引入的宏定义。以下为宏定义文件的样例:
~~~~txt
_GNU_SOURCE
_BSD_SOURCE
......
~~~~
### 三、执行命令
执行脚本时提示信息如下
~~~~shell
usage: python3.8 compare.py [-h] -l <path> -i <path>
[-b <path>] [-m <path>] [-o <path>]
options:
-h : print this help message #打印帮助信息
-l : input lib file path #库文件路径
-i : input head file path #头文件路径
-b : input blacklist file path #黑名单文件路径(可选)
-m : input macros file path #宏定义文件路径(可选)
-o : result file output path #比较结果输出路径(可选)
~~~~
以下为执行命令的例子
~~~~shell
$python3.8 compare.py -l ~/libc.so -i ~/include
$python3.8 compare.py -h
$python3.8 compare.py -l ~/libc.so -i ~/include -b ~/backlist.txt -m macros.txt -o ~/
~~~~
\ No newline at end of file
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()
_fini
_init
__bss_start
_end
\ No newline at end of file
ccsyspath==1.1.0
clang==14.0
libclang==14.0.1
ply==3.11
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册