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