1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# 5# Copyright (c) 2023 Huawei Device Co., Ltd. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19 20import sys 21import os 22from enum import Enum 23 24from containers.status import throw_exception 25from exceptions.ohos_exception import OHOSException 26from services.interface.build_file_generator_interface import BuildFileGeneratorInterface 27from resources.config import Config 28from containers.arg import Arg, ModuleType 29from util.system_util import SystemUtil 30from util.io_util import IoUtil 31from util.log_util import LogUtil 32 33 34class CMDTYPE(Enum): 35 GEN = 1 36 PATH = 2 37 DESC = 3 38 LS = 4 39 REFS = 5 40 FORMAT = 6 41 CLEAN = 7 42 43 44class Gn(BuildFileGeneratorInterface): 45 46 def __init__(self): 47 super().__init__() 48 self.config = Config() 49 self._regist_gn_path() 50 51 def run(self): 52 self.execute_gn_cmd(CMDTYPE.GEN) 53 54 @throw_exception 55 def execute_gn_cmd(self, cmd_type: int, **kwargs): 56 if cmd_type == CMDTYPE.GEN: 57 return self._execute_gn_gen_cmd() 58 elif cmd_type == CMDTYPE.PATH: 59 return self._execute_gn_path_cmd(**kwargs) 60 elif cmd_type == CMDTYPE.DESC: 61 return self._execute_gn_desc_cmd(**kwargs) 62 elif cmd_type == CMDTYPE.LS: 63 return self._execute_gn_ls_cmd(**kwargs) 64 elif cmd_type == CMDTYPE.REFS: 65 return self._execute_gn_refs_cmd(**kwargs) 66 elif cmd_type == CMDTYPE.FORMAT: 67 return self._execute_gn_format_cmd(**kwargs) 68 elif cmd_type == CMDTYPE.CLEAN: 69 return self._execute_gn_clean_cmd(**kwargs) 70 else: 71 raise OHOSException( 72 'You are tring to use an unsupported gn cmd type "{}"'.format(cmd_type), '3001') 73 74 '''Description: Get gn excutable path and regist it 75 @parameter: none 76 @return: Status 77 ''' 78 79 @throw_exception 80 def _regist_gn_path(self): 81 gn_path = os.path.join(self.config.root_path, 'prebuilts/build-tools/{}-x86/bin/gn' 82 .format(sys.platform)) 83 if os.path.exists(gn_path): 84 self.exec = gn_path 85 else: 86 raise OHOSException( 87 'There is no gn executable file at {}'.format(gn_path), '0001') 88 89 '''Description: Convert all registed args into a list 90 @parameter: none 91 @return: list of all registed args 92 ''' 93 94 def _convert_args(self) -> list: 95 args_list = [] 96 97 for key, value in self.args_dict.items(): 98 if isinstance(value, bool): 99 args_list.append('{}={}'.format(key, str(value).lower())) 100 101 elif isinstance(value, str): 102 args_list.append('{}="{}"'.format(key, value)) 103 104 elif isinstance(value, int): 105 args_list.append('{}={}'.format(key, value)) 106 107 elif isinstance(value, list): 108 args_list.append('{}="{}"'.format(key, "&&".join(value))) 109 110 return args_list 111 112 '''Description: Convert all registed flags into a list 113 @parameter: none 114 @return: list of all registed flags 115 ''' 116 117 def _convert_flags(self) -> list: 118 flags_list = [] 119 120 for key, value in self.flags_dict.items(): 121 if key == 'gn_flags' and isinstance(value, list): 122 flags_list += value 123 elif value == '': 124 flags_list.append('{}'.format(key)) 125 else: 126 flags_list.append('{}={}'.format(key, str(value)).lower()) 127 128 return flags_list 129 130 '''Description: Option validity check 131 @parameter: "option": Option to be checked 132 "args_file": Option config file 133 @return: Inspection result(True|False) 134 ''' 135 136 def _check_options_validity(self, option: str, args_file: dict): 137 support_sub_options = args_file.get( 138 "arg_attribute").get("support_sub_options") 139 option_name = option.lstrip('-') 140 option_value = "" 141 if '=' in option: 142 option_name, option_value = option.lstrip('-').split('=') 143 if option_name in support_sub_options: 144 sub_optional_list = support_sub_options.get( 145 option_name).get("arg_attribute").get("optional") 146 if sub_optional_list and option_value not in sub_optional_list: 147 if not len(option_value): 148 raise OHOSException('ERROR argument "--{}": Invalid choice "{}". ' 149 'choose from {}'.format(option_name, option_value, sub_optional_list), '3006') 150 else: 151 raise OHOSException('ERROR argument "--{}": Invalid choice "{}". ' 152 'choose from {}'.format(option_name, option_value, sub_optional_list), '3003') 153 else: 154 raise OHOSException('ERROR argument "{}": Invalid choice "{}". ' 155 'choose from {}'.format(args_file.get("arg_name"), 156 option, list(support_sub_options.keys())), '3003') 157 158 '''Description: Execute 'gn gen' command using registed args 159 @parameter: kwargs TBD 160 @return: None 161 ''' 162 163 @throw_exception 164 def _execute_gn_gen_cmd(self, **kwargs): 165 gn_gen_cmd = [self.exec, 'gen', 166 '--args={}'.format(' '.join(self._convert_args())), 167 self.config.out_path] + self._convert_flags() 168 if self.config.os_level == 'mini' or self.config.os_level == 'small': 169 gn_gen_cmd.append(f'--script-executable={sys.executable}') 170 try: 171 LogUtil.write_log(self.config.log_path, 'Excuting gn command: {} {} --args="{}" {}'.format( 172 self.exec, 'gen', 173 ' '.join(self._convert_args()).replace('"', "\\\""), 174 ' '.join(gn_gen_cmd[3:])), 175 'info') 176 SystemUtil.exec_command(gn_gen_cmd, self.config.log_path) 177 except OHOSException: 178 raise OHOSException('GN phase failed', '3000') 179 180 '''Description: Execute 'gn path' command using registed args 181 @parameter: kwargs TBD 182 @return: None 183 ''' 184 185 @throw_exception 186 def _execute_gn_path_cmd(self, **kwargs): 187 out_dir = kwargs.get("out_dir") 188 default_options = ['--all'] 189 args_file = Arg.read_args_file(ModuleType.TOOL)['path'] 190 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 191 gn_path_cmd = [self.exec, 'path', out_dir] 192 for arg in kwargs.get('args_list'): 193 if arg.startswith('-'): 194 self._check_options_validity(arg, args_file) 195 gn_path_cmd.append(arg) 196 gn_path_cmd.extend(default_options) 197 sort_index = gn_path_cmd.index 198 gn_path_cmd = list(set(gn_path_cmd)) 199 gn_path_cmd.sort(key=sort_index) 200 SystemUtil.exec_command(gn_path_cmd) 201 else: 202 raise OHOSException( 203 '"{}" Not a build directory.'.format(out_dir), '3004') 204 205 '''Description: Execute 'gn desc' command using registed args 206 @parameter: kwargs TBD 207 @return: None 208 ''' 209 210 @throw_exception 211 def _execute_gn_desc_cmd(self, **kwargs): 212 out_dir = kwargs.get("out_dir") 213 default_options = ['--tree', '--blame'] 214 args_file = Arg.read_args_file(ModuleType.TOOL)['desc'] 215 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 216 gn_desc_cmd = [self.exec, 'desc', out_dir] 217 for arg in kwargs.get('args_list'): 218 if arg.startswith('-'): 219 self._check_options_validity(arg, args_file) 220 gn_desc_cmd.append(arg) 221 gn_desc_cmd.extend(default_options) 222 sort_index = gn_desc_cmd.index 223 gn_desc_cmd = list(set(gn_desc_cmd)) 224 gn_desc_cmd.sort(key=sort_index) 225 SystemUtil.exec_command(gn_desc_cmd) 226 else: 227 raise OHOSException( 228 '"{}" Not a build directory.'.format(out_dir), '3004') 229 230 '''Description: Execute 'gn ls' command using registed args 231 @parameter: kwargs TBD 232 @return: None 233 ''' 234 235 @throw_exception 236 def _execute_gn_ls_cmd(self, **kwargs): 237 out_dir = kwargs.get("out_dir") 238 args_file = Arg.read_args_file(ModuleType.TOOL)['ls'] 239 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 240 gn_ls_cmd = [self.exec, 'ls', out_dir] 241 for arg in kwargs.get('args_list'): 242 if arg.startswith('-'): 243 self._check_options_validity(arg, args_file) 244 gn_ls_cmd.append(arg) 245 SystemUtil.exec_command(gn_ls_cmd) 246 else: 247 raise OHOSException( 248 '"{}" Not a build directory.'.format(out_dir), '3004') 249 250 '''Description: Execute 'gn refs' command using registed args 251 @parameter: kwargs TBD 252 @return: None 253 ''' 254 255 @throw_exception 256 def _execute_gn_refs_cmd(self, **kwargs): 257 out_dir = kwargs.get("out_dir") 258 args_file = Arg.read_args_file(ModuleType.TOOL)['refs'] 259 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 260 gn_refs_cmd = [self.exec, 'refs', out_dir] 261 for arg in kwargs.get('args_list'): 262 if arg.startswith('-'): 263 self._check_options_validity(arg, args_file) 264 gn_refs_cmd.append(arg) 265 SystemUtil.exec_command(gn_refs_cmd) 266 else: 267 raise OHOSException( 268 '"{}" Not a build directory.'.format(out_dir), '3004') 269 270 '''Description: Execute 'gn format' command using registed args 271 @parameter: kwargs TBD 272 @return: None 273 ''' 274 275 @throw_exception 276 def _execute_gn_format_cmd(self, **kwargs): 277 gn_format_cmd = [self.exec, 'format'] 278 args_file = Arg.read_args_file(ModuleType.TOOL)['format'] 279 for arg in kwargs.get("args_list"): 280 if (arg.endswith('.gn')): 281 if (os.path.exists(arg)): 282 gn_format_cmd.append(arg) 283 else: 284 raise OHOSException( 285 "ERROR Couldn't read '{}'".format(arg), '3005') 286 else: 287 if arg.startswith('-'): 288 self._check_options_validity(arg, args_file) 289 gn_format_cmd.append(arg) 290 SystemUtil.exec_command(gn_format_cmd) 291 292 '''Description: Execute 'gn clean' command using registed args 293 @parameter: kwargs TBD 294 @return: None 295 ''' 296 297 @throw_exception 298 def _execute_gn_clean_cmd(self, **kwargs): 299 out_dir = kwargs.get("out_dir") 300 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 301 gn_clean_cmd = [self.exec, 'clean', out_dir] 302 SystemUtil.exec_command(gn_clean_cmd) 303 else: 304 raise OHOSException('"{}" Not a build directory.' 305 'Usage: "gn clean <out_dir>"'.format(out_dir), '3004') 306