• 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 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