• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright (c) 2023 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
16# This file provide the detection tool for unconditional dependence of required components on optional components.
17
18import argparse
19import json
20import os
21import re
22
23
24class Analyzer:
25    @classmethod
26    def analysis(cls, gn_path_list, new_line_nums, gn_name, config_path: str, open_components_path,
27                 result_json_name: str):
28        if not os.path.exists(config_path):
29            print("error: {} is inaccessible or not found".format(config_path))
30            return
31        if not os.path.exists(open_components_path):
32            print("error: {} is inaccessible or not found".format(open_components_path))
33            return
34        if len(gn_path_list) != len(new_line_nums):
35            print("error: The new_line_nums and the gn_path are not in one-to-one correspondence.")
36            return
37        if len(gn_path_list) != len(gn_name):
38            print("error: The gn_path and gn_name are not in one-to-one correspondence.")
39            return
40        required_components = cls.__get_required_components(config_path)
41        open_components, gn_name_list, white_list = cls.__get_open_components(open_components_path)
42        gn_name2component = dict(zip(gn_name_list, open_components))
43        optional_components = list()
44        for components in open_components:
45            if components not in required_components:
46                optional_components.append(components)
47        result = list()
48        for i, _ in enumerate(gn_path_list):
49            one_result = dict()
50            one_result["file_path"] = gn_path_list[i]
51            if gn_name[i] in gn_name_list and gn_name2component[gn_name[i]] in required_components:
52                one_result["error"] = cls.__judge_deps(gn_path_list[i], new_line_nums[i], open_components,
53                                                       optional_components, white_list)
54            else:
55                one_result["error"] = []
56            result.append(one_result)
57        with os.fdopen(os.open(result_json_name + ".json", os.O_WRONLY | os.O_CREAT, mode=0o640), "w",
58                       encoding='utf-8') as fd:
59            json.dump(result, fd, indent=4, ensure_ascii=False)
60
61    @classmethod
62    def __get_open_components(cls, xml_path):
63        open_components = list()
64        gn_name = list()
65        white_components_list = ["common", "hilog", "ylong_runtime"]
66        with open(xml_path, 'r', encoding='utf-8') as r:
67            xml_info = r.readlines()
68        for line in xml_info:
69            if "path=" in line:
70                one_component = re.findall('path="(.*?)"', line)[0].split('/')[-1]
71                open_components.append(one_component)
72                one_name = re.findall('name="(.*?)"', line)[0]
73                gn_name.append(one_name)
74        return open_components, gn_name, white_components_list
75
76    @classmethod
77    def __deal_config_json(cls, config_json):
78        components = list()
79        for subsystem in config_json['subsystems']:
80            for component in subsystem['components']:
81                if component not in components:
82                    components.append(component['component'])
83        return components
84
85    @classmethod
86    def __get_required_components(cls, config_path: str):
87        required_components = list()
88        files = os.listdir(config_path)
89        for file in files:
90            if file.endswith(".json"):
91                with open(os.path.join(config_path, file), 'r', encoding='utf-8') as r:
92                    config_json = json.load(r)
93                required_components += cls.__deal_config_json(config_json)
94        return required_components
95
96    @classmethod
97    def __get_line(cls, txt_list, key_words: str):
98        for i, txt in enumerate(txt_list):
99            if key_words in txt:
100                return i + 1
101        return 0
102
103    @classmethod
104    def __judge_deps(cls, gn_path: str, new_line_num: str, open_components_list, optional_components, white_names):
105        deps = list()
106        new_line_num = [int(i) for i in new_line_num.split('_')]
107        with open(gn_path, 'r', encoding='utf-8') as r:
108            gn_lines = [line.strip("\n") for line in r.readlines()]
109        dependent_close = True
110        txt = ''
111        for line in gn_lines:
112            txt += line
113        for component in open_components_list:
114            if dependent_close == True:
115                if component in txt:
116                    dependent_close = False
117        scan_line_num = cls.__get_scan_line_num(gn_lines, new_line_num)
118        for i in scan_line_num:
119            if '/' in gn_lines[i - 1]:
120                dep_info = re.findall('(.*?):', gn_lines[i - 1].split("/")[-1])[0]
121            else:
122                dep_info = re.findall('"(.*?):', gn_lines[i - 1])[0]
123            for component in optional_components:
124                if component not in white_names and component == dep_info:
125                    deps.append((component, i))
126        error = list()
127        if dependent_close == True and re.findall('deps =', txt):
128            line = cls.__get_line(gn_lines, 'deps =')
129            error.append(
130                {"line": line, "code": gn_lines[line - 1].strip(), "rule": "depend close component",
131                 "detail": "可能依赖闭源部件,请检查deps中的内容"})
132        for one_dep in deps:
133            error.append(
134                {"line": one_dep[1], "code": gn_lines[one_dep[1] - 1].strip(), "rule": "depend optional component",
135                 "detail": "依赖开源部件中的非必选部件{},请检查deps中的内容".format(one_dep[0])})
136        return error
137
138    @classmethod
139    def __get_scan_line_num(cls, gn_lines, new_line_num):
140        add_line_txt = ''
141        line_num = 0
142        for line in gn_lines:
143            line_num += 1
144            add_line_txt += '@' + str(line_num) + '@' + line
145        in_if_txt = re.findall('if \(.+?\{(.*?)\}', add_line_txt)
146        in_if_line_num = cls.__get_line_num(in_if_txt)
147        in_dep_txt = re.findall('deps = \[(.*?)\]', add_line_txt) + re.findall('deps += \[(.*?)\]', add_line_txt)
148        in_dep_line_num = cls.__get_line_num(in_dep_txt)
149        for line_num, line in enumerate(gn_lines):
150            if ('deps = ' in line or 'deps += ' in line) and ']' in line and (line_num + 1) not in in_dep_line_num:
151                in_dep_line_num.append(line_num + 1)
152        scan_line = list()
153        for num in new_line_num:
154            if num not in in_if_line_num and num in in_dep_line_num:
155                scan_line.append(num)
156        return scan_line
157
158    @classmethod
159    def __get_line_num(cls, txt_line_list):
160        line_num = list()
161        for one_txt in txt_line_list:
162            one_line_list = re.findall('@(.*?)@', one_txt)
163            if one_line_list != ['']:
164                line_num += one_line_list
165        line_num = [int(i) for i in line_num]
166        return line_num
167
168
169def get_args():
170    parser = argparse.ArgumentParser(
171        description=f"analyze components deps.\n")
172    parser.add_argument("-p", "--components_gn_path_list", required=True, type=str,
173                        help="path of pr BUILD.gn")
174    parser.add_argument("-n", "--new_line_nums_list", required=True, type=str,
175                        help="eg: 1_2_3,4_5")
176    parser.add_argument("-g", "--gn_name", required=True, type=str,
177                        help="gn file corresponding name")
178    parser.add_argument("-c", "--config_path", required=True, type=str,
179                        help="path of config_file")
180    parser.add_argument("-o", "--open_component_xml_path", required=True, type=str,
181                        help="open component name set")
182    parser.add_argument("-r", "--result_json_name", type=str, default="result",
183                        help="name of output_json")
184    return parser.parse_args()
185
186
187if __name__ == '__main__':
188    args = get_args()
189    gn_path_list_name = args.components_gn_path_list.split(',')
190    new_line_nums_list = args.new_line_nums_list.split(',')
191    gn_component_name = args.gn_name.split(',')
192    args_config_path = args.config_path
193    open_components_xml_path = args.open_component_xml_path
194    result_json = args.result_json_name
195    Analyzer.analysis(gn_path_list_name, new_line_nums_list, gn_component_name, args_config_path, open_components_xml_path,
196                      result_json)