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