• 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
23from utils.constants import StringConstant, RegularExpressions
24from typedef.parser.parser import ParserGetResultTable, OneFileApiMessage, NodeKind
25from coreImpl.parser import parse_include, generating_tables  # 引入解析文件 # 引入得到结果表格文件
26
27
28def find_gn_file(directory):  # 找指定目录下所有GN文件
29    gn_files = []
30    for root, _, files in os.walk(directory):  # dirpath, dirnames, filenames(对应信息)
31        for file in files:
32            if file.endswith(".gn"):
33                gn_files.append(os.path.join(root, file))
34    return gn_files
35
36
37def find_h_file(matches, f, sources):
38    for mat in matches:
39        # 匹配sources = \[[^\]]*\](匹配方括号内的内容,其中包括一个或多个非右括号字符),\s*:匹配0个或多个空白字符
40        f.seek(mat.span()[0])
41        content = f.read()
42        pattern = RegularExpressions.SOURCES.value
43        sources_match = re.search(pattern, content)
44        if sources_match:
45            sources_value = sources_match.group(0)  # 获取完整匹配的字符串
46            sources_value = re.sub(r'\s', '', sources_value)  # 去除源字符串的空白字符(换行符)和空格
47            pattern = RegularExpressions.INCLUDE_H.value  # 匹配引号中的内容,找对应的.h
48            source = re.findall(pattern, sources_value)
49            sources.extend(source)
50
51
52def find_function_file(file, function_name):  # 在GN文件中查找指定函数并在有函数名,获取对应sources的值
53    with open(file, 'r') as f:
54        content = f.read()  # 获取文件内容
55        pattern = ''.join([r'\b', re.escape(function_name), r'\b'])    # '\b'确保函数名的完全匹配
56        matches = re.finditer(pattern, content)  # finditer会返回位置信息
57        f.seek(0)  # 回到文件开始位置
58        sources = []  # 装全部匹配的sources的.h(可能不止一个-headers函数)
59        if matches:  # 是否匹配成功
60            find_h_file(matches, f, sources)
61        f.close()
62        return matches, sources
63
64
65def find_dest_dir(matches, content, f):
66    sources_dir = []
67    if matches:
68        end = 0
69        for _ in matches:
70            pattern = RegularExpressions.DEST_DIR.value
71            source_match = re.search(pattern, content)
72            if source_match:
73                con = source_match.group(1)
74                sources_dir.append(con)
75                end += source_match.end()  # 每次找完一个sources的.h路径,记录光标结束位置
76                f.seek(end)  # 移动光标在该结束位置
77                content = f.read()
78    return sources_dir
79
80
81def get_dest_dir(file, function_name):  # 获取dest_dir
82    with open(file, 'r') as f:
83        content = f.read()  # 获取文件内容
84        pattern = ''.join([r'\b', re.escape(function_name), r'\b'])  # '\b'确保函数名的完全匹配
85        matches = re.findall(pattern, content)
86        f.seek(0)
87        sources_dir = find_dest_dir(matches, content, f)
88        f.close()
89        return sources_dir
90
91
92def find_json_file(gn_file_match):  # 找gn文件同级目录下的.json文件
93    match_json_file = []
94    directory = os.path.dirname(gn_file_match)
95    for file in glob.glob(os.path.join(directory, "*.json")):  # 统计.json文件
96        match_json_file.append(file)
97    return match_json_file
98
99
100def dire_func(gn_file, func_name):  # 统计数据的
101    matches_file_total = []  # 统计有ohos_ndk_headers函数的gn文件
102    json_file_total = []  # 统计跟含有函数的gn文件同级的json文件
103    source_include = []  # 统计sources里面的.h
104    matches, source = find_function_file(gn_file, func_name)  # 找到包含函数的gn文件
105    if matches:  # 保证两个都不为空,source可能为空
106        source_include = source  # 获取头文件列表
107        matches_file_total.append(gn_file)  # 调用匹配函数的函数(说明有对应的函数、source)
108        json_file_total.extend(find_json_file(gn_file))  # 同级目录下的.json文件
109
110    return matches_file_total, json_file_total, source_include
111
112
113def change_json_file(dict_data, name):  # 生成json文件
114    file_name = name + '_new' + '.json'  # json文件名
115    with open(file_name, 'w', encoding='UTF-8') as f:  # encoding='UTF-8'能显示中文
116        # ensure_ascii=False确保能显示中文,indent=4(格式控制)使生成的json样式跟字典一样
117        json.dump(dict_data, f, ensure_ascii=False, indent=4)
118    f.close()
119    return file_name
120
121
122def change_abs(include_files, dire_path):  # 获取.h绝对路径
123    abs_path = []
124    for j_item in include_files:  # 拼接路径,生成绝对路径
125        # os.path.normpath(path):规范或者是格式化路径,它会把所有路径分割符按照操作系统进行替换
126        # 把规范路径和gn文件对应的目录路径拼接
127        if os.path.isabs(j_item):  # 是否是绝对路径,是就拼接路径盘,不是就拼接gn目录路径
128            head = os.path.splitdrive(dire_path)  # 获取windows盘路径
129            include_file = j_item
130            change_path = head[1].split('interface_sdk_c')
131            replace_path = os.path.normpath(os.path.join(change_path[0], 'interface_sdk_c'))
132            if 'third_party/node/src' in j_item:
133                include_file = include_file.replace('//',
134                                                    '{}{}'.format(replace_path, '/'))
135            else:
136                # 去掉绝对路径的双\\,替换为interface_sdk_c
137                include_file = include_file.replace('//interface/sdk_c',
138                                                    replace_path)
139            if head:
140                include_file = os.path.join(head[0], include_file)  # 拼接盘和路径
141            include_file = os.path.normpath(include_file)
142            abs_path.append(include_file)
143        else:
144            relative_path = os.path.abspath(os.path.join(dire_path, os.path.normpath(j_item)))  # ../ .解决
145            abs_path.append(relative_path)
146    return abs_path
147
148
149def get_result_table(json_files, abs_path, link_path, gn_path):  # 进行处理,生成表格
150    compare_result_list = []
151    head_name = ""
152    generate_data_only = []
153    original_data_only = []
154    parser_file_data = []
155    if json_files:
156        file_name = os.path.split(json_files[0])  # 取第一个json名,但我是用列表装的
157        file_name = os.path.splitext(file_name[1])  # 取下标1对应的元素(元组)
158        parser_file_data = parse_include.get_include_file(abs_path, link_path, gn_path)  # 获取解析返回的数据
159        parser_json_name = change_json_file(parser_file_data, file_name[0])  # 生成json文件
160        # 解析完后,传两个json文件,对比两个json文件,最后生成数据表格
161        (compare_result_list, head_name, generate_data_only,
162         original_data_only) = generating_tables.get_json_file(parser_json_name, json_files)
163
164    elif abs_path:
165        file_name = os.path.splitext(os.path.split(abs_path[0])[1])
166        parser_file_data = parse_include.get_include_file(abs_path, link_path, gn_path)
167        parser_json_name = change_json_file(parser_file_data, file_name[0])  # 生成json文件
168        (compare_result_list, head_name, generate_data_only,
169         original_data_only) = generating_tables.get_parser_json_data(parser_json_name, parser_file_data)
170
171    obj_data = ParserGetResultTable(compare_result_list, head_name,
172                                    generate_data_only, original_data_only, parser_file_data)
173
174    return obj_data
175
176
177def create_dir(sources_dir, gn_file, function_name, link_include_file):
178    if sources_dir:
179        for item in sources_dir:
180            directory = item
181            new_dire = os.path.join('sysroot_myself', directory)
182            new_dire = os.path.normpath(new_dire)
183            if not os.path.exists(new_dire):
184                os.makedirs(new_dire)
185            if new_dire in link_include_file:
186                pass
187            else:
188                link_include_file.append(new_dire)  # 添加链接的头文件
189            match_files, json_files, include_files = dire_func(gn_file, function_name)
190            dire_path = os.path.dirname(gn_file)  # 获取gn文件路径
191            if match_files:
192                dir_copy(include_files, dire_path, new_dire)
193            else:
194                print("在create_dir函数中,原因:gn文件条件不满足")
195    else:
196        print("gn文件没有ohos_sdk_headers")
197
198
199def dir_copy(include_files, dire_path, new_dire):
200    abs_path = change_abs(include_files, dire_path)  # 接收.h绝对路径
201    for j_item in abs_path:
202        shutil.copy(j_item, new_dire)
203
204
205def link_include(directory_path, function_names, link_include_file):
206    gn_file_total = find_gn_file(directory_path)  # 查找gn文件
207    for item in gn_file_total:  # 处理每个gn文件
208        sources_dir = get_dest_dir(item, function_names)
209        if sources_dir:
210            create_dir(sources_dir, item, function_names, link_include_file)
211
212
213def main_entrance(directory_path, function_names, link_path):  # 主入口
214    gn_file_total = find_gn_file(directory_path)  # 查找gn文件
215    compare_result_list_total = []
216    generate_data_only_total = []
217    original_data_only_total = []
218    data_total = []             # 总的解析数据
219    for item in gn_file_total:  # 处理每个gn文件
220        match_files, json_files, include_files = dire_func(item, function_names)
221        dire_path = os.path.dirname(item)  # 获取gn文件路径
222        if include_files:  # 符合条件的gn文件
223            abs_path = change_abs(include_files, dire_path)  # 接收.h绝对路径
224            # 接收对比结果信息
225            data_result = get_result_table(json_files, abs_path, link_path, directory_path)
226            data_total.append(data_result.parser_data)
227            if len(data_result.compare_result_list) != 0:
228                compare_result_list_total.extend(data_result.compare_result_list)
229                generate_data_only_total.extend(data_result.generate_data_only)
230                original_data_only_total.extend(data_result.original_data_only)
231            elif data_result.head_name == "":
232                print("gn文件下无json文件")
233            else:
234                generate_data_only_total.extend(data_result.generate_data_only)
235                original_data_only_total.extend(data_result.original_data_only)
236                print("没有匹配项")
237        else:
238            print("gn文件无header函数")
239    generating_tables.generate_excel(compare_result_list_total, StringConstant.RESULT_HEAD_NAME.value,
240                                     generate_data_only_total, original_data_only_total)
241
242    obj_data_total = ParserGetResultTable(compare_result_list_total, '', generate_data_only_total,
243                                          original_data_only_total, data_total)
244    return obj_data_total
245
246
247def copy_std_lib(link_include_file, root_path=''):
248    if root_path:
249        include_lib = os.path.abspath(os.path.join(root_path, StringConstant.INCLUDE_LIB.value))
250    else:
251        include_lib = StringConstant.INCLUDE_LIB.value
252    std_include = StringConstant.STD_INCLUDE.value
253    if not os.path.exists(std_include):
254        try:
255            shutil.copytree(include_lib, std_include)
256        except OSError:
257            pass
258    if std_include not in link_include_file:
259        link_include_file.append(std_include)
260
261
262def find_include(link_include_path):
263    for dir_path, _, _ in os.walk(StringConstant.CREATE_LIB_PATH.value):
264        if dir_path not in link_include_path:
265            link_include_path.append(dir_path)
266
267
268def copy_self_include(link_include_path, self_include_file):
269    for dir_path, dir_name, file_name_list in os.walk(self_include_file):
270        if 'sysroot_myself' not in dir_path and 'build-tools' not in dir_path and dir_path not in link_include_path:
271            link_include_path.append(dir_path)
272
273
274def delete_typedef_child(child):
275    if child['kind'] == 'TYPEDEF_DECL':
276        if 'children' in child and len(child['children']) \
277                and (child['children'][0]['kind'] == 'STRUCT_DECL'
278                     or child['children'][0]['kind'] == 'ENUM_DECL'
279                     or child['children'][0]['kind'] == 'UNION_DECL'):
280            child['children'] = []
281
282
283def parser(directory_path):  # 目录路径
284    function_name = StringConstant.FUNK_NAME.value  # 匹配的函数名
285
286    link_include_path = []  # 装链接头文件路径
287    copy_std_lib(link_include_path)  # 头文件移到sysroot_myself中
288    find_include(link_include_path)
289    link_include(directory_path, function_name, link_include_path)
290
291    data_total = main_entrance(directory_path, function_name, link_include_path)  # 调用入口函数
292    return data_total
293
294
295def parser_include_ast(dire_file_path, include_path: list):        # 对于单独的.h解析接口
296    correct_include_path = []
297    link_include_path = [dire_file_path]
298    copy_self_include(link_include_path, dire_file_path)
299    for item in include_path:
300        split_path = os.path.splitext(item)
301        if split_path[1] == '.h':   # 判断.h结尾
302            correct_include_path.append(item)
303
304    data = parse_include.get_include_file(correct_include_path, link_include_path, dire_file_path)
305
306    for item in data:
307        if 'children' in item:
308            for child in item['children']:
309                delete_typedef_child(child)
310
311    return data
312
313
314def diff_parser_include_ast(dire_file_path, include_path: list, flag=-1):        # 对于单独的.h解析接口
315    link_include_path = [dire_file_path]
316    copy_self_include(link_include_path, dire_file_path)
317    data = parse_include.get_include_file(include_path, link_include_path, dire_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
326
327def get_dir_file_path(dir_path):
328    file_path_list = []
329    link_include_path = []  # 装链接头文件路径
330    for dir_path, dir_names, filenames in os.walk(dir_path):
331        if 'sysroot_myself' not in dir_path and 'build-tools' not in dir_path and dir_path not in link_include_path:
332            link_include_path.append(dir_path)
333        for file in filenames:
334            if 'build-tools' not in dir_path and 'sysroot_myself' not in dir_path and file.endswith('.h'):
335                file_path_list.append(os.path.normpath(os.path.join(dir_path, file)))
336
337    return file_path_list, link_include_path
338
339
340def get_file_api_num(file_data, kind_list):
341    api_number = 0
342    if 'children' in file_data:
343        for child in file_data['children']:
344            if 'kind' in child and child['kind'] in kind_list:
345                api_number += 1
346    return api_number
347
348
349def get_file_api_dict(data_total):
350    api_obj_total_list = []
351    kind_list = [
352        NodeKind.MACRO_DEFINITION.value,
353        NodeKind.STRUCT_DECL.value,
354        NodeKind.UNION_DECL.value,
355        NodeKind.ENUM_DECL.value,
356        NodeKind.FUNCTION_DECL.value,
357        NodeKind.VAR_DECL.value
358    ]
359    for one_file_data in data_total:
360        file_api_num = get_file_api_num(one_file_data, kind_list)
361        if 'name' in one_file_data and 'kit_name' in one_file_data and 'sub_system' in one_file_data:
362            api_message_obj = OneFileApiMessage(one_file_data['name'], one_file_data['kit_name'],
363                                                one_file_data['sub_system'], file_api_num)
364            api_message_obj.set_file_path(api_message_obj.get_file_path().replace('\\', '/'))
365            current_file = os.path.dirname(__file__)
366            kit_json_file_path = os.path.abspath(os.path.join(current_file,
367                                                              r"kit_sub_system/c_file_kit_sub_system.json"))
368            complete_kit_or_system(api_message_obj, kit_json_file_path)
369            api_obj_total_list.append(api_message_obj)
370    api_dict_total_list = obj_change_to_dict(api_obj_total_list)
371
372    return api_dict_total_list
373
374
375def obj_change_to_dict(obj_data: list):
376    dict_list = []
377    for element in obj_data:
378        element_dict = {
379            'filePath': element.file_path,
380            'kitName': element.kit_name,
381            'subSystem': element.sub_system,
382            'apiNumber': element.api_number
383        }
384        dict_list.append(element_dict)
385
386    return dict_list
387
388
389def generate_file_api_json(dict_data, output_path=''):
390    if not output_path:
391        output_path = StringConstant.FILE_LEVEL_API_DATA.value
392    with open(output_path, 'w', encoding='utf-8') as fs:
393        json.dump(dict_data, fs, indent=4, ensure_ascii=False)
394        fs.close()
395
396
397def complete_kit_or_system(api_message: OneFileApiMessage, json_path):
398    if (not api_message.get_kit_name()) or (not api_message.get_sub_system()):
399        kit_name, sub_system_name = parse_include.get_kit_system_data(json_path,
400                                                                      api_message.get_file_path())
401        if not api_message.get_kit_name():
402            api_message.set_kit_name(kit_name)
403        if not api_message.get_sub_system():
404            api_message.set_sub_system(sub_system_name)
405
406
407def get_dependent_path_all(dependent_path):
408    all_dependent_path_list = []
409    for dir_path, _, _ in os.walk(dependent_path):
410        if 'sysroot_myself' not in dir_path and 'build-tools' not in dir_path:
411            all_dependent_path_list.append(dir_path)
412
413    return all_dependent_path_list
414
415
416def parser_direct(path, dependent_path):  # 目录路径
417    file_path_list = []
418    link_include_path = []  # 装链接头文件路径
419    link_include_path.extend(get_dependent_path_all(dependent_path))
420    dir_path = ''
421    if os.path.isdir(path):
422        if path not in link_include_path:
423            link_include_path.append(path)
424        file_path_total, _ = get_dir_file_path(path)
425        file_path_list.extend(file_path_total)
426        dir_path = path
427    elif path.endswith('.h'):
428        file_path_list.append(path)
429        dir_path = os.path.dirname(path)
430        if dir_path not in link_include_path:
431            link_include_path.append(dir_path)
432    data_total = parse_include.get_include_file(file_path_list, link_include_path, dir_path)
433    generating_tables.get_api_data(data_total, StringConstant.PARSER_DIRECT_EXCEL_NAME.value)
434
435    return data_total
436
437
438def parser_file_level(output_path):
439    current_file = os.path.dirname(__file__)
440    parser_path = os.path.abspath(os.path.join(current_file, r'../../../../..'))
441    file_path_list = []
442    link_include_path = []  # 装链接头文件路径
443    data_total = []
444    if not os.path.isdir(parser_path):
445        return data_total
446    file_path_total, link_include_total = get_dir_file_path(parser_path)
447    file_path_list.extend(file_path_total)
448    link_include_path.extend(link_include_total)
449    data_total = parse_include.get_include_file(file_path_list, link_include_path, parser_path)
450    file_api_dict = get_file_api_dict(data_total)
451    generate_file_api_json(file_api_dict, output_path)
452
453    return data_total
454