• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright (c) 2022 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 contains some template processor to collection information
17# from some gn's template in BUILD.gn
18
19from typing import *
20from abc import ABC, abstractmethod
21import os
22import logging
23from pprint import pprint
24
25from pkgs.basic_tool import do_nothing, BasicTool
26from pkgs.gn_common_tool import GnCommonTool, GnVariableParser
27from misc import *
28
29TYPE = Literal["str", "list"]
30
31
32class BaseProcessor(ABC):
33    """
34    extend and usage:
35    DerivedClass(BaseProcessor):
36        def call():
37            # your implementation
38
39    """
40
41    def __init__(self,
42                 project_path: str,
43                 result_dict: Dict[str, Dict[str, Dict]],
44                 target_type: str,
45                 match_pattern: str,
46                 sub_com_dict: Dict[str, Dict[str, str]],
47                 target_name_parser: Callable[[Text], Text] = do_nothing,
48                 other_info_handlers: Dict[str, Callable[[
49                     Text], Union[str, list]]] = dict(),
50                 unit_post_handler: BasePostHandler = do_nothing,
51                 resource_field: str = None,
52                 ud_post_handler: Callable[[Dict, Dict], None] = None
53                 ):
54        """
55        :param project_path: 项目根路径
56        :param result_dict: 存储结果的字典
57        :param target_type: target类型,eg:"shared_library"
58        :param match_pattern: 用于进行的模式,eg:r"^( *)shared_library\(.*?\)"
59        :param sub_com_dict: 从get_subsystem_component.py运行结果加载进来的dict,包含oh整体上的子系统、部件及其路径信息
60        :param target_name_parser: 解析target名字的Callable
61        :param other_info_handlers: 对其他信息进行收集处理,eg:{"sources": SourcesParser}——表示要处理target段落中的sources属性,
62                           SourceParser是对target段落进行分析处理的Callable,接受一个字符串作为参数
63        :param unit_post_handler: 对最终要存储的结果字典进行后处理,应当返回一个字符串作为存储时的key,且该key应为预期产物去除前后缀后的名字
64        :resource_field: 针对资源类target,资源字段,如files = ["a.txt","b.txt"],则field为files
65        :ud_post_handler: 参数为unit和result_dict的handler
66        """
67        if target_type not in result_dict.keys():
68            result_dict[target_type] = dict()
69        self.project_path = project_path
70        self.result_dict = result_dict
71        self.target_type = target_type
72        self.match_pattern = match_pattern
73        self.gn_file_line_no_dict = gn_lineno_collect(
74            self.match_pattern, self.project_path)
75        self.sc_dict = sub_com_dict
76        self.target_name_parser = target_name_parser
77        self.other_info_handlers = other_info_handlers
78        self.unit_post_handler = unit_post_handler
79        self.resource_field = resource_field
80        self.ud_post_handler = ud_post_handler
81
82    def _append(self, key: str, unit: Dict) -> None:
83        """
84        将target的结果存储到最终的结果字典中
85        :param key:进行存储的key,应为预期的文件名
86        :param unit: 要存储的target
87        :return: None
88        """
89        self.result_dict.get(self.target_type)[key] = unit
90
91    def _find_sc(self, gn_path: str):
92        # gn_path与project_path都应当是绝对路径
93        if not gn_path.startswith(self.project_path):
94            logging.error("gn_path and project_path is not consistent: gn_path={}, project_path={}".format(
95                gn_path, self.project_path))
96            return str(), str()
97        gp = gn_path.replace(self.project_path, "").lstrip(os.sep)
98        alter_list = list()
99        for k, v in self.sc_dict.items():
100            if gp.startswith(k):
101                alter_list.append(k)
102        if not alter_list:
103            return str(), str()
104        alter_list.sort(key=lambda x: len(x), reverse=True)
105        return self.sc_dict[alter_list[0]].get("subsystem"),  self.sc_dict[alter_list[0]].get("component")
106
107    @abstractmethod
108    def run(self):
109        ...
110
111    def __call__(self, *args, **kwargs):
112        self.run()
113
114
115def _gn_var_process(project_path: str, gn_v: str, alt_v: str, gn_path: str, ifrom: str, efrom: str, strip_quote: bool = False) -> Tuple[str, str]:
116    """
117    :param project_path:项目根路径
118    :gn_v:gn中的值(可能为变量或空)
119    :alt_v: 如果gn_v为空,则直接使用alt_v代替
120    :gn_path: gn文件的路径
121    :ifrom: 如果gn_v不为空,则其来自哪个字段
122    :efrom: 如果gn_v为空,则其(准确来说是alt_v)来自哪个字段
123    """
124    if strip_quote:
125        gn_v = gn_v.strip('"')
126    if gn_v:
127        if GnCommonTool.contains_gn_variable(gn_v):
128            gn_v = GnCommonTool.replace_gn_variables(
129                gn_v, gn_path, project_path).strip('"')
130        else:
131            gn_v = gn_v.strip('"')
132        gn_f = ifrom
133    else:
134        gn_v = alt_v
135        gn_f = efrom
136    return gn_v, gn_f
137
138
139class DefaultProcessor(BaseProcessor):
140
141    @property
142    def UNDEFINED(self):
143        return "UNDEFINED"
144
145    def helper(self, target_name: str, paragraph: str, gn_path: str, line_no: int, _sub: str, _com: str) -> Tuple[str]:
146        output_name = GnVariableParser.string_parser("output_name", paragraph)
147        output_name, out_from = _gn_var_process(self.project_path,
148                                                output_name, target_name, gn_path, "target_name", "target_name", True)
149        sub = GnVariableParser.string_parser("subsystem_name", paragraph)
150        com = GnVariableParser.string_parser("part_name", paragraph)
151        sub, sub_from = _gn_var_process(
152            self.project_path, sub, _sub, gn_path, "gn", "json", True)
153        com, com_from = _gn_var_process(
154            self.project_path, com, _com, gn_path, "gn", "json", True)
155        if not sub:
156            sub = self.UNDEFINED
157        if not com:
158            com = self.UNDEFINED
159        result = {
160            "gn_path": gn_path,
161            "target_type": self.target_type,
162            "line_no": line_no,
163            "subsystem_name": sub,
164            "component_name": com,
165            "subsystem_from": sub_from,
166            "component_from": com_from,
167            "target_name": target_name,
168            "output_name": output_name,
169            "output_from": out_from,
170        }
171        for k, h in self.other_info_handlers.items():
172            result[k] = h(paragraph)
173        key = self.unit_post_handler(result)
174        self._append(key, result)
175        if self.ud_post_handler:
176            self.ud_post_handler(result, self.result_dict)
177
178    def run(self):
179        for gn_path, line_no_list in self.gn_file_line_no_dict.items():
180            # 该路径下的主要的subsystem_name与component_name,如果target中没有指定,则取此值,如果指定了,则以target中的为准
181            _sub, _com = self._find_sc(gn_path)
182            with open(gn_path, 'r', encoding='utf-8') as f:
183                content = f.read()
184                itr = BasicTool.match_paragraph(
185                    content, start_pattern=self.target_type)
186                for line_no, p in zip(line_no_list, itr):
187                    paragraph = p.group()
188                    target_name = self.target_name_parser(paragraph).strip('"')
189                    if not target_name:
190                        continue
191                    if GnCommonTool.contains_gn_variable(target_name, quote_processed=True):
192                        possible_name_list = GnCommonTool.find_values_of_variable(target_name, path=gn_path,
193                                                                                  stop_tail=self.project_path)
194                        for n in possible_name_list:
195                            self.helper(n, paragraph, gn_path,
196                                        line_no, _sub, _com)
197                    else:
198                        self.helper(target_name, paragraph,
199                                    gn_path, line_no, _sub, _com)
200
201
202class StrResourceProcessor(DefaultProcessor):
203    def helper(self, target_name: str, paragraph: str, gn_path: str, line_no: int, _sub: str, _com: str) -> Tuple[str]:
204        resources = GnVariableParser.string_parser(
205            self.resource_field, paragraph)
206        if not resources:
207            return
208        _, resources = os.path.split(resources.strip('"'))
209
210        if GnCommonTool.contains_gn_variable(resources):
211            resources = GnCommonTool.replace_gn_variables(
212                resources, gn_path, self.project_path).strip('"')
213        sub = GnVariableParser.string_parser("subsystem_name", paragraph)
214        com = GnVariableParser.string_parser("part_name", paragraph)
215        sub, sub_from = _gn_var_process(
216            self.project_path, sub, _sub, gn_path, "gn", "json")
217        com, com_from = _gn_var_process(
218            self.project_path, com, _com, gn_path, "gn", "json")
219        if not sub:
220            sub = self.UNDEFINED
221        if not com:
222            com = self.UNDEFINED
223        _, file_name = os.path.split(resources)
224        result = {
225            "gn_path": gn_path,
226            "target_type": self.target_type,
227            "line_no": line_no,
228            "subsystem_name": sub,
229            "component_name": com,
230            "subsystem_from": sub_from,
231            "component_from": com_from,
232            "target_name": target_name,
233            "output_name": file_name,
234            "output_from": "file_name",
235        }
236        for k, h in self.other_info_handlers.items():
237            result[k] = h(paragraph)
238        key = self.unit_post_handler(result)
239        self._append(key, result)
240
241
242class ListResourceProcessor(DefaultProcessor):
243
244    def helper(self, target_name: str, paragraph: str, gn_path: str, line_no: int, _sub: str, _com: str) -> Tuple[str]:
245        resources = GnVariableParser.list_parser(
246            self.resource_field, paragraph)
247        if not resources:
248            return
249        sub = GnVariableParser.string_parser("subsystem_name", paragraph)
250        com = GnVariableParser.string_parser("part_name", paragraph)
251        sub, sub_from = _gn_var_process(
252            self.project_path, sub, _sub, gn_path, "gn", "json")
253        com, com_from = _gn_var_process(
254            self.project_path, com, _com, gn_path, "gn", "json")
255        if not sub:
256            sub = self.UNDEFINED
257        if not com:
258            com = self.UNDEFINED
259        for ff in resources:
260            _, file_name = os.path.split(ff)
261            result = {
262                "gn_path": gn_path,
263                "target_type": self.target_type,
264                "line_no": line_no,
265                "subsystem_name": sub,
266                "component_name": com,
267                "subsystem_from": sub_from,
268                "component_from": com_from,
269                "target_name": target_name,
270                "output_name": file_name,
271                "output_from": "file_name",
272            }
273            for k, h in self.other_info_handlers.items():
274                result[k] = h(paragraph)
275            key = self.unit_post_handler(result)
276            self._append(key, result)
277
278
279if __name__ == '__main__':
280    ...
281