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