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