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