1#!/usr/bin/env python 2# coding=utf-8 3############################################## 4# Copyright (c) 2021-2022 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16############################################## 17 18import json 19import os 20import glob 21import re 22import shutil 23import stat 24from utils.constants import StringConstant, RegularExpressions 25from typedef.parser.parser import ParserGetResultTable 26from coreImpl.parser import parse_include, generating_tables # 引入解析文件 # 引入得到结果表格文件 27 28 29def find_gn_file(directory): # 找指定目录下所有GN文件 30 gn_files = [] 31 for root, _, files in os.walk(directory): # dirpath, dirnames, filenames(对应信息) 32 for file in files: 33 if file.endswith(".gn"): 34 gn_files.append(os.path.join(root, file)) 35 return gn_files 36 37 38def find_h_file(matches, f, sources): 39 for mat in matches: 40 # 匹配sources = \[[^\]]*\](匹配方括号内的内容,其中包括一个或多个非右括号字符),\s*:匹配0个或多个空白字符 41 f.seek(mat.span()[0]) 42 content = f.read() 43 pattern = RegularExpressions.SOURCES.value 44 sources_match = re.search(pattern, content) 45 if sources_match: 46 sources_value = sources_match.group(0) # 获取完整匹配的字符串 47 sources_value = re.sub(r'\s', '', sources_value) # 去除源字符串的空白字符(换行符)和空格 48 pattern = RegularExpressions.INCLUDE_H.value # 匹配引号中的内容,找对应的.h 49 source = re.findall(pattern, sources_value) 50 sources.extend(source) 51 52 53def find_function_file(file, function_name): # 在GN文件中查找指定函数并在有函数名,获取对应sources的值 54 with open(file, 'r') as f: 55 content = f.read() # 获取文件内容 56 pattern = ''.join([r'\b', re.escape(function_name), r'\b']) # '\b'确保函数名的完全匹配 57 matches = re.finditer(pattern, content) # finditer会返回位置信息 58 f.seek(0) # 回到文件开始位置 59 sources = [] # 装全部匹配的sources的.h(可能不止一个-headers函数) 60 if matches: # 是否匹配成功 61 find_h_file(matches, f, sources) 62 f.close() 63 return matches, sources 64 65 66def find_dest_dir(matches, content, f): 67 sources_dir = [] 68 if matches: 69 end = 0 70 for _ in matches: 71 pattern = RegularExpressions.DEST_DIR.value 72 source_match = re.search(pattern, content) 73 if source_match: 74 con = source_match.group(1) 75 sources_dir.append(con) 76 end += source_match.end() # 每次找完一个sources的.h路径,记录光标结束位置 77 f.seek(end) # 移动光标在该结束位置 78 content = f.read() 79 return sources_dir 80 81 82def get_dest_dir(file, function_name): # 获取dest_dir 83 with open(file, 'r') as f: 84 content = f.read() # 获取文件内容 85 pattern = ''.join([r'\b', re.escape(function_name), r'\b']) # '\b'确保函数名的完全匹配 86 matches = re.findall(pattern, content) 87 f.seek(0) 88 sources_dir = find_dest_dir(matches, content, f) 89 f.close() 90 return sources_dir 91 92 93def find_json_file(gn_file_match): # 找gn文件同级目录下的.json文件 94 match_json_file = [] 95 directory = os.path.dirname(gn_file_match) 96 for file in glob.glob(os.path.join(directory, "*.json")): # 统计.json文件 97 match_json_file.append(file) 98 return match_json_file 99 100 101def dire_func(gn_file, func_name): # 统计数据的 102 matches_file_total = [] # 统计有ohos_ndk_headers函数的gn文件 103 json_file_total = [] # 统计跟含有函数的gn文件同级的json文件 104 source_include = [] # 统计sources里面的.h 105 matches, source = find_function_file(gn_file, func_name) # 找到包含函数的gn文件 106 if matches: # 保证两个都不为空,source可能为空 107 source_include = source # 获取头文件列表 108 matches_file_total.append(gn_file) # 调用匹配函数的函数(说明有对应的函数、source) 109 json_file_total.extend(find_json_file(gn_file)) # 同级目录下的.json文件 110 111 return matches_file_total, json_file_total, source_include 112 113 114def change_json_file(dict_data, name): # 生成json文件 115 file_name = name + '_new' + '.json' # json文件名 116 with open(file_name, 'w', encoding='UTF-8') as f: # encoding='UTF-8'能显示中文 117 # ensure_ascii=False确保能显示中文,indent=4(格式控制)使生成的json样式跟字典一样 118 json.dump(dict_data, f, ensure_ascii=False, indent=4) 119 f.close() 120 return file_name 121 122 123def change_abs(include_files, dire_path): # 获取.h绝对路径 124 abs_path = [] 125 for j_item in include_files: # 拼接路径,生成绝对路径 126 # os.path.normpath(path):规范或者是格式化路径,它会把所有路径分割符按照操作系统进行替换 127 # 把规范路径和gn文件对应的目录路径拼接 128 if os.path.isabs(j_item): # 是否是绝对路径,是就拼接路径盘,不是就拼接gn目录路径 129 head = os.path.splitdrive(dire_path) # 获取windows盘路径 130 include_file = os.path.normpath(j_item) 131 if 'third_party/node/src' in j_item: 132 include_file = include_file.replace('\\\\', 133 '{}{}'.format(StringConstant.REPLACE_WAREHOUSE.value, '\\')) 134 else: 135 # 去掉绝对路径的双\\,替换为interface_sdk_c 136 include_file = include_file.replace('\\\\interface\\sdk_c', 137 StringConstant.REPLACE_WAREHOUSE.value) 138 if head: 139 include_file = os.path.join(head[0], include_file) # 拼接盘和路径 140 abs_path.append(include_file) 141 else: 142 relative_path = os.path.abspath(os.path.join(dire_path, os.path.normpath(j_item))) # ../ .解决 143 abs_path.append(relative_path) 144 print("=" * 50) 145 return abs_path 146 147 148def get_result_table(json_files, abs_path, link_path, gn_path): # 进行处理,生成表格 149 result_list = [] 150 head_name = "" 151 only_file1 = [] 152 only_file2 = [] 153 data = [] 154 if json_files: 155 file_name = os.path.split(json_files[0]) # 取第一个json名,但我是用列表装的 156 file_name = os.path.splitext(file_name[1]) # 取下标1对应的元素(元组) 157 data = parse_include.get_include_file(abs_path, link_path, gn_path) # 获取解析返回的数据 158 parse_json_name = change_json_file(data, file_name[0]) # 生成json文件 159 # 解析完后,传两个json文件,对比两个json文件,最后生成数据表格 160 result_list, head_name, only_file1, only_file2 = generating_tables.get_json_file(parse_json_name, 161 json_files) 162 163 obj_data = ParserGetResultTable(result_list, head_name, only_file1, only_file2, data) 164 165 return obj_data 166 167 168def create_dir(sources_dir, gn_file, function_name, link_include_file): 169 if sources_dir: 170 for item in sources_dir: 171 directory = item 172 new_dire = os.path.join('sysroot', directory) 173 new_dire = os.path.normpath(new_dire) 174 if not os.path.exists(new_dire): 175 os.makedirs(new_dire) 176 else: 177 print("目录已存在") 178 179 if new_dire in link_include_file: 180 pass 181 else: 182 link_include_file.append(new_dire) # 添加链接的头文件 183 match_files, json_files, include_files = dire_func(gn_file, function_name) 184 dire_path = os.path.dirname(gn_file) # 获取gn文件路径 185 if match_files: 186 dir_copy(include_files, dire_path, new_dire) 187 else: 188 print("在create_dir函数中,原因:gn文件条件不满足") 189 else: 190 print("gn文件没有ohos_sdk_headers") 191 192 193def dir_copy(include_files, dire_path, new_dire): 194 abs_path = change_abs(include_files, dire_path) # 接收.h绝对路径 195 for j_item in abs_path: 196 shutil.copy(j_item, new_dire) 197 198 199def link_include(directory_path, function_names, link_include_file): 200 gn_file_total = find_gn_file(directory_path) # 查找gn文件 201 for item in gn_file_total: # 处理每个gn文件 202 sources_dir = get_dest_dir(item, function_names) 203 if sources_dir: 204 create_dir(sources_dir, item, function_names, link_include_file) 205 206 207def main_entrance(directory_path, function_names, link_path): # 主入口 208 gn_file_total = find_gn_file(directory_path) # 查找gn文件 209 result_list_total = [] 210 only_file1_total = [] 211 only_file2_total = [] 212 data_total = [] # 总的解析数据 213 for item in gn_file_total: # 处理每个gn文件 214 match_files, json_files, include_files = dire_func(item, function_names) 215 dire_path = os.path.dirname(item) # 获取gn文件路径 216 print("目录路径: {}".format(dire_path)) 217 print("同级json文件:\n", json_files) 218 print("头文件:\n", include_files) 219 if include_files: # 符合条件的gn文件 220 abs_path = change_abs(include_files, dire_path) # 接收.h绝对路径 221 print("头文件绝对路径:\n", abs_path) 222 # 接收对比结果信息 223 data_result = get_result_table(json_files, abs_path, link_path, dire_path) 224 data_total.append(data_result.data) 225 if len(data_result.result_list) != 0: 226 result_list_total.extend(data_result.result_list) 227 only_file1_total.extend(data_result.only_file1) 228 only_file2_total.extend(data_result.only_file2) 229 elif data_result.head_name == "": 230 print("gn文件下无json文件") 231 else: 232 generating_tables.generate_excel(data_result.result_list, data_result.head_name, 233 data_result.only_file1, data_result.only_file2) 234 print("没有匹配项") 235 else: 236 print("gn文件无header函数") 237 generating_tables.generate_excel(result_list_total, StringConstant.RESULT_HEAD_NAME.value, 238 only_file1_total, only_file2_total) 239 240 obj_data_total = ParserGetResultTable(result_list_total, '', only_file1_total, 241 only_file2_total, data_total) 242 return obj_data_total 243 244 245def copy_std_lib(link_include_file): 246 std_include = StringConstant.STD_INCLUDE.value 247 if not os.path.exists(std_include): 248 shutil.copytree(StringConstant.INCLUDE_LIB.value, std_include) 249 link_include_file.append(std_include) 250 251 252def find_include(link_include_path): 253 for dir_path, _, _ in os.walk(StringConstant.CREATE_LIB_PATH.value): 254 link_include_path.append(dir_path) 255 256 257def copy_self_include(link_include_path, self_include_file, flag=-1): 258 if flag == 0: 259 std_include = StringConstant.SELF_INCLUDE_OLD.value 260 elif flag == 1: 261 std_include = StringConstant.SELF_INCLUDE_NEW.value 262 else: 263 std_include = StringConstant.SELF_INCLUDE.value 264 265 if std_include and not os.path.exists(std_include): 266 shutil.copytree(self_include_file, std_include) 267 268 for dir_path, _, files in os.walk(std_include): 269 for file in files: 270 if not file.endswith('.h'): 271 os.remove(os.path.join(dir_path, file)) 272 elif dir_path not in link_include_path: 273 link_include_path.append(dir_path) 274 275 276def delete_typedef_child(child): 277 if child['kind'] == 'TYPEDEF_DECL': 278 if 'children' in child and len(child['children']) \ 279 and (child['children'][0]['kind'] == 'STRUCT_DECL' 280 or child['children'][0]['kind'] == 'ENUM_DECL' 281 or child['children'][0]['kind'] == 'UNION_DECL'): 282 child['children'] = [] 283 284 285def parser(directory_path): # 目录路径 286 function_name = StringConstant.FUNK_NAME.value # 匹配的函数名 287 288 link_include_path = [] # 装链接头文件路径 289 copy_std_lib(link_include_path) # ndk头文件移到sysroot中 290 find_include(link_include_path) 291 link_include(directory_path, function_name, link_include_path) 292 293 data_total = main_entrance(directory_path, function_name, link_include_path) # 调用入口函数 294 return data_total 295 296 297def parser_include_ast(gn_file_path, include_path, flag=-1): # 对于单独的.h解析接口 298 correct_include_path = [] 299 300 link_include_path = [] 301 copy_std_lib(link_include_path) 302 find_include(link_include_path) 303 link_include(gn_file_path, StringConstant.FUNK_NAME.value, link_include_path) 304 copy_self_include(link_include_path, gn_file_path, flag) 305 306 modes = stat.S_IRWXO | stat.S_IRWXG | stat.S_IRWXU 307 fd = os.open('include_file_suffix.txt', os.O_WRONLY | os.O_CREAT, mode=modes) 308 for item in include_path: 309 split_path = os.path.splitext(item) 310 if split_path[1] == '.h': # 判断.h结尾 311 correct_include_path.append(item) 312 else: 313 exc = 'The file does not end with.h: {}\n'.format(item) 314 os.write(fd, exc.encode()) 315 os.close(fd) 316 317 data = parse_include.get_include_file(correct_include_path, link_include_path, gn_file_path) 318 319 for item in data: 320 if 'children' in item: 321 for child in item['children']: 322 delete_typedef_child(child) 323 324 return data 325