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