1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (c) 2024 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import argparse 17import json 18import os 19import re 20import glob 21import os.path 22import stat 23 24 25class Analyzer: 26 @classmethod 27 def get_components_from_inherit_attr(cls, components, inherit, project): 28 for json_name in inherit: 29 with open(project + os.sep + json_name, 'r', encoding='utf-8') as r: 30 inherit_file = json.load(r) 31 for subsystem in inherit_file['subsystems']: 32 for component in subsystem['components']: 33 component['subsystem'] = subsystem['subsystem'] 34 components.append(component) 35 36 @classmethod 37 def check(cls, include): 38 if include is not None and './' in include.group(): 39 return True 40 return False 41 42 @classmethod 43 def scan_files(cls, components, proj_path): 44 results = [] 45 for component in components: 46 if not component.__contains__('scan_path') or component['scan_path'].strip() == '': 47 continue 48 files = glob.glob(os.path.join(component['scan_path'], '**', '*.c'), recursive=True) 49 files.extend(glob.glob(os.path.join(component['scan_path'], '**', '*.cpp'), recursive=True)) 50 files.extend(glob.glob(os.path.join(component['scan_path'], '**', '*.cc'), recursive=True)) 51 files.extend(glob.glob(os.path.join(component['scan_path'], '**', '*.h'), recursive=True)) 52 for file in files: 53 try: 54 cls.scan_each_file(component, file, proj_path, results) 55 except UnicodeDecodeError as e: 56 print("scan file {} with unicode decode error: {}".format(file, e)) 57 return results 58 59 @classmethod 60 def scan_each_file(cls, component, file, project_path, results): 61 with open(file, 'r', encoding='ISO-8859-1') as f: 62 line_list = f.readlines() 63 line_num = 0 64 for line in line_list: 65 include = re.match(r'#include\s+"([^">]+)"', line) 66 line_num = line_num + 1 67 if cls.check(include): 68 result = {'line_num': line_num, 'file_path': file.replace(project_path, "/"), 69 'include_cmd': include.group(), 'component': component['component'], 70 'subsystem': component['subsystem']} 71 results.append(result) 72 73 @classmethod 74 def analysis(cls, config: str, project_path: str, components_info: str, output_path: str): 75 if not os.path.exists(config): 76 print("error: {} is inaccessible or not found".format(config)) 77 return 78 if not os.path.exists(project_path): 79 print("error: {} is inaccessible or not found".format(project_path)) 80 return 81 if not os.path.exists(components_info): 82 print("error: {} is inaccessible or not found".format(components_info)) 83 return 84 components = cls.__get_components(config, project_path) 85 cls.__get_need_scan_path(components, project_path, components_info) 86 print("scan:") 87 print([item['scan_path'] for item in components], project_path) 88 result = cls.scan_files(components, project_path) 89 flags = os.O_WRONLY | os.O_CREAT 90 modes = stat.S_IWUSR | stat.S_IRUSR 91 with os.fdopen(os.open(output_path, flags, modes), 'w') as f: 92 for ele in result: 93 items = ele['subsystem'], ele['component'], ele['file_path'], str(ele['line_num']), ele['include_cmd'] 94 f.write(f'{" ".join(items)}\n') 95 96 @classmethod 97 def __get_need_scan_path(cls, components, project, components_info_path): 98 path_info = dict() 99 with open(components_info_path, 'r', encoding='utf-8') as r: 100 xml_info = r.readlines() 101 for line in xml_info: 102 if "path=" in line: 103 path = re.findall('path="(.*?)"', line)[0] 104 component = path.split('/')[-1] 105 path_info[component] = path 106 item_list = list(path_info.keys()) 107 for component in components: 108 if component['component'] in path_info.keys(): 109 component['scan_path'] = project + '/' + path_info[component['component']] 110 if (component['component'] in item_list): 111 item_list.remove(component['component']) 112 else: 113 component['scan_path'] = '' 114 print("no scan component :" + " ".join(item_list)) 115 116 @classmethod 117 def __get_components(cls, config: str, project: str): 118 components = list() 119 with open(config, 'r', encoding='utf-8') as r: 120 config_json = json.load(r) 121 if "inherit" in config_json.keys(): 122 inherit = config_json['inherit'] 123 cls.get_components_from_inherit_attr(components, inherit, project) 124 for subsystem in config_json['subsystems']: 125 for component in subsystem['components']: 126 if component not in components: 127 component['subsystem'] = subsystem['subsystem'] 128 components.append(component) 129 return components 130 131 132 133def get_args(): 134 parser = argparse.ArgumentParser( 135 description=f"common_template.\n") 136 parser.add_argument("-c", "--config_path", required=True, type=str, 137 help="path of config_file", default="") 138 parser.add_argument("-p", "--project_path", type=str, required=False, 139 help="root path of openharmony. eg: -p ~/openharmony", default="./") 140 parser.add_argument("-x", "--xml_path", type=str, required=True, 141 help="path of ohos.xml", default="") 142 parser.add_argument("-o", "--output_path", required=False, type=str, 143 default="include_relative_path.list", help="name of output_json") 144 return parser.parse_args() 145 146if __name__ == '__main__': 147 args = get_args() 148 local_config_path = args.config_path 149 local_project_path = args.project_path 150 local_xml_path = args.xml_path 151 local_output_path = args.output_path 152 Analyzer.analysis(local_config_path, local_project_path, local_xml_path, local_output_path) 153