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 22import time 23import threading 24from enum import Enum 25 26from containers.status import throw_exception 27from exceptions.ohos_exception import OHOSException 28from services.interface.build_file_generator_interface import BuildFileGeneratorInterface 29from resources.config import Config 30from containers.arg import Arg, ModuleType 31from util.system_util import SystemUtil 32from util.io_util import IoUtil 33from util.log_util import LogUtil 34 35 36class CMDTYPE(Enum): 37 GEN = 1 38 PATH = 2 39 DESC = 3 40 LS = 4 41 REFS = 5 42 FORMAT = 6 43 CLEAN = 7 44 45 46class Gn(BuildFileGeneratorInterface): 47 48 def __init__(self): 49 super().__init__() 50 self.config = Config() 51 self._regist_gn_path() 52 53 def run(self): 54 self.execute_gn_cmd(CMDTYPE.GEN) 55 56 @throw_exception 57 def execute_gn_cmd(self, cmd_type: int, **kwargs): 58 if cmd_type == CMDTYPE.GEN: 59 return self._execute_gn_gen_cmd() 60 elif cmd_type == CMDTYPE.PATH: 61 return self._execute_gn_path_cmd(**kwargs) 62 elif cmd_type == CMDTYPE.DESC: 63 return self._execute_gn_desc_cmd(**kwargs) 64 elif cmd_type == CMDTYPE.LS: 65 return self._execute_gn_ls_cmd(**kwargs) 66 elif cmd_type == CMDTYPE.REFS: 67 return self._execute_gn_refs_cmd(**kwargs) 68 elif cmd_type == CMDTYPE.FORMAT: 69 return self._execute_gn_format_cmd(**kwargs) 70 elif cmd_type == CMDTYPE.CLEAN: 71 return self._execute_gn_clean_cmd(**kwargs) 72 else: 73 raise OHOSException( 74 'You are tring to use an unsupported gn cmd type "{}"'.format(cmd_type), '3001') 75 76 '''Description: Get gn excutable path and regist it 77 @parameter: none 78 @return: Status 79 ''' 80 81 @throw_exception 82 def _regist_gn_path(self): 83 gn_path = os.path.join(self.config.root_path, 'prebuilts/build-tools/{}-x86/bin/gn' 84 .format(sys.platform)) 85 if os.path.exists(gn_path): 86 self.exec = gn_path 87 else: 88 raise OHOSException( 89 'There is no gn executable file at {}'.format(gn_path), '0001') 90 91 '''Description: Convert all registed args into a list 92 @parameter: none 93 @return: list of all registed args 94 ''' 95 96 def _convert_args(self) -> list: 97 args_list = [] 98 99 for key, value in self.args_dict.items(): 100 if isinstance(value, bool): 101 args_list.append('{}={}'.format(key, str(value).lower())) 102 103 elif isinstance(value, str): 104 args_list.append('{}="{}"'.format(key, value)) 105 106 elif isinstance(value, int): 107 args_list.append('{}={}'.format(key, value)) 108 109 elif isinstance(value, list): 110 args_list.append('{}="{}"'.format(key, "&&".join(value))) 111 112 return args_list 113 114 '''Description: Convert all registed flags into a list 115 @parameter: none 116 @return: list of all registed flags 117 ''' 118 119 def _convert_flags(self) -> list: 120 flags_list = [] 121 122 for key, value in self.flags_dict.items(): 123 if key == 'gn_flags' and isinstance(value, list): 124 flags_list += value 125 elif value == '': 126 flags_list.append('{}'.format(key)) 127 else: 128 flags_list.append('{}={}'.format(key, str(value)).lower()) 129 130 return flags_list 131 132 '''Description: Option validity check 133 @parameter: "option": Option to be checked 134 "args_file": Option config file 135 @return: Inspection result(True|False) 136 ''' 137 138 def _check_options_validity(self, option: str, args_file: dict): 139 support_sub_options = args_file.get( 140 "arg_attribute").get("support_sub_options") 141 option_name = option.lstrip('-') 142 option_value = "" 143 if '=' in option: 144 option_name, option_value = option.lstrip('-').split('=') 145 if option_name in support_sub_options: 146 sub_optional_list = support_sub_options.get( 147 option_name).get("arg_attribute").get("optional") 148 if sub_optional_list and option_value not in sub_optional_list: 149 if not len(option_value): 150 raise OHOSException('ERROR argument "--{}": Invalid choice "{}". ' 151 'choose from {}'.format(option_name, option_value, sub_optional_list), '3006') 152 else: 153 raise OHOSException('ERROR argument "--{}": Invalid choice "{}". ' 154 'choose from {}'.format(option_name, option_value, sub_optional_list), '3003') 155 else: 156 raise OHOSException('ERROR argument "{}": Invalid choice "{}". ' 157 'choose from {}'.format(args_file.get("arg_name"), 158 option, list(support_sub_options.keys())), '3003') 159 160 '''Description: Execute 'gn gen' command using registed args 161 @parameter: kwargs TBD 162 @return: None 163 ''' 164 165 @throw_exception 166 def _execute_gn_gen_cmd(self, **kwargs): 167 gn_gen_cmd = [self.exec, 'gen', '--json=gn_log.json', 168 '--args={}'.format(' '.join(self._convert_args())), 169 self.config.out_path] + self._convert_flags() 170 if self.config.os_level == 'mini' or self.config.os_level == 'small': 171 gn_gen_cmd.append(f'--script-executable={sys.executable}') 172 LogUtil.write_log(self.config.log_path, 'Excuting gn command: {} {} --args="{}" {}'.format( 173 self.exec, 'gen', 174 ' '.join(self._convert_args()).replace('"', "\\\""), 175 ' '.join(gn_gen_cmd[3:])), 176 'info') 177 if self.config.log_mode == 'silent': 178 def loading_animation(done_event): 179 frames = ["|", "/", "-", "\\"] 180 circle_times = 0 181 while not done_event.is_set(): 182 sys.stdout.write("\r" + "[OHOS INFO] GN parsing... " + frames[circle_times % len(frames)]) 183 sys.stdout.flush() 184 time.sleep(0.1) 185 circle_times += 1 186 187 def task(done_event): 188 SystemUtil.exec_command(gn_gen_cmd, self.config.log_path, log_mode=self.config.log_mode) 189 done_event.set() 190 sys.stdout.write("\n" + "[OHOS INFO] GN parsing Done\n") 191 done_event = threading.Event() 192 animation_thread = threading.Thread(target=loading_animation, args=(done_event,)) 193 animation_thread.start() 194 task(done_event) 195 animation_thread.join() 196 else: 197 SystemUtil.exec_command(gn_gen_cmd, self.config.log_path) 198 199 '''Description: Execute 'gn path' command using registed args 200 @parameter: kwargs TBD 201 @return: None 202 ''' 203 204 @throw_exception 205 def _execute_gn_path_cmd(self, **kwargs): 206 out_dir = kwargs.get("out_dir") 207 default_options = ['--all'] 208 args_file = Arg.read_args_file(ModuleType.TOOL)['path'] 209 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 210 gn_path_cmd = [self.exec, 'path', out_dir] 211 for arg in kwargs.get('args_list'): 212 if arg.startswith('-'): 213 self._check_options_validity(arg, args_file) 214 gn_path_cmd.append(arg) 215 gn_path_cmd.extend(default_options) 216 sort_index = gn_path_cmd.index 217 gn_path_cmd = list(set(gn_path_cmd)) 218 gn_path_cmd.sort(key=sort_index) 219 SystemUtil.exec_command(gn_path_cmd) 220 else: 221 raise OHOSException( 222 '"{}" Not a build directory.'.format(out_dir), '3004') 223 224 '''Description: Execute 'gn desc' command using registed args 225 @parameter: kwargs TBD 226 @return: None 227 ''' 228 229 @throw_exception 230 def _execute_gn_desc_cmd(self, **kwargs): 231 out_dir = kwargs.get("out_dir") 232 default_options = ['--tree', '--blame'] 233 args_file = Arg.read_args_file(ModuleType.TOOL)['desc'] 234 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 235 gn_desc_cmd = [self.exec, 'desc', out_dir] 236 for arg in kwargs.get('args_list'): 237 if arg.startswith('-'): 238 self._check_options_validity(arg, args_file) 239 gn_desc_cmd.append(arg) 240 gn_desc_cmd.extend(default_options) 241 sort_index = gn_desc_cmd.index 242 gn_desc_cmd = list(set(gn_desc_cmd)) 243 gn_desc_cmd.sort(key=sort_index) 244 SystemUtil.exec_command(gn_desc_cmd) 245 else: 246 raise OHOSException( 247 '"{}" Not a build directory.'.format(out_dir), '3004') 248 249 '''Description: Execute 'gn ls' command using registed args 250 @parameter: kwargs TBD 251 @return: None 252 ''' 253 254 @throw_exception 255 def _execute_gn_ls_cmd(self, **kwargs): 256 out_dir = kwargs.get("out_dir") 257 args_file = Arg.read_args_file(ModuleType.TOOL)['ls'] 258 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 259 gn_ls_cmd = [self.exec, 'ls', out_dir] 260 for arg in kwargs.get('args_list'): 261 if arg.startswith('-'): 262 self._check_options_validity(arg, args_file) 263 gn_ls_cmd.append(arg) 264 SystemUtil.exec_command(gn_ls_cmd) 265 else: 266 raise OHOSException( 267 '"{}" Not a build directory.'.format(out_dir), '3004') 268 269 '''Description: Execute 'gn refs' command using registed args 270 @parameter: kwargs TBD 271 @return: None 272 ''' 273 274 @throw_exception 275 def _execute_gn_refs_cmd(self, **kwargs): 276 out_dir = kwargs.get("out_dir") 277 args_file = Arg.read_args_file(ModuleType.TOOL)['refs'] 278 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 279 gn_refs_cmd = [self.exec, 'refs', out_dir] 280 for arg in kwargs.get('args_list'): 281 if arg.startswith('-'): 282 self._check_options_validity(arg, args_file) 283 gn_refs_cmd.append(arg) 284 SystemUtil.exec_command(gn_refs_cmd) 285 else: 286 raise OHOSException( 287 '"{}" Not a build directory.'.format(out_dir), '3004') 288 289 '''Description: Execute 'gn format' command using registed args 290 @parameter: kwargs TBD 291 @return: None 292 ''' 293 294 @throw_exception 295 def _execute_gn_format_cmd(self, **kwargs): 296 gn_format_cmd = [self.exec, 'format'] 297 args_file = Arg.read_args_file(ModuleType.TOOL)['format'] 298 for arg in kwargs.get("args_list"): 299 if (arg.endswith('.gn')): 300 if (os.path.exists(arg)): 301 gn_format_cmd.append(arg) 302 else: 303 raise OHOSException( 304 "ERROR Couldn't read '{}'".format(arg), '3005') 305 else: 306 if arg.startswith('-'): 307 self._check_options_validity(arg, args_file) 308 gn_format_cmd.append(arg) 309 SystemUtil.exec_command(gn_format_cmd) 310 311 '''Description: Execute 'gn clean' command using registed args 312 @parameter: kwargs TBD 313 @return: None 314 ''' 315 316 @throw_exception 317 def _execute_gn_clean_cmd(self, **kwargs): 318 out_dir = kwargs.get("out_dir") 319 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 320 gn_clean_cmd = [self.exec, 'clean', out_dir] 321 SystemUtil.exec_command(gn_clean_cmd) 322 else: 323 raise OHOSException('"{}" Not a build directory.' 324 'Usage: "gn clean <out_dir>"'.format(out_dir), '3004') 325