• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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