• 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 Tuple, Union, Callable, Literal, Dict, Text
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 BasePostHandler, gn_lineno_collect
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
83    def __call__(self, *args, **kwargs):
84        self.run()
85
86    @abstractmethod
87    def run(self):
88        ...
89
90    def _append(self, key: str, unit: Dict) -> None:
91        """
92        将target的结果存储到最终的结果字典中
93        :param key:进行存储的key,应为预期的文件名
94        :param unit: 要存储的target
95        :return: None
96        """
97        self.result_dict.get(self.target_type)[key] = unit
98
99    def _find_sc(self, gn_path: str):
100        # gn_path与project_path都应当是绝对路径
101        if not gn_path.startswith(self.project_path):
102            logging.error("gn_path and project_path is not consistent: gn_path={}, project_path={}".format(
103                gn_path, self.project_path))
104            return str(), str()
105        gp = gn_path.replace(self.project_path, "").lstrip(os.sep)
106        alter_list = list()
107        for k, v in self.sc_dict.items():
108            if gp.startswith(k):
109                alter_list.append(k)
110        if not alter_list:
111            return str(), str()
112        alter_list.sort(key=lambda x: len(x), reverse=True)
113        return self.sc_dict[alter_list[0]].get("subsystem"), self.sc_dict[alter_list[0]].get("component")
114
115
116def _gn_var_process(project_path: str, gn_v: str, alt_v: str, gn_path: str, ifrom: str, efrom: str,
117                    strip_quote: bool = False) -> Tuple[str, str]:
118    """
119    :param project_path:项目根路径
120    :gn_v:gn中的值(可能为变量或空)
121    :alt_v: 如果gn_v为空,则直接使用alt_v代替
122    :gn_path: gn文件的路径
123    :ifrom: 如果gn_v不为空,则其来自哪个字段
124    :efrom: 如果gn_v为空,则其(准确来说是alt_v)来自哪个字段
125    """
126    if strip_quote:
127        gn_v = gn_v.strip('"')
128    if gn_v:
129        if GnCommonTool.contains_gn_variable(gn_v):
130            gn_v = GnCommonTool.replace_gn_variables(
131                gn_v, gn_path, project_path).strip('"')
132        else:
133            gn_v = gn_v.strip('"')
134        gn_f = ifrom
135    else:
136        gn_v = alt_v
137        gn_f = efrom
138    return gn_v, gn_f
139
140
141class DefaultProcessor(BaseProcessor):
142
143    @property
144    def undefined(self):
145        return "UNDEFINED"
146
147    def helper(self, target_name: str, paragraph: str, gn_path: str, line_no: int, _sub: str, _com: str) -> Tuple[str]:
148        output_name = GnVariableParser.string_parser("output_name", paragraph)
149        output_name, out_from = _gn_var_process(self.project_path,
150                                                output_name, target_name, gn_path, "target_name", "target_name", True)
151        sub = GnVariableParser.string_parser("subsystem_name", paragraph)
152        com = GnVariableParser.string_parser("part_name", paragraph)
153        sub, sub_from = _gn_var_process(
154            self.project_path, sub, _sub, gn_path, "gn", "json", True)
155        com, com_from = _gn_var_process(
156            self.project_path, com, _com, gn_path, "gn", "json", True)
157        if not sub:
158            sub = self.undefined
159        if not com:
160            com = self.undefined
161        result = {
162            "gn_path": gn_path,
163            "target_type": self.target_type,
164            "line_no": line_no,
165            "subsystem_name": sub,
166            "component_name": com,
167            "subsystem_from": sub_from,
168            "component_from": com_from,
169            "target_name": target_name,
170            "output_name": output_name,
171            "output_from": out_from,
172        }
173        for k, h in self.other_info_handlers.items():
174            result[k] = h(paragraph)
175        key = self.unit_post_handler(result)
176        self._append(key, result)
177        if self.ud_post_handler:
178            self.ud_post_handler(result, self.result_dict)
179
180    def run(self):
181        for gn_path, line_no_list in self.gn_file_line_no_dict.items():
182            # 该路径下的主要的subsystem_name与component_name,如果target中没有指定,则取此值,如果指定了,则以target中的为准
183            _sub, _com = self._find_sc(gn_path)
184            with open(gn_path, 'r', encoding='utf-8') as f:
185                content = f.read()
186                itr = BasicTool.match_paragraph(
187                    content, start_pattern=self.target_type)
188                for line_no, p in zip(line_no_list, itr):
189                    paragraph = p.group()
190                    target_name = self.target_name_parser(paragraph).strip('"')
191                    if not target_name:
192                        continue
193                    if GnCommonTool.contains_gn_variable(target_name, quote_processed=True):
194                        possible_name_list = GnCommonTool.find_values_of_variable(target_name, path=gn_path,
195                                                                                  stop_tail=self.project_path)
196                        for n in possible_name_list:
197                            self.helper(n, paragraph, gn_path,
198                                        line_no, _sub, _com)
199                    else:
200                        self.helper(target_name, paragraph,
201                                    gn_path, line_no, _sub, _com)
202
203
204class StrResourceProcessor(DefaultProcessor):
205    def helper(self, target_name: str, paragraph: str, gn_path: str, line_no: int, _sub: str, _com: str) -> Tuple[str]:
206        resources = GnVariableParser.string_parser(
207            self.resource_field, paragraph)
208        if not resources:
209            return
210        _, resources = os.path.split(resources.strip('"'))
211
212        if GnCommonTool.contains_gn_variable(resources):
213            resources = GnCommonTool.replace_gn_variables(
214                resources, gn_path, self.project_path).strip('"')
215        sub = GnVariableParser.string_parser("subsystem_name", paragraph)
216        com = GnVariableParser.string_parser("part_name", paragraph)
217        sub, sub_from = _gn_var_process(
218            self.project_path, sub, _sub, gn_path, "gn", "json")
219        com, com_from = _gn_var_process(
220            self.project_path, com, _com, gn_path, "gn", "json")
221        if not sub:
222            sub = self.undefined
223        if not com:
224            com = self.undefined
225        _, file_name = os.path.split(resources)
226        result = {
227            "gn_path": gn_path,
228            "target_type": self.target_type,
229            "line_no": line_no,
230            "subsystem_name": sub,
231            "component_name": com,
232            "subsystem_from": sub_from,
233            "component_from": com_from,
234            "target_name": target_name,
235            "output_name": file_name,
236            "output_from": "file_name",
237        }
238        for k, h in self.other_info_handlers.items():
239            result[k] = h(paragraph)
240        key = self.unit_post_handler(result)
241        self._append(key, result)
242
243
244class ListResourceProcessor(DefaultProcessor):
245
246    def helper(self, target_name: str, paragraph: str, gn_path: str, line_no: int, _sub: str, _com: str) -> Tuple[str]:
247        resources = GnVariableParser.list_parser(
248            self.resource_field, paragraph)
249        if not resources:
250            return
251        sub = GnVariableParser.string_parser("subsystem_name", paragraph)
252        com = GnVariableParser.string_parser("part_name", paragraph)
253        sub, sub_from = _gn_var_process(
254            self.project_path, sub, _sub, gn_path, "gn", "json")
255        com, com_from = _gn_var_process(
256            self.project_path, com, _com, gn_path, "gn", "json")
257        if not sub:
258            sub = self.undefined
259        if not com:
260            com = self.undefined
261        for ff in resources:
262            _, file_name = os.path.split(ff)
263            result = {
264                "gn_path": gn_path,
265                "target_type": self.target_type,
266                "line_no": line_no,
267                "subsystem_name": sub,
268                "component_name": com,
269                "subsystem_from": sub_from,
270                "component_from": com_from,
271                "target_name": target_name,
272                "output_name": file_name,
273                "output_from": "file_name",
274            }
275            for k, h in self.other_info_handlers.items():
276                result[k] = h(paragraph)
277            key = self.unit_post_handler(result)
278            self._append(key, result)
279
280
281if __name__ == '__main__':
282    ...
283