• 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
16import re
17import sys
18import os
19import xml.etree.ElementTree as ET
20
21
22class CheckGnOnline(object):
23    TARGET_NAME = ('ohos_shared_library',
24                   'ohos_static_library', 'ohos_executable',
25                   'ohos_source_set',
26                   'ohos_copy',
27                   'ohos_group',
28                   'ohos_prebuilt_executable',
29                   'ohos_prebuilt_shared_library',
30                   'ohos_prebuilt_static_library',
31                   'ohos_prebuilt_etc')
32
33    def __init__(self, gn_data: dict) -> None:
34        self.status = True
35        self.gn_data = gn_data
36        self.err_info = list()
37
38    def merge_line(self) -> dict:
39        ret_dict = dict()
40        for key, values in self.gn_data.items():
41            contents = ''
42            row_range = list()
43            start = -2
44            end = -2
45            for line in values:
46                pos = line[0]
47                content = line[1]
48
49                if pos == end + 1:
50                    end = pos
51                    contents += '\n{}'.format(content)
52                elif start != end:
53                    row_range.append([start, end, contents])
54                    contents = content
55                    start = pos
56                    end = pos
57                else:
58                    contents = content
59                    start = pos
60                    end = pos
61
62                if pos == values[-1][0] and start != end:
63                    row_range.append([start, end, contents])
64
65            ret_dict.update({key: row_range})
66        return ret_dict
67
68    def check_have_product_name(self, key: str, line: list) -> None:
69        rules = '规则4.1 部件编译脚本中禁止使用产品名称变量'
70        check_list = ['product_name', 'device_name']
71        flag = {check_list[0]: False, check_list[1]: False}
72        for check_item in check_list:
73            if line[1].find(check_item) != -1 and line[1].find("==") != -1:
74                flag[check_item] = True
75        if any(flag.values()):
76            issue = '存在'
77            issue += check_list[0] if flag[check_list[0]] else ''
78            issue += ',' if all(flag.values()) else ''
79            issue += check_list[1] if flag[check_list[1]] else ''
80            pos = "line:{}  {}".format(line[0], line[1])
81            self.err_info.append([key, pos, rules, issue])
82            self.status = False
83        return
84
85    def check_abs_path(self, key: str, line: list) -> None:
86        rules = '规则3.1 部件编译脚本中只允许引用本部件路径,禁止引用其他部件的绝对或相对路径'
87        issue = '存在绝对路径'
88        abs_path_pattern = r'"\/(\/[^\/\n]+){1,63}"'
89        abs_info = list()
90        abs_iter = re.finditer(abs_path_pattern, line[1])
91        for match in abs_iter:
92            path = match.group().strip('"')
93            if path.startswith('//build') or path.startswith('//third_party'):
94                break
95            if path.startswith('//prebuilts'):
96                break
97            if path.startswith('//out'):
98                break
99            abs_info.append(path)
100        if len(abs_info) > 0:
101            pos = "line:{}  {}".format(line[0], line[1])
102            self.err_info.append([key, pos, rules, issue])
103            self.status = False
104        return
105
106    def iter_modified_line(self, key, modified_line, target_pattern) -> None:
107        rules = '规则3.2 部件编译目标必须指定部件和子系统名'
108        check_list = ['subsystem_name', 'part_name']
109        for line in modified_line:
110            targets = re.finditer(target_pattern, line[2], re.M)
111            for it_target in targets:
112                flag = {check_list[0]: False, check_list[1]: False}
113                target = it_target.group()
114                start_target = target.split('\n')[0]
115                target_pos = line[2].find(start_target)
116                line_offset = line[2].count('\n', 1, target_pos)
117
118                if target.find(check_list[0]) == -1:
119                    flag[check_list[0]] = True
120                if target.find(check_list[1]) == -1:
121                    flag[check_list[1]] = True
122                if any(flag.values()):
123                    issue = 'target不存在'
124                    issue += check_list[0] if flag[check_list[0]] else ''
125                    issue += ',' if all(flag.values()) else ''
126                    issue += check_list[1] if flag[check_list[1]] else ''
127                    pos = "line:{}  {}".format(
128                        line[0] + line_offset, start_target)
129                    self.err_info.append([key, pos, rules, issue])
130                    self.status = False
131        return
132
133    def check_pn_sn(self) -> None:
134        rules = '规则3.2 部件编译目标必须指定部件和子系统名'
135        target_pattern = r"^( *)("
136        for target in self.TARGET_NAME:
137            target_pattern += target
138            if target != self.TARGET_NAME[-1]:
139                target_pattern += r'|'
140        target_pattern += r")[\s|\S]*?\n\1}$"
141
142        gn_data_merge = self.merge_line()
143        for key, values in gn_data_merge.items():
144            self.iter_modified_line(key, values, target_pattern)
145        return
146
147    def load_ohos_xml(self, path):
148        ret_dict = dict()
149        tree = ET.parse(path)
150        root = tree.getroot()
151        for node in root.iter():
152            if node.tag != 'project':
153                continue
154            repo_info = node.attrib
155            ret_item = {repo_info['name']: repo_info['groups']}
156            ret_dict.update(ret_item)
157        return ret_dict
158
159    def is_checked(self, file, xml_dict):
160        repo_name = file.split(',')[0].split('/')[-3]
161
162        if repo_name in xml_dict.keys():
163            if not file.endswith("(new file)"):
164                if xml_dict[repo_name].find('ohos:mini') != -1:
165                    return False
166                if xml_dict[repo_name].find('ohos:small') != -1:
167                    return False
168        if repo_name.startswith('device_') or repo_name.startswith('vendor'):
169            return False
170        if repo_name.startswith('build') or repo_name.startswith('third_party'):
171            return False
172        return True
173
174    def pre_check(self):
175        xml_dict = self.load_ohos_xml(".repo/manifests/ohos/ohos.xml")
176        for key in list(self.gn_data.keys()):
177            if not self.is_checked(key, xml_dict):
178                self.gn_data.pop(key)
179
180    def check(self):
181        if not self.gn_data:
182            return
183        for key, values in self.gn_data.items():
184            for line in values:
185                if line[1].strip().startswith("import"):
186                    continue;
187                self.check_have_product_name(key, line)
188                self.check_abs_path(key, line)
189        self.check_pn_sn()
190
191    def output(self):
192        self.pre_check()
193        self.check()
194
195        return self.status, self.err_info
196
197
198if __name__ == "__main__":
199    data = {'a.gn': [[11, 'dsfsdf product_name'], [13, '("//build/dasd/")']],
200            'b.gn': [[4, 'sdfd'],
201                     [5, 'ohos_shared_library("librawfile") {'],
202                     [14, 'sdfd'],
203                     [15, 'ohos_shared_library("librawfile") {'],
204                     [16, '  include_dirs = ['],
205                     [17, '    "//base/global/resource_management/frameworks/resmgr/include",'],
206                     [18, '  subsystem_name = "global"'], [19, '}'],
207                     [25, '  subsystem_name = "global"'], [29, '}']]}
208
209    a = CheckGnOnline(data)
210    a.output()
211    exit(0)
212