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 Classes for gn's BUILD.gn 17 18import os 19import logging 20import re 21import ast 22import json 23import collections 24from typing import List 25if __name__ == '__main__': 26 from basic_tool import BasicTool 27else: 28 from pkgs.basic_tool import BasicTool 29 30 31class GnCommonTool: 32 """ 33 处理BUILD.gn文件的通用方法 34 """ 35 36 @classmethod 37 def is_gn_variable(cls, target: str, quote_processed: bool = False): 38 """ 39 判断target是否是gn中的变量: 40 规则:如果是quote_processed is False,则没有引号均认为是变量,有引号的情况下,如果是"$xxx"或${xx}的模式,则认为xxx是变量; 41 如果quote_processed is True,则只要$开头就认为是变量 42 b = "xxx" 43 c = b 44 c = "${b}" 45 "$p" 46 :param target: 要进行判断的字符串对象 47 :param quote_processed: 引号是否已经去除 48 :return: target是否为gn中的变量 49 """ 50 target = target.strip() 51 if quote_processed: 52 return target.startswith("$") 53 if target.startswith('"') and target.endswith('"'): 54 target = target.strip('"') 55 if target.startswith("${") and target.endswith("}"): 56 return True 57 elif target.startswith("$"): 58 return True 59 return False 60 else: 61 return True 62 63 @classmethod 64 def contains_gn_variable(cls, s: str, quote_processed: bool = False): 65 """ 66 判断字符串s中是否包含gn变量 67 """ 68 return cls.is_gn_variable(s, quote_processed) or ("$" in s) 69 70 # 给__find_variables_in_gn用的,减少io 71 __var_val_mem_dict = collections.defaultdict(str) 72 73 @classmethod 74 def find_variables_in_gn(cls, var_name_tuple: tuple, path: str, stop_tail: str = "home", use_cache: bool = False) -> \ 75 List[str]: 76 """ 77 同时查找多个gn变量的值 78 var_name_tuple:变量名的tuple,变量名应是未经过处理后的,如: 79 xxx 80 "${xxx}" 81 "$xxx" 82 :param var_name_tuple: 待查找的变量名的列表 83 :param path: 变量名所在文件的路径 84 :param stop_tail: 当path以stop_tail结尾时,停止查找 85 :param use_cache: 是否使用缓存 86 :return: 变量值的列表 87 """ 88 if os.path.isfile(path): 89 path, _ = os.path.split(path) 90 var_val_dict = collections.defaultdict(str) 91 not_found_count = len(var_name_tuple) 92 if use_cache: 93 for var in var_name_tuple: 94 val = GnCommonTool.__var_val_mem_dict[var] 95 if val: 96 not_found_count -= 1 97 var_val_dict[var] = val 98 while (stop_tail in path) and not_found_count: 99 for v in var_name_tuple: 100 pv = v.strip('"').lstrip("${").rstrip('}') 101 # 先直接grep出pv *= *\".*?\"的 102 # 然后排除含有$符的 103 # 再取第一个 104 # 最后只取引号内的 105 cmd = fr"grep -Ern '{pv} *= *\".*?\"' --include=*.gn* {path} | grep -Ev '\$' " \ 106 r"| head -n 1 | grep -E '\".*\"' -wo" 107 output = BasicTool.execute(cmd, lambda x: x.strip().strip('"')) 108 # backup:end 109 if not output: 110 continue 111 not_found_count -= 1 112 var_val_dict[v] = output 113 GnCommonTool.__var_val_mem_dict[v] = output 114 path, _ = os.path.split(path) 115 return list(var_val_dict.values()) 116 117 @classmethod 118 def replace_gn_variables(cls, s: str, gn_path: str, stop_tail: str) -> str: 119 """ 120 替换字符串中的gn变量名为其值,注意,没有对s是否真的包含gn变量进行验证 121 :param s: 待替换的字符串 122 :param gn_path: 字符串所在的gn文件 123 :param stop_tail: 当变量查找到stop_tail目录时停止 124 :return: 将变量替换为其值的字符串 125 """ 126 variable_list = GnCommonTool._find_gn_variable_list(s) 127 if len(variable_list) == 0: 128 return s 129 value_list = GnCommonTool.find_variables_in_gn( 130 tuple(variable_list), path=gn_path, stop_tail=stop_tail) 131 for k, v in dict(zip(variable_list, value_list)).items(): 132 s = s.replace(k, v) 133 return s 134 135 @classmethod 136 def find_values_of_variable(cls, var_name: str, path: str, stop_tail: str = "home") -> list: 137 """ 138 查找变量的值,如果有多个可能值,全部返回 139 :param var_name: 变量名 140 :param path: 变量名所在的文件 141 :param stop_tail: 当变量查找到stop_tail目录时停止 142 :return: 该变量的可能值 143 """ 144 if os.path.isfile(path): 145 path, _ = os.path.split(path) 146 result = list() 147 v = var_name.strip('"').lstrip("${").rstrip('}') 148 while stop_tail in path: 149 cmd = fr"grep -Ern '^( *){v} *= *\".*?\"' --include=*.gn* {path}" 150 output = os.popen(cmd).readlines() 151 path = os.path.split(path)[0] 152 if not output: 153 continue 154 for line in output: 155 line = line.split('=')[-1].strip().strip('"') 156 if len(line) == 0: 157 continue 158 result.append(line) 159 break 160 return result 161 162 @classmethod 163 def _find_gn_variable_list(cls, content: str) -> List: 164 """ 165 获取s中${xxx}或$xxx形式的gn变量 166 :param content: 待查找的字符串 167 :param sep: 分隔符,使用本分隔符将内容进行分隔然后逐一查找 168 :return: 变量名及其符号,eg:${abc}、$abc 169 :FIXME 没有对a = 'a' b = a中的b这种形式进行处理 170 """ 171 result = list() 172 splited = content.split(os.sep) 173 patern = re.compile(r"\${.*?}") 174 for item in splited: 175 m = re.findall(patern, item) 176 result.extend(m) 177 if len(m) == 0 and "$" in item: 178 item = item.strip('"') 179 result.append(item[item.index("$"):]) 180 return result 181 182 183class GnVariableParser: 184 @classmethod 185 def string_parser(cls, var: str, content: str) -> str: 186 """ 187 解析值为字符串的变量,没有对引号进行去除,如果是a = b这种b为变量的,则无法匹配 188 :param content: 要进行解析的内容 189 :param var: 变量名 190 :return: 变量值[str] 191 """ 192 result = BasicTool.re_group_1( 193 content, r"{} *= *[\n]?(\".*?\")".format(var), flags=re.S | re.M) 194 return result 195 196 @classmethod 197 def list_parser(cls, var: str, content: str) -> List[str]: 198 """ 199 解析值为列表的变量,list的元素必须全为数字或字符串,且没有对引号进行去除,如果是a = b这种b为变量的,则无法匹配 200 :param var: 变量名 201 :param content: 要进行 202 :return: 变量值[List] 203 """ 204 result = BasicTool.re_group_1( 205 content, r"{} *= *(\[.*?\])".format(var), flags=re.S | re.M) 206 result_list = list() 207 for item in result.lstrip('[').rstrip(']').split('\n'): 208 item = item.strip().strip(',"') 209 if not item: 210 continue 211 result_list.append(item) 212 return result_list 213