diff --git a/capi_parser/readme.md b/capi_parser/readme.md index 7f443769737e9ee3a485b66c7dadde1efcf13374..2b0fc3823b7515eefd6f3f83783971df879a74e0 100644 --- a/capi_parser/readme.md +++ b/capi_parser/readme.md @@ -1,6 +1,8 @@ -1.使用该工具前需要修改[constants.py](./src/utils/constants.py)文件下的StringConstant.LIB_CLANG_PATH和StringConstant.REPLACE_WAREHOUSE; -StringConstant.LIB_CLANG_PATH:libclang.dll共享库(本地的) +1.使用该工具前需要修改[constants.py](./src/utils/constants.py) +文件下的StringConstant.LIB_CLG_PATH、StringConstant.REPLACE_WAREHOUSE、StringConstant.INCLUDE_LIB; +StringConstant.LIB_CLG_PATH:共享库(本地的) REPLACE_WAREHOUSE:拉下来的interface_sdk_c仓的目录(本地的路径) --例如:(去掉磁盘的路径)\\interface_sdk_c +StringConstant.INCLUDE_LIB:# 拉到本地仓的三方库路径 2.环境: 1)python-3.11.4-amd64 @@ -11,7 +13,7 @@ REPLACE_WAREHOUSE:拉下来的interface_sdk_c仓的目录(本地的路径) -- 4)需要把src目录设置为sources root(找到src目录,点击右键,将目标标记为里面) -5)运行的是src目录下的mian.py文件 +5)在interface_sdk_c目录下运行的是src目录下的mian.py文件 3.终端指令 options: diff --git a/capi_parser/src/bin/config.py b/capi_parser/src/bin/config.py index 5fbb32e5da1690046fbc484ef6ef7bee42cc3f31..3a6b34a955b895a50e3f83e6c16da8f60e50e8f8 100644 --- a/capi_parser/src/bin/config.py +++ b/capi_parser/src/bin/config.py @@ -7,8 +7,10 @@ class ToolNameType(enum.Enum): DIFF = 'diff' -toolNameTypeSet = [member.value for name, - member in ToolNameType.__members__.items()] +tool_name_type_set = [ + member.value for name_tool, + member in ToolNameType.__members__.items() +] class FormatType(enum.Enum): @@ -16,8 +18,10 @@ class FormatType(enum.Enum): EXCEL = 'excel' -formatSet = [member.value for name, - member in FormatType.__members__.items()] +format_set = [ + member.value for name_format, + member in FormatType.__members__.items() +] def run_tools(options): @@ -34,5 +38,21 @@ class Config(object): name = 'parser' version = '0.1.0' description = 'Compare the parser the NDKS' - commands = [{"name": "--tool-name", "abbr": "-N", "required": True, "choices": toolNameTypeSet, "type": str, "default": ToolNameType["COLLECT"], "help":"工具名称"}, - {"name": "--parser-path", "abbr": "-P", "required": True, "type": str, "help": "解析路径"}] + commands = [ + { + "name": "--tool-name", + "abbr": "-N", + "required": True, + "choices": tool_name_type_set, + "type": str, + "default": ToolNameType["COLLECT"], + "help": "工具名称" + }, + { + "name": "--parser-path", + "abbr": "-P", + "required": True, + "type": str, + "help": "解析路径" + } + ] diff --git a/capi_parser/src/coreImpl/parser/generating_tables.py b/capi_parser/src/coreImpl/parser/generating_tables.py index 26984f9f49b73db826298e0fed7e077b9a58734e..ae20fa253a7fc6b5923489023b1a52c9f069037b 100644 --- a/capi_parser/src/coreImpl/parser/generating_tables.py +++ b/capi_parser/src/coreImpl/parser/generating_tables.py @@ -1,29 +1,27 @@ import json -import pandas as pd # 用于生成表格 import os +import pandas as pd # 用于生成表格 -def compare_json_file(js_file1, js_file2): # 获取对比结果 +def compare_json_file(js_file1, js_file2): # 获取对比结果 with open(js_file1, 'r', encoding='utf-8') as js1: data1 = json.load(js1) with open(js_file2, 'r') as js2: data2 = json.load(js2) compare_result = [] - only_file1 = [] # 装file1独有的 + only_file1 = [] # 装file1独有的 result_api = filter_compare(data1) - for i in range(len(result_api)): - name1 = result_api[i]["name"] + for it in result_api: + name1 = it["name"] key = 0 for item in data2: - if item["name"]: - name2 = item["name"] - if name1 == name2: - key = 1 - compare_result.append(result_api[i]) - break + if item["name"] == name1: + key = 1 + compare_result.append(it) + break if key == 0: - only_file1.append(result_api[i]) - only_file2 = get_difference_data(compare_result, data2) # 获取file2独有的 + only_file1.append(it) + only_file2 = get_difference_data(compare_result, data2) # 获取file2独有的 return compare_result, only_file1, only_file2 @@ -33,8 +31,8 @@ def get_difference_data(compare_result, data2): for item in data2: name2 = item["name"] key = 0 - for j in range(len(compare_result)): - name1 = compare_result[j]["name"] + for it in compare_result: + name1 = it["name"] if name2 == name1: key = 1 break @@ -43,43 +41,50 @@ def get_difference_data(compare_result, data2): return only_file2 -def filter_compare(data1): # 获取函数和变量 +def filter_compare(data1): # 获取函数和变量 result_api = [] - for i in range(len(data1)): - for item1 in data1[i]["children"]: # 抛开根节点 + for it in data1: + for item1 in it["children"]: # 抛开根节点 if (item1["kind"] == 'FUNCTION_DECL' or item1["kind"] == 'VAR_DECL') and item1["is_extern"]: item = filter_func(item1) result_api.append(item) return result_api +def get_parm(item, parm): + if item["parm"]: + for i in range(len(item["parm"])): + if item["parm"][i]["kind"] != 'PARM_DECL': + continue + else: + str_parm = item["parm"][i]["type"] + ' ' + item["parm"][i]["name"] + parm.append(str_parm) + item["parm"] = parm + + def filter_func(item): - del item["is_extern"] # 剔除is_extern键值对,过滤后都是extern + del item["is_extern"] # 剔除is_extern键值对,过滤后都是extern del item["comment"] item["location_path"] = item["location"]["location_path"] item["location"] = item["location"]["location_line"] if item["kind"] == 'FUNCTION_DECL': item["kind"] = '函数类型' - parm = [] # 装函数参数 + parm = [] # 装函数参数 if "parm" in item: - if item["parm"]: - for i in range(len(item["parm"])): - if item["parm"][i]["kind"] != 'PARM_DECL': - continue - else: - str_parm = item["parm"][i]["type"] + ' ' + item["parm"][i]["name"] - parm.append(str_parm) - item["parm"] = parm + get_parm(item, parm) else: item["kind"] = '变量类型' return item def generate_excel(array, name, only_file1, only_file2): - pf = pd.DataFrame.from_dict(array, orient='columns') # 将列表转成DataFrame,并且按列的方式读取数据(orient='columns') + # 将列表转成DataFrame,并且按列的方式读取数据(orient='columns') + pf = pd.DataFrame.from_dict(array, orient='columns') pf1 = pd.DataFrame(only_file1) pf2 = pd.DataFrame(only_file2) - columns_map = { # 将列名换为中文名 + + # 将列名换为中文名 + columns_map = { 'name': '名称', 'kind': '节点类型', 'type': '类型', @@ -89,8 +94,9 @@ def generate_excel(array, name, only_file1, only_file2): 'return_type': '返回类型', 'parm': '参数' } + pf.rename(columns=columns_map, inplace=True) - with pd.ExcelWriter(name) as writer: # 生成该表格 + with pd.ExcelWriter(name) as writer: # 生成该表格 pf.to_excel(writer, sheet_name='对比结果', index=False) pf1.to_excel(writer, sheet_name='生成json独有', index=False) pf2.to_excel(writer, sheet_name='原json独有', index=False) @@ -102,15 +108,16 @@ def increase_sheet(array, name, sheet): pf.to_excel(writer, sheet_name=sheet, index=False) -def get_json_file(json_file_new, json_file): # 获取生成的json文件 - json_file1 = r'{}'.format(json_file_new) # 获取要对比的json文件 +def get_json_file(json_file_new, json_file): # 获取生成的json文件 + json_file1 = r'{}'.format(json_file_new) # 获取要对比的json文件 json_file2 = json_file - head_name = os.path.splitext(json_file1) # 去掉文件名后缀 - head_name = head_name[0] + '.xlsx' # 加后缀 + head_name = os.path.splitext(json_file1) # 去掉文件名后缀 + head_name = head_name[0] + '.xlsx' # 加后缀 result_list = [] only_file1 = [] only_file2 = [] - for i in range(len(json_file2)): # 对比每一个json(目录下的) - result_list, only_file1, only_file2 = compare_json_file(json_file1, json_file2[i]) # 对比两个json文件 + for item in json_file2: # 对比每一个json(目录下的) + # 对比两个json文件 + result_list, only_file1, only_file2 = compare_json_file(json_file1, item) - return result_list, head_name, only_file1, only_file2 # 返回对比数据,和所需表格名 + return result_list, head_name, only_file1, only_file2 # 返回对比数据,和所需表格名 diff --git a/capi_parser/src/coreImpl/parser/parse_include.py b/capi_parser/src/coreImpl/parser/parse_include.py index 365a377eb1bde7829f153fd5210055fdfbdc60e9..e41113323d080c5a2cbf87e1c2cef4024d48029d 100644 --- a/capi_parser/src/coreImpl/parser/parse_include.py +++ b/capi_parser/src/coreImpl/parser/parse_include.py @@ -1,26 +1,26 @@ import re -import clang.cindex -from clang.cindex import Config # 配置 -from clang.cindex import Index # 主要API -from clang.cindex import CursorKind # 索引结点的类别 -from clang.cindex import TypeKind # 节点的语义类别 import os +import clang.cindex +from clang.cindex import Config +from clang.cindex import Index +from clang.cindex import CursorKind +from clang.cindex import TypeKind +from utils.constants import StringConstant -def find_parent(cursor): # 获取父节点 +def find_parent(cursor): # 获取父节点 cursor_parent = cursor.semantic_parent if cursor_parent is not None: if cursor_parent.kind == CursorKind.VAR_DECL: # 父节点为VAR_DECL 用于整型变量节点 return cursor_parent.kind - elif cursor_parent.kind == CursorKind.STRUCT_DECL or cursor_parent.kind == CursorKind.UNION_DECL: # 用于判断里面成员属于那类 + # 用于判断里面成员属于那类 + elif cursor_parent.kind == CursorKind.STRUCT_DECL or cursor_parent.kind == CursorKind.UNION_DECL: return cursor_parent.kind else: parent = cursor_parent.semantic_parent if parent is not None: return parent.kind - else: - return None def processing_no_child(cursor, data): # 处理没有子节点的节点 @@ -41,34 +41,41 @@ def processing_no_child(cursor, data): # 处理没有子节点的节点 data["integer_value"] = token.spelling # 获取整型变量值 +def get_complex_def(tokens_new, count_token, tokens, data): + count = 1 + logo = 0 + logo_com = 0 + count_com = 0 + for token_2 in tokens_new: + if token_2.spelling == ')': + logo = 1 + break + else: + count += 1 + if count_token == count: + pass + elif logo == 1: # 获取复合型宏定义宏名 + logo_com = logo + count_com = count + 1 + tokens_name = tokens[:count + 1] + data["name"] = ''.join([token.spelling for token in tokens_name]) + return logo_com, count_com + + def processing_complex_def(tokens, data): # 处理复合型宏 tokens_new = tokens[1:] # 跳过正常宏名 logo_com = 0 # 记录复合型,复合型文本也得根据这个 count_com = 0 - count_token = len(tokens_new) # value () + count_token = len(tokens_new) # value () for token in tokens_new: if token.kind.name == 'KEYWORD': break if token.kind.name == 'IDENTIFIER': - count = 1 - logo = 0 - for token_2 in tokens_new: - if token_2.spelling == ')': - logo = 1 - break - else: - count += 1 - if count_token == count: - pass - elif logo == 1: # 获取复合型宏定义宏名 - logo_com = logo - count_com = count + 1 - tokens_name = tokens[:count + 1] - data["name"] = ''.join([token.spelling for token in tokens_name]) + logo_com, count_com = get_complex_def(tokens_new, count_token, tokens, data) get_def_text(tokens, data, logo_com, count_com) # 获取宏文本 -def get_def_text(tokens, data, logo_compose, count_compose): # 获取宏文本 +def get_def_text(tokens, data, logo_compose, count_compose): # 获取宏文本 if logo_compose == 1: marco_expansion = ''.join([token.spelling for token in tokens[count_compose:]]) # 获取宏文本,有就记录,没有不管 if marco_expansion: @@ -76,7 +83,7 @@ def get_def_text(tokens, data, logo_compose, count_compose): else: pass else: - marco_expansion = ''.join([token.spelling for token in tokens[1:]]) # 获取宏文本,有就记录,没有不管 + marco_expansion = ''.join([token.spelling for token in tokens[1:]]) # 获取宏文本,有就记录,没有不管 if marco_expansion: data["text"] = marco_expansion else: @@ -91,13 +98,11 @@ def get_token(cursor): return tokens -def judgment_extern(cursor, data): # 判断是否带有extern - is_extern = None +def judgment_extern(cursor, data): # 判断是否带有extern tokens = get_token(cursor) if cursor.kind == CursorKind.FUNCTION_DECL: if 'static' in tokens: is_extern = False - # elif 'deprecated' in tokens and ('attribute' in tokens or '__declspec' in tokens): elif 'deprecated' in tokens: is_extern = False else: @@ -109,13 +114,11 @@ def judgment_extern(cursor, data): is_extern = False else: is_extern = True - if is_extern: - data["is_extern"] = is_extern - else: - data["is_extern"] = is_extern + data["is_extern"] = is_extern -def binary_operator(cursor, data): # 二元操作符处理 + +def binary_operator(cursor, data): # 二元操作符处理 data["name"] = "binary_ope_no_spelling" tokens = cursor.get_tokens() spelling_arr = ['<<', '>>', '+', '-', '*', '/'] @@ -124,29 +127,29 @@ def binary_operator(cursor, data): data["operator"] = token.spelling -def distinction_member(cursor, data): # 区别结构体和联合体成员 - parent_kind = find_parent(cursor) # 查找父节点类型 +def distinction_member(cursor, data): # 区别结构体和联合体成员 + parent_kind = find_parent(cursor) # 查找父节点类型 if parent_kind == CursorKind.UNION_DECL: data["member"] = "union_member" elif parent_kind == CursorKind.STRUCT_DECL: data["member"] = "struct_member" -def processing_parm(cursor, data): # 函数参数节点处理 - if cursor.spelling: # 函数参数是否带参数名 +def processing_parm(cursor, data): # 函数参数节点处理 + if cursor.spelling: # 函数参数是否带参数名 data["name"] = cursor.spelling else: data["name"] = "arg_no_spelling" - if cursor.type.get_pointee().kind == TypeKind.FUNCTIONPROTO: # 参数为函数指针,获取对应的返回类型 + if cursor.type.get_pointee().kind == TypeKind.FUNCTIONPROTO: # 参数为函数指针,获取对应的返回类型 data["func_pointer_result_type"] = cursor.type.get_pointee().get_result().spelling -def processing_enum(cursor, data): # 获取枚举值 +def processing_enum(cursor, data): # 获取枚举值 data["value"] = cursor.enum_value -def processing_def(cursor, data): # 处理宏定义 +def processing_def(cursor, data): # 处理宏定义 marco_ext = cursor.extent tokens = cursor.translation_unit.get_tokens(extent=marco_ext) # 找到对应的宏定义位置 tokens = list(tokens) # Generator转为list @@ -154,21 +157,20 @@ def processing_def(cursor, data): data["type"] = "def_no_type" -def processing_func(cursor, data): # 处理函数 +def processing_func(cursor, data): # 处理函数 data["return_type"] = cursor.result_type.spelling # 增加返回类型键值对 judgment_extern(cursor, data) -def processing_type(cursor, data): # 没有类型的节点处理 +def processing_type(cursor, data): # 没有类型的节点处理 if cursor.kind == CursorKind.MACRO_INSTANTIATION: # 也属于宏定义 --宏引用 data["type"] = "insta_no_type" elif cursor.kind == CursorKind.INCLUSION_DIRECTIVE: # 头文件也没type,规定 data["type"] = "inclusion_no_type" - return -def processing_name(cursor, data): # 没有名的节点处理 +def processing_name(cursor, data): # 没有名的节点处理 if cursor.kind == CursorKind.PAREN_EXPR: # 括号表达式() data["paren"] = "()" data["name"] = "paren_expr_no_spelling" @@ -177,7 +179,7 @@ def processing_name(cursor, data): data["name"] = "unexposed_expr_no_spelling" -def processing_char(cursor, data): # 字符节点处理 +def processing_char(cursor, data): # 字符节点处理 tokens = list(cursor.get_tokens()) char_value = (tokens[0].spelling.strip("'")) data["name"] = char_value @@ -199,22 +201,24 @@ special_node_process = { } -def processing_special_node(cursor, data, gn_path=None): # 处理需要特殊处理的节点 +def processing_special_node(cursor, data, gn_path=None): # 处理需要特殊处理的节点 loc = { "location_path": '{}'.format(cursor.location.file.name), "location_line": cursor.location.line, "location_column": cursor.location.column } - relative_path = os.path.relpath(cursor.location.file.name, gn_path) # 获取头文件相对路 - loc["location_path"] = relative_path + if gn_path: + relative_path = os.path.relpath(cursor.location.file.name, gn_path) # 获取头文件相对路 + loc["location_path"] = relative_path data["location"] = loc if cursor.kind.name in special_node_process.keys(): node_process = special_node_process[cursor.kind.name] - node_process(cursor, data) # 调用对应节点处理函数 + node_process(cursor, data) # 调用对应节点处理函数 def ast_to_dict(cursor, current_file, gn_path=None, comment=None): # 解析数据的整理 - data = { # 通用 + # 通用 + data = { "name": cursor.spelling, "kind": cursor.kind.name, "type": cursor.type.spelling, @@ -237,23 +241,30 @@ def ast_to_dict(cursor, current_file, gn_path=None, comment=None): # 解析数 if len(children) > 0: if cursor.kind == CursorKind.FUNCTION_DECL: # 函数参数 name = "parm" - elif cursor.kind == CursorKind.ENUM_DECL or cursor.kind == CursorKind.STRUCT_DECL or cursor.kind == CursorKind.UNION_DECL: + elif (cursor.kind == CursorKind.ENUM_DECL + or cursor.kind == CursorKind.STRUCT_DECL + or cursor.kind == CursorKind.UNION_DECL): name = "members" else: name = "children" data[name] = [] - for child in children: - if child.location.file is not None and child.kind != CursorKind.UNEXPOSED_ATTR: # 剔除多余宏定义和跳过UNEXPOSED_ATTR节点 - if child.location.file.name == current_file: - child_data = ast_to_dict(child, current_file, gn_path) - data[name].append(child_data) - else: - pass + processing_ast_node(children, current_file, data, name, gn_path) else: processing_no_child(cursor, data) # 处理没有子节点的节点 return data +def processing_ast_node(children, current_file, data, name, gn_path): + for child in children: + # 剔除多余宏定义和跳过UNEXPOSED_ATTR节点 + if child.location.file is not None and child.kind != CursorKind.UNEXPOSED_ATTR: + if child.location.file.name == current_file: + child_data = ast_to_dict(child, current_file, gn_path) + data[name].append(child_data) + else: + pass + + def preorder_travers_ast(cursor, total, comment, current_file, gn_path=None): # 获取属性 ast_dict = ast_to_dict(cursor, current_file, gn_path, comment) # 获取节点属性 total.append(ast_dict) # 追加到数据统计列表里面 @@ -272,14 +283,13 @@ def get_start_comments(include_path): # 获取每个头文件的最开始注释 return matches -def api_entrance(share_lib, include_path, gn_path, link_path=None): # 统计入口 +def api_entrance(share_lib, include_path, gn_path=None, link_path=None): # 统计入口 # clang.cindex需要用到libclang.dll共享库 所以配置共享库 if Config.loaded: print("config.loaded == true") else: Config.set_library_file(share_lib) print("lib.dll: install path") - # 创建AST索引 index = Index.create() print('=' * 50) @@ -290,26 +300,26 @@ def api_entrance(share_lib, include_path, gn_path, link_path=None): # 统计入 print(args) data_total = [] # 列表对象-用于统计 - for i in range(len(include_path)): # 对每个头文件做处理 - tu = index.parse(include_path[i], args=args, options=options) + for item in include_path: # 对每个头文件做处理 + tu = index.parse(item, args=args, options=options) print(tu) print('=' * 50) ast_root_node = tu.cursor # 获取根节点 print(ast_root_node) - matches = get_start_comments(include_path[i]) # 接收文件最开始的注释 + matches = get_start_comments(item) # 接收文件最开始的注释 # 前序遍历AST - preorder_travers_ast(ast_root_node, data_total, matches, include_path[i], gn_path) # 调用处理函数 + preorder_travers_ast(ast_root_node, data_total, matches, item, gn_path) # 调用处理函数 print('=' * 50) return data_total -def get_include_file(libclang, include_file_path, link_path, gn_path=None): # 库路径、.h文件路径、链接头文件路径 +def get_include_file(include_file_path, link_path, gn_path=None): # 库路径、.h文件路径、链接头文件路径 # libclang.dll库路径 - libclang_path = libclang + libclang_path = StringConstant.LIB_CLG_PATH.value # c头文件的路径 file_path = include_file_path - + print(file_path) # 头文件链接路径 link_include_path = link_path # 可以通过列表传入 data = api_entrance(libclang_path, file_path, gn_path, link_include_path) # 调用接口 diff --git a/capi_parser/src/coreImpl/parser/parser.py b/capi_parser/src/coreImpl/parser/parser.py index 6a5fa11b460afd15f429ea38c0c15370aefaefd8..810dfc40cad3a8be30b9b36675646c93d9deca03 100644 --- a/capi_parser/src/coreImpl/parser/parser.py +++ b/capi_parser/src/coreImpl/parser/parser.py @@ -1,212 +1,237 @@ -import os # 可用于操作目录文件 -import glob # 可用于查找指定目录下指定后缀的文件 -import re # 正则表达是模块--可用于操作文件里面的内容 -import shutil # 拷贝文件 -from coreImpl.parser import parse_include, generating_tables # 引入解析文件 # 引入得到结果表格文件 import json +import os +import glob +import re +import shutil from utils.constants import StringConstant +from coreImpl.parser import parse_include, generating_tables # 引入解析文件 # 引入得到结果表格文件 -def find_gn_file(directory): # 找指定目录下所有GN文件 +def find_gn_file(directory): # 找指定目录下所有GN文件 gn_files = [] - for root, dirs, files in os.walk(directory): # dirpath, dirnames, filenames(对应信息) + for root, _, files in os.walk(directory): # dirpath, dirnames, filenames(对应信息) for file in files: if file.endswith(".gn"): gn_files.append(os.path.join(root, file)) return gn_files -def find_function_file(file, function_name): # 在GN文件中查找指定函数并在有函数名,获取对应sources的值 - with open(file, 'r') as f: - content = f.read() # 获取文件内容 - pattern = r'\b' + re.escape(function_name) + r'\b' # '\b'确保函数名的完全匹配 - matches = re.findall(pattern, content) - f.seek(0) # 回到文件开始位置 - if len(matches): # 是否匹配成功 - sources = [] # 转全部匹配的sources的.h(可能不止一个-headers函数) - f.seek(0) - end = 0 # 记录光标 - for i in range(len(matches)): - # 匹配sources = \[[^\]]*\](匹配方括号内的内容,其中包括一个或多个非右括号字符),\s*:匹配0个或多个空白字符 - pattern = r'sources\s*=\s*\[[^\]]*\]' - sources_match = re.search(pattern, content) - if sources_match: - sources_value = sources_match.group(0) # 获取完整匹配的字符串 - sources_value = re.sub(r'\s', '', sources_value) # 去除源字符串的空白字符(换行符)和空格 - pattern = r'"([^"]+h)"' # 匹配引号中的内容,找对应的.h - source = re.findall(pattern, sources_value) - sources.extend(source) - end += sources_match.end() # 每次找完一个sources的.h路径,记录光标结束位置 - f.seek(end) # 移动光标在该结束位置 - content = f.read() # 从当前位置读取问价内容,防止重复 - return len(matches) > 0, sources - else: - return None, None # gn文件没有对应的函数 +def find_h_file(matches, f, sources): + for mat in matches: + # 匹配sources = \[[^\]]*\](匹配方括号内的内容,其中包括一个或多个非右括号字符),\s*:匹配0个或多个空白字符 + f.seek(mat.span()[0]) + content = f.read() + pattern = r'sources\s*=\s*\[[^\]]*\]' + sources_match = re.search(pattern, content) + if sources_match: + sources_value = sources_match.group(0) # 获取完整匹配的字符串 + sources_value = re.sub(r'\s', '', sources_value) # 去除源字符串的空白字符(换行符)和空格 + pattern = r'"([^"]+h)"' # 匹配引号中的内容,找对应的.h + source = re.findall(pattern, sources_value) + sources.extend(source) -def get_dest_dir(file, function_name): # 获取dest_dir +def find_function_file(file, function_name): # 在GN文件中查找指定函数并在有函数名,获取对应sources的值 with open(file, 'r') as f: - content = f.read() # 获取文件内容 - pattern = r'\b' + re.escape(function_name) + r'\b' # '\b'确保函数名的完全匹配 + content = f.read() # 获取文件内容 + pattern = r'\b' + re.escape(function_name) + r'\b' # '\b'确保函数名的完全匹配 + matches = re.finditer(pattern, content) # finditer会返回位置信息 + f.seek(0) # 回到文件开始位置 + sources = [] # 装全部匹配的sources的.h(可能不止一个-headers函数) + if matches: # 是否匹配成功 + find_h_file(matches, f, sources) + print("where", sources) + return matches, sources + + +def find_dest_dir(matches, content, f): + sources_dir = [] + if matches: + end = 0 + for _ in matches: + pattern = r'dest_dir\s*=\s*"([^"]*)"' + source_match = re.search(pattern, content) + if source_match: + con = source_match.group(1) + sources_dir.append(con) + end += source_match.end() # 每次找完一个sources的.h路径,记录光标结束位置 + f.seek(end) # 移动光标在该结束位置 + content = f.read() + return sources_dir + + +def get_dest_dir(file, function_name): # 获取dest_dir + with open(file, 'r') as f: + content = f.read() # 获取文件内容 + pattern = r'\b' + re.escape(function_name) + r'\b' # '\b'确保函数名的完全匹配 matches = re.findall(pattern, content) f.seek(0) - if matches: - sources_dir = [] - f.seek(0) - end = 0 - for i in range(len(matches)): - pattern = r'dest_dir\s*=\s*"([^"]*)"' - source_match = re.search(pattern, content) - if source_match: - con = source_match.group(1) - con_real = con[1:] - sources_dir.append(con) - end += source_match.end() # 每次找完一个sources的.h路径,记录光标结束位置 - f.seek(end) # 移动光标在该结束位置 - content = f.read() - return sources_dir - else: - return None + sources_dir = find_dest_dir(matches, content, f) + return sources_dir -def find_json_file(gn_file_match): # 找gn文件同级目录下的.json文件 +def find_json_file(gn_file_match): # 找gn文件同级目录下的.json文件 match_json_file = [] directory = os.path.dirname(gn_file_match) - for file in glob.glob(os.path.join(directory, "*.json")): # 统计.json文件 + for file in glob.glob(os.path.join(directory, "*.json")): # 统计.json文件 match_json_file.append(file) return match_json_file # def get_ -def dire_func(gn_file, func_name): # 统计数据的 - matches_file_total = [] # 统计有ohos_ndk_headers函数的gn文件 - json_file_total = [] # 统计跟含有函数的gn文件同级的json文件 - source_include = [] # 统计sources里面的.h - length, source = find_function_file(gn_file, func_name) # 找到包含函数的gn文件和同级目录下的.json文件 - if length: # 保证两个都不为空,source可能为空 - source_include = source # 获取头文件列表 - matches_file_total.append(gn_file) # 调用匹配函数的函数(说明有对应的函数、source) - json_file_total.extend(find_json_file(gn_file)) # 找json +def dire_func(gn_file, func_name): # 统计数据的 + matches_file_total = [] # 统计有ohos_ndk_headers函数的gn文件 + json_file_total = [] # 统计跟含有函数的gn文件同级的json文件 + source_include = [] # 统计sources里面的.h + matches, source = find_function_file(gn_file, func_name) # 找到包含函数的gn文件 + if matches: # 保证两个都不为空,source可能为空 + source_include = source # 获取头文件列表 + matches_file_total.append(gn_file) # 调用匹配函数的函数(说明有对应的函数、source) + json_file_total.extend(find_json_file(gn_file)) # 同级目录下的.json文件 return matches_file_total, json_file_total, source_include -def change_json_file(dict_data, name): # 生成json文件 - file_name = name + '_new' + '.json' # json文件名 - with open(file_name, 'w', encoding='UTF-8') as f: # encoding='UTF-8'能显示中文 +def change_json_file(dict_data, name): # 生成json文件 + file_name = name + '_new' + '.json' # json文件名 + with open(file_name, 'w', encoding='UTF-8') as f: # encoding='UTF-8'能显示中文 # ensure_ascii=False确保能显示中文,indent=4(格式控制)使生成的json样式跟字典一样 json.dump(dict_data, f, ensure_ascii=False, indent=4) return file_name -def change_abs(include_files, dire_path): # 获取.h绝对路径 +def change_abs(include_files, dire_path): # 获取.h绝对路径 abs_path = [] - for j in range(len(include_files)): # 拼接路径,生成绝对路径 + for j_item in include_files: # 拼接路径,生成绝对路径 # os.path.normpath(path):规范或者是格式化路径,它会把所有路径分割符按照操作系统进行替换 # 把规范路径和gn文件对应的目录路径拼接 - if os.path.isabs(include_files[j]): # 是否是绝对路径,是就拼接路径盘,不是就拼接gn目录路径 - head = os.path.splitdrive(dire_path) # 获取windows盘路径 - include_file = os.path.normpath(include_files[j]) - include_file = include_file.replace('\\\\interface\\sdk_c', StringConstant.REPLACE_WAREHOUSE.value) # 去掉绝对路径的双\\,替换为interface_sdk_c + if os.path.isabs(j_item): # 是否是绝对路径,是就拼接路径盘,不是就拼接gn目录路径 + head = os.path.splitdrive(dire_path) # 获取windows盘路径 + include_file = os.path.normpath(j_item) + if 'third_party/node/src' in j_item: + include_file = include_file.replace('\\\\', StringConstant.REPLACE_WAREHOUSE.value + '\\') + else: + # 去掉绝对路径的双\\,替换为interface_sdk_c + include_file = include_file.replace('\\\\interface\\sdk_c', + StringConstant.REPLACE_WAREHOUSE.value) if head: - include_file = os.path.join(head[0], include_file) # 拼接盘和路径 + include_file = os.path.join(head[0], include_file) # 拼接盘和路径 abs_path.append(include_file) else: - relative_path = os.path.abspath(os.path.join(dire_path, os.path.normpath(include_files[j]))) # ../ .解决 + relative_path = os.path.abspath(os.path.join(dire_path, os.path.normpath(j_item))) # ../ .解决 abs_path.append(relative_path) - print("头文件绝对路径:\n", abs_path) print("=" * 50) return abs_path -def get_result_table(json_files, abs_path, lib_path, link_path, gn_path): # 进行处理,生成表格 +def get_result_table(json_files, abs_path, link_path, gn_path): # 进行处理,生成表格 + result_list = [] + head_name = "" + only_file1 = [] + only_file2 = [] if json_files: - file_name = os.path.split(json_files[0]) # 取第一个json名,但我是用列表装的 - file_name = os.path.splitext(file_name[1]) # 取下标1对应的元素(元组) - data = parse_include.get_include_file(lib_path, abs_path, link_path, gn_path) # 获取解析返回的数据 - parse_json_name = change_json_file(data, file_name[0]) # 生成json文件 - result_list, head_name, only_file1, only_file2 = generating_tables.get_json_file(parse_json_name, json_files) # 解析完后,传两个json文件,对比两个json文件,最后生成数据表格 - return result_list, head_name, only_file1, only_file2 - else: - return None, None + file_name = os.path.split(json_files[0]) # 取第一个json名,但我是用列表装的 + file_name = os.path.splitext(file_name[1]) # 取下标1对应的元素(元组) + data = parse_include.get_include_file(abs_path, link_path, gn_path) # 获取解析返回的数据 + parse_json_name = change_json_file(data, file_name[0]) # 生成json文件 + # 解析完后,传两个json文件,对比两个json文件,最后生成数据表格 + result_list, head_name, only_file1, only_file2 = generating_tables.get_json_file(parse_json_name, + json_files) + return result_list, head_name, only_file1, only_file2 def create_dir(sources_dir, gn_file, function_name, link_include_file): - for i in range(len(sources_dir)): - directory = sources_dir[i] - new_dire = os.path.join('sysroot', directory) - new_dire = os.path.normpath(new_dire) - if not os.path.exists(new_dire): - os.makedirs(new_dire) + if sources_dir: + for item in sources_dir: + directory = item + new_dire = os.path.join('sysroot', directory) + new_dire = os.path.normpath(new_dire) + if not os.path.exists(new_dire): + os.makedirs(new_dire) + else: + print("目录已存在") - else: - print("目录已存在") - if new_dire in link_include_file: - pass - else: - link_include_file.append(new_dire) # 添加链接的头文件 - match_files, json_files, include_files = dire_func(gn_file, function_name) - dire_path = os.path.dirname(gn_file) # 获取gn文件路径 - if match_files: - abs_path = change_abs(include_files, dire_path) # 接收.h绝对路径 - for j in range(len(abs_path)): - shutil.copy(abs_path[j], new_dire) - else: - print("在create_dir函数中,原因:gn文件条件不满足") + if new_dire in link_include_file: + pass + else: + link_include_file.append(new_dire) # 添加链接的头文件 + match_files, json_files, include_files = dire_func(gn_file, function_name) + dire_path = os.path.dirname(gn_file) # 获取gn文件路径 + if match_files: + abs_path = change_abs(include_files, dire_path) # 接收.h绝对路径 + for j_item in abs_path: + shutil.copy(j_item, new_dire) + else: + print("在create_dir函数中,原因:gn文件条件不满足") + else: + print("gn文件没有ohos_sdk_headers") def link_include(directory_path, function_names, link_include_file): - gn_file_total = find_gn_file(directory_path) # 查找gn文件 - for i in range(len(gn_file_total)): # 处理每个gn文件 - sources_dir = get_dest_dir(gn_file_total[i], function_names) - create_dir(sources_dir, gn_file_total[i], function_names, link_include_file) - - -def main_entrance(directory_path, function_names, lib_path, link_path): # 主入口 - gn_file_total = find_gn_file(directory_path) # 查找gn文件 - - for i in range(len(gn_file_total)): # 处理每个gn文件 - match_files, json_files, include_files = dire_func(gn_file_total[i], function_names) - dire_path = os.path.dirname(gn_file_total[i]) # 获取gn文件路径 - + gn_file_total = find_gn_file(directory_path) # 查找gn文件 + for item in gn_file_total: # 处理每个gn文件 + sources_dir = get_dest_dir(item, function_names) + if sources_dir: + create_dir(sources_dir, item, function_names, link_include_file) + + +def main_entrance(directory_path, function_names, link_path): # 主入口 + gn_file_total = find_gn_file(directory_path) # 查找gn文件 + result_list_total = [] + only_file1_total = [] + only_file2_total = [] + for item in gn_file_total: # 处理每个gn文件 + match_files, json_files, include_files = dire_func(item, function_names) + dire_path = os.path.dirname(item) # 获取gn文件路径 print("目录路径: {}".format(dire_path)) print("同级json文件:\n", json_files) print("头文件:\n", include_files) - - if match_files: # 符合条件的gn文件 - abs_path = change_abs(include_files, dire_path) # 接收.h绝对路径 - result_list, head_name, only_file1, only_file2 = get_result_table(json_files, abs_path, lib_path, link_path, dire_path) # 接收对比结果信息 - generating_tables.generate_excel(result_list, head_name, only_file1, only_file2) # 转为表格 - if result_list: - print("有匹配项,已生成表格") + if include_files: # 符合条件的gn文件 + abs_path = change_abs(include_files, dire_path) # 接收.h绝对路径 + print("头文件绝对路径:\n", abs_path) + result_list, head_name, only_file1, only_file2 = get_result_table(json_files, abs_path, + link_path, dire_path) # 接收对比结果信息 + if len(result_list) != 0: + result_list_total.extend(result_list) + only_file1_total.extend(only_file1) + only_file2_total.extend(only_file2) + elif head_name == "": + print("gn文件下无json文件") else: - print("没有匹配项 or gn文件下无json文件") + generating_tables.generate_excel(result_list, head_name, only_file1, only_file2) + print("没有匹配项") else: print("gn文件无header函数") + head_name = "result_total.xlsx" # 总结果表格 + generating_tables.generate_excel(result_list_total, head_name, only_file1_total, only_file2_total) def copy_std_lib(link_include_file): std_include = r'sysroot\ndk_musl_include_files' if not os.path.exists(std_include): - shutil.copytree(r'third_party\musl\ndk_musl_include', std_include) + shutil.copytree(StringConstant.INCLUDE_LIB.value, std_include) link_include_file.append(std_include) def find_include(link_include_path): - for dir_path, dir_name, file_name in os.walk('sysroot\\$ndk_headers_out_dir'): + for dir_path, _, _ in os.walk('sysroot\\$ndk_headers_out_dir'): link_include_path.append(dir_path) -def parser(directory_path): # 目录路径 - function_name = StringConstant.FUNK_NAME.value # 匹配的函数名 - libclang_path = StringConstant.LIB_CLANG_PATH.value # 共享库路径 +def parser(directory_path): # 目录路径 + function_name = StringConstant.FUNK_NAME.value # 匹配的函数名 - link_include_path = [] # 装链接头文件路径 - copy_std_lib(link_include_path) # ndk头文件移到sysroot中 + link_include_path = [] # 装链接头文件路径 + copy_std_lib(link_include_path) # ndk头文件移到sysroot中 find_include(link_include_path) link_include(directory_path, function_name, link_include_path) - main_entrance(directory_path, function_name, libclang_path, link_include_path) # 调用入口函数 + main_entrance(directory_path, function_name, link_include_path) # 调用入口函数 + +def parser_include_ast(gn_file_path, include_path): + link_path = [StringConstant.INCLUDE_LIB.value] + data = parse_include.get_include_file(include_path, link_path, gn_file_path) + return data diff --git a/capi_parser/src/main.py b/capi_parser/src/main.py index f7b0ba128fcc957702a5014a38aa34cf252b641c..a4a25d3752c40c164e1895481a23b5de6285f8a9 100644 --- a/capi_parser/src/main.py +++ b/capi_parser/src/main.py @@ -1,21 +1,22 @@ - import argparse from bin import config -parser = argparse.ArgumentParser( - prog=config.Config.name, description=config.Config.description) -for command in config.Config.commands: - abbr = command.get("abbr") - name = command.get("name") - choices = command.get("choices") - required = (True if command.get("required") else False) - type = command.get("type") - default = command.get("default") - help = command.get("help") - parser.add_argument(abbr, name, choices=choices, - required=required, type=type, default=default, help=help) +def main_function(): + parser = argparse.ArgumentParser( + prog=config.Config.name, description=config.Config.description) + for command in config.Config.commands: + arg_abbr = command.get("abbr") + arg_name = command.get("name") + arg_choices = command.get("choices") + arg_required = (True if command.get("required") else False) + arg_type = command.get("type") + default = command.get("default") + arg_help = command.get("help") + parser.add_argument(arg_abbr, arg_name, choices=arg_choices, + required=arg_required, type=arg_type, default=default, help=arg_help) + + config.run_tools(parser.parse_args()) -# args = parser.parse_args() -config.run_tools(parser.parse_args()) +main_function() diff --git a/capi_parser/src/utils/constants.py b/capi_parser/src/utils/constants.py index fb0edb13dbaa901c5b6b22b7c78ab229eb664ba7..f8d94bce37e666f8a8f0bd61146a47dc6f5c8adb 100644 --- a/capi_parser/src/utils/constants.py +++ b/capi_parser/src/utils/constants.py @@ -2,6 +2,8 @@ import enum class StringConstant(enum.Enum): - LIB_CLANG_PATH = r'D:\Environment\LLVM\bin\libclang.dll' + LIB_CLG_PATH = r'D:\Environment\LLVM\bin\libclang.dll' # 共享库 FUNK_NAME = "ohos_ndk_headers" - REPLACE_WAREHOUSE = '\\interface_sdk_c\\interface_sdk_c' + REPLACE_WAREHOUSE = '\\interface_sdk_c\\interface_sdk_c' # 拉到本地仓路径(去掉磁盘) + # 拉到本地仓的三方库绝对路径 + INCLUDE_LIB = r'third_party\musl\ndk_musl_include'