1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (c) 2024 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 16import os 17import json 18import re 19from typing import List, Tuple 20 21if __name__ == '__main__': 22 from basic_tool import BasicTool 23else: 24 from pkgs.basic_tool import BasicTool 25 26 27class GnCommonTool: 28 """ 29 处理BUILD.gn文件的通用方法 30 """ 31 32 @classmethod 33 def is_gn_variable(cls, target: str, has_quote: bool = True): 34 """ 35 判断target是否是gn中的变量: 36 规则:如果是有引号的模式,则没有引号均认为是变量,有引号的情况下,如有是"$xxx"的模式,则认为xxx是变量;如果是无引号模式,则只要$开头就认为是变量 37 b = "xxx" 38 c = b 39 c = "${b}" 40 "$p" 41 """ 42 target = target.strip() 43 if not has_quote: 44 return target.startswith("$") 45 if target.startswith('"') and target.endswith('"'): 46 target = target.strip('"') 47 if target.startswith("${") and target.endswith("}"): 48 return True 49 elif target.startswith("$"): 50 return True 51 return False 52 else: 53 return True 54 55 # 给__find_variables_in_gn用的,减少io 56 __var_val_mem_dict = dict() 57 58 @classmethod 59 def find_variables_in_gn(cls, var_name_tuple: tuple, path: str, stop_tail: str = "home") -> tuple: 60 """ 61 同时查找多个gn变量的值 62 var_name_tuple:变量名的tuple,变量名应是未经过处理后的,如: 63 xxx 64 "${xxx}" 65 "$xxx" 66 """ 67 68 if os.path.isfile(path): 69 path = os.path.split(path)[0] 70 var_val_dict = dict() 71 not_found_count = len(var_name_tuple) 72 for var in var_name_tuple: 73 val = GnCommonTool.__var_val_mem_dict.get(var) 74 if val is not None: 75 not_found_count -= 1 76 var_val_dict[var] = val 77 while not path.endswith(stop_tail) and not_found_count != 0: 78 for v in var_name_tuple: 79 cmd = r"grep -Ern '^( *){} *= *\".*?\"' --include=*.gn* {}| grep -Ev '\$' | head -n 1 | grep -E '\".*\"' -wo".format( 80 v.strip('"').lstrip("${").rstrip('}'), path) 81 output = os.popen(cmd).read().strip().strip('"') 82 if len(output) != 0: 83 not_found_count -= 1 84 var_val_dict[v] = output 85 GnCommonTool.__var_val_mem_dict[v] = output 86 path = os.path.split(path)[0] 87 return tuple(var_val_dict.values()) 88 89 @classmethod 90 def find_part_subsystem(cls, gn_file: str, project_path: str) -> tuple: 91 """ 92 查找gn_file对应的part_name和subsystem 93 如果在gn中找不到,就到bundle.json中去找 94 """ 95 part_var_flag = False # 标识这个变量从gn中取出的原始值是不是变量 96 subsystem_var_flag = False 97 var_list = list() 98 part_name_pattern = r"part_name *=\s*\S*" 99 subsystem_pattern = r"subsystem_name *=\s*\S*" 100 meta_grep_pattern = "grep -E '{}' {} | head -n 1" 101 part_cmd = meta_grep_pattern.format(part_name_pattern, gn_file) 102 subsystem_cmd = meta_grep_pattern.format(subsystem_pattern, gn_file) 103 104 part_name, subsystem_name = cls._parse_part_subsystem(part_var_flag, subsystem_var_flag, 105 var_list, part_cmd, subsystem_cmd, gn_file, project_path) 106 if part_name and subsystem_name: 107 return part_name, subsystem_name 108 # 如果有一个没有找到,就要一层层去找bundle.json文件 109 t_part_name, t_subsystem_name = cls.__find_part_subsystem_from_bundle( 110 gn_file, stop_tail=project_path) 111 if t_part_name: 112 part_name = t_part_name 113 if t_subsystem_name: 114 subsystem_name = t_subsystem_name 115 return part_name, subsystem_name 116 117 @classmethod 118 def __find_part_subsystem_from_bundle(cls, gnpath: str, stop_tail: str = "home") -> tuple: 119 """ 120 根据BUILD.gn的全路径,一层层往上面查找bundle.json文件, 121 并从bundle.json中查找part_name和subsystem 122 """ 123 filename = "bundle.json" 124 part_name = None 125 subsystem_name = None 126 if stop_tail not in gnpath: 127 return part_name, subsystem_name 128 if os.path.isfile(gnpath): 129 gnpath = os.path.split(gnpath)[0] 130 while not gnpath.endswith(stop_tail): 131 bundle_path = os.path.join(gnpath, filename) 132 if not os.path.isfile(bundle_path): # 如果该文件不在该目录下 133 gnpath = os.path.split(gnpath)[0] 134 continue 135 with open(bundle_path, 'r', encoding='utf-8') as f: 136 content = json.load(f) 137 try: 138 part_name = content["component"]["name"] 139 subsystem_name = content["component"]["subsystem"] 140 except KeyError: 141 ... 142 finally: 143 break 144 part_name = None if (part_name is not None and len( 145 part_name) == 0) else part_name 146 subsystem_name = None if (subsystem_name is not None and len( 147 subsystem_name) == 0) else subsystem_name 148 return part_name, subsystem_name 149 150 @classmethod 151 def _parse_part_subsystem(cls, part_var_flag: bool, subsystem_var_flag: bool, var_list: List[str], part_cmd: str, 152 subsystem_cmd: str, gn_file: str, project_path: str) -> Tuple[str, str]: 153 part_name = subsystem_name = None 154 part = os.popen(part_cmd).read().strip() 155 if len(part) != 0: 156 part = part.split('=')[-1].strip() 157 if GnCommonTool.is_gn_variable(part): 158 part_var_flag = True 159 var_list.append(part) 160 else: 161 part_name = part.strip('"') 162 if len(part_name) == 0: 163 part_name = None 164 subsystem = os.popen(subsystem_cmd).read().strip() 165 if len(subsystem) != 0: # 这里是只是看有没有grep到关键字 166 subsystem = subsystem.split('=')[-1].strip() 167 if GnCommonTool.is_gn_variable(subsystem): 168 subsystem_var_flag = True 169 var_list.append(subsystem) 170 else: 171 subsystem_name = subsystem.strip('"') 172 if len(subsystem_name) == 0: 173 subsystem_name = None 174 if part_var_flag and subsystem_var_flag: 175 part_name, subsystem_name = GnCommonTool.find_variables_in_gn( 176 tuple(var_list), gn_file, project_path) 177 elif part_var_flag: 178 t = GnCommonTool.find_variables_in_gn( 179 tuple(var_list), gn_file, project_path)[0] 180 part_name = t if t is not None and len(t) != 0 else part_name 181 elif subsystem_var_flag: 182 t = GnCommonTool.find_variables_in_gn( 183 tuple(var_list), gn_file, project_path)[0] 184 subsystem_name = t if t is not None and len( 185 t) != 0 else subsystem_name 186 return part_name, subsystem_name 187 188 189class GnVariableParser: 190 @classmethod 191 def string_parser(cls, var: str, content: str) -> str: 192 """ 193 解析值为字符串的变量,没有对引号进行去除,如果是a = b这种b为变量的,则无法匹配 194 :param content: 要进行解析的内容 195 :param var: 变量名 196 :return: 变量值[str] 197 """ 198 result = BasicTool.re_group_1( 199 content, r"{} *= *[\n]?(\".*?\")".format(var), flags=re.S | re.M) 200 return result 201 202 @classmethod 203 def list_parser(cls, var: str, content: str) -> List[str]: 204 """ 205 解析值为列表的变量,list的元素必须全为数字或字符串,且没有对引号进行去除,如果是a = b这种b为变量的,则无法匹配 206 :param var: 变量名 207 :param content: 要进行 208 :return: 变量值[List] 209 """ 210 result = BasicTool.re_group_1( 211 content, r"{} *= *(\[.*?\])".format(var), flags=re.S | re.M) 212 result_list = list() 213 for item in result.lstrip('[').rstrip(']').split('\n'): 214 item = item.strip().strip(',"') 215 if not item: 216 continue 217 result_list.append(item) 218 return result_list 219