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_need_scan_path(cls, components, project, components_info_path): 28 path_info = dict() 29 with open(components_info_path, 'r', encoding='utf-8') as r: 30 xml_info = r.readlines() 31 for line in xml_info: 32 if "path=" in line: 33 path = re.findall('path="(.*?)"', line)[0] 34 component = path.split('/')[-1] 35 path_info[component] = path 36 item_list = list(path_info.keys()) 37 for component in components: 38 if component['component'] in path_info.keys(): 39 component['scan_path'] = project + '/' + path_info[component['component']] 40 if (component['component'] in item_list): 41 item_list.remove(component['component']) 42 else: 43 component['scan_path'] = '' 44 print("no scan component :" + " ".join(item_list)) 45 46 @classmethod 47 def __get_components(cls, config: str, project: str): 48 components = list() 49 with open(config, 'r', encoding='utf-8') as r: 50 config_json = json.load(r) 51 if "inherit" in config_json.keys(): 52 inherit = config_json['inherit'] 53 cls.get_components_from_inherit_attr(components, inherit, project) 54 for subsystem in config_json['subsystems']: 55 for component in subsystem['components']: 56 if component not in components: 57 component['subsystem'] = subsystem['subsystem'] 58 components.append(component) 59 return components 60 61 @classmethod 62 def get_components_from_inherit_attr(cls, components, inherit, project): 63 for json_name in inherit: 64 with open(project + os.sep + json_name, 'r', encoding='utf-8') as r: 65 inherit_file = json.load(r) 66 for subsystem in inherit_file['subsystems']: 67 for component in subsystem['components']: 68 component['subsystem'] = subsystem['subsystem'] 69 components.append(component) 70 71 @classmethod 72 def check(cls, include): 73 if include is not None and './' in include.group(): 74 return True 75 return False 76 77 @classmethod 78 def scan_files(cls, components, proj_path): 79 results = [] 80 for component in components: 81 if not component.__contains__('scan_path') or component['scan_path'].strip() == '': 82 continue 83 files = glob.glob(os.path.join(component['scan_path'], '**', '*.c'), recursive=True) 84 files.extend(glob.glob(os.path.join(component['scan_path'], '**', '*.cpp'), recursive=True)) 85 files.extend(glob.glob(os.path.join(component['scan_path'], '**', '*.cc'), recursive=True)) 86 files.extend(glob.glob(os.path.join(component['scan_path'], '**', '*.h'), recursive=True)) 87 for file in files: 88 try: 89 cls.scan_each_file(component, file, proj_path, results) 90 except UnicodeDecodeError as e: 91 print("scan file {} with unicode decode error: {}".format(file, e)) 92 return results 93 94 @classmethod 95 def scan_each_file(cls, component, file, project_path, results): 96 with open(file, 'r', encoding='ISO-8859-1') as f: 97 line_list = f.readlines() 98 line_num = 0 99 for line in line_list: 100 include = re.match(r'#include\s+"([^">]+)"', line) 101 line_num = line_num + 1 102 if cls.check(include): 103 result = {'line_num': line_num, 'file_path': file.replace(project_path, "/"), 104 'include_cmd': include.group(), 'component': component['component'], 105 'subsystem': component['subsystem']} 106 results.append(result) 107 108 @classmethod 109 def analysis(cls, config: str, project_path: str, components_info: str, output_path: str): 110 if not os.path.exists(config): 111 print("error: {} is inaccessible or not found".format(config)) 112 return 113 if not os.path.exists(project_path): 114 print("error: {} is inaccessible or not found".format(project_path)) 115 return 116 if not os.path.exists(components_info): 117 print("error: {} is inaccessible or not found".format(components_info)) 118 return 119 components = cls.__get_components(config, project_path) 120 cls.__get_need_scan_path(components, project_path, components_info) 121 print("scan:") 122 print([item['scan_path'] for item in components], project_path) 123 result = cls.scan_files(components, project_path) 124 flags = os.O_WRONLY | os.O_CREAT 125 modes = stat.S_IWUSR | stat.S_IRUSR 126 with os.fdopen(os.open(output_path, flags, modes), 'w') as f: 127 for ele in result: 128 items = ele['subsystem'], ele['component'], ele['file_path'], str(ele['line_num']), ele['include_cmd'] 129 f.write(" ".join(items) + "\n") 130 131def get_args(): 132 parser = argparse.ArgumentParser( 133 description=f"common_template.\n") 134 parser.add_argument("-c", "--config_path", required=True, type=str, 135 help="path of config_file", default="") 136 parser.add_argument("-p", "--project_path", type=str, required=False, 137 help="root path of openharmony. eg: -p ~/openharmony", default="./") 138 parser.add_argument("-x", "--xml_path", type=str, required=True, 139 help="path of ohos.xml", default="") 140 parser.add_argument("-o", "--output_path", required=False, type=str, 141 default="include_relative_path.list", help="name of output_json") 142 return parser.parse_args() 143 144if __name__ == '__main__': 145 args = get_args() 146 local_config_path = args.config_path 147 local_project_path = args.project_path 148 local_xml_path = args.xml_path 149 local_output_path = args.output_path 150 Analyzer.analysis(local_config_path, local_project_path, local_xml_path, local_output_path) 151