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: Execute 'gn gen' command using registed args 92 @parameter: kwargs TBD 93 @return: None 94 ''' 95 96 @throw_exception 97 def _execute_gn_gen_cmd(self, **kwargs): 98 gn_gen_cmd = [self.exec, 'gen', '--json=gn_log.json', 99 '--args={}'.format(' '.join(self._convert_args())), 100 self.config.out_path] + self._convert_flags() 101 if self.config.os_level == 'mini' or self.config.os_level == 'small': 102 gn_gen_cmd.append(f'--script-executable={sys.executable}') 103 LogUtil.write_log(self.config.log_path, 'Excuting gn command: {} {} --args="{}" {}'.format( 104 self.exec, 'gen', 105 ' '.join(self._convert_args()).replace('"', "\\\""), 106 ' '.join(gn_gen_cmd[3:])), 107 'info') 108 if self.config.log_mode == 'silent': 109 def loading_animation(done_event): 110 frames = ["|", "/", "-", "\\"] 111 circle_times = 0 112 while not done_event.is_set(): 113 sys.stdout.write("\r" + "[OHOS INFO] GN parsing... " + frames[circle_times % len(frames)]) 114 sys.stdout.flush() 115 time.sleep(0.1) 116 circle_times += 1 117 118 def task(done_event): 119 SystemUtil.exec_command(gn_gen_cmd, self.config.log_path, log_mode=self.config.log_mode) 120 done_event.set() 121 sys.stdout.write("\n" + "[OHOS INFO] GN parsing Done\n") 122 done_event = threading.Event() 123 animation_thread = threading.Thread(target=loading_animation, args=(done_event,)) 124 animation_thread.start() 125 task(done_event) 126 animation_thread.join() 127 else: 128 SystemUtil.exec_command(gn_gen_cmd, self.config.log_path) 129 130 '''Description: Execute 'gn path' command using registed args 131 @parameter: kwargs TBD 132 @return: None 133 ''' 134 135 @throw_exception 136 def _execute_gn_path_cmd(self, **kwargs): 137 out_dir = kwargs.get("out_dir") 138 default_options = ['--all'] 139 args_file = Arg.read_args_file(ModuleType.TOOL)['path'] 140 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 141 gn_path_cmd = [self.exec, 'path', out_dir] 142 for arg in kwargs.get('args_list'): 143 if arg.startswith('-'): 144 self._check_options_validity(arg, args_file) 145 gn_path_cmd.append(arg) 146 gn_path_cmd.extend(default_options) 147 sort_index = gn_path_cmd.index 148 gn_path_cmd = list(set(gn_path_cmd)) 149 gn_path_cmd.sort(key=sort_index) 150 SystemUtil.exec_command(gn_path_cmd) 151 else: 152 raise OHOSException( 153 '"{}" Not a build directory.'.format(out_dir), '3004') 154 155 '''Description: Execute 'gn desc' command using registed args 156 @parameter: kwargs TBD 157 @return: None 158 ''' 159 160 @throw_exception 161 def _execute_gn_desc_cmd(self, **kwargs): 162 out_dir = kwargs.get("out_dir") 163 default_options = ['--tree', '--blame'] 164 args_file = Arg.read_args_file(ModuleType.TOOL)['desc'] 165 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 166 gn_desc_cmd = [self.exec, 'desc', out_dir] 167 for arg in kwargs.get('args_list'): 168 if arg.startswith('-'): 169 self._check_options_validity(arg, args_file) 170 gn_desc_cmd.append(arg) 171 gn_desc_cmd.extend(default_options) 172 sort_index = gn_desc_cmd.index 173 gn_desc_cmd = list(set(gn_desc_cmd)) 174 gn_desc_cmd.sort(key=sort_index) 175 SystemUtil.exec_command(gn_desc_cmd) 176 else: 177 raise OHOSException( 178 '"{}" Not a build directory.'.format(out_dir), '3004') 179 180 '''Description: Execute 'gn ls' command using registed args 181 @parameter: kwargs TBD 182 @return: None 183 ''' 184 185 @throw_exception 186 def _execute_gn_ls_cmd(self, **kwargs): 187 out_dir = kwargs.get("out_dir") 188 args_file = Arg.read_args_file(ModuleType.TOOL)['ls'] 189 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 190 gn_ls_cmd = [self.exec, 'ls', out_dir] 191 for arg in kwargs.get('args_list'): 192 if arg.startswith('-'): 193 self._check_options_validity(arg, args_file) 194 gn_ls_cmd.append(arg) 195 SystemUtil.exec_command(gn_ls_cmd) 196 else: 197 raise OHOSException( 198 '"{}" Not a build directory.'.format(out_dir), '3004') 199 200 '''Description: Execute 'gn refs' command using registed args 201 @parameter: kwargs TBD 202 @return: None 203 ''' 204 205 @throw_exception 206 def _execute_gn_refs_cmd(self, **kwargs): 207 out_dir = kwargs.get("out_dir") 208 args_file = Arg.read_args_file(ModuleType.TOOL)['refs'] 209 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 210 gn_refs_cmd = [self.exec, 'refs', out_dir] 211 for arg in kwargs.get('args_list'): 212 if arg.startswith('-'): 213 self._check_options_validity(arg, args_file) 214 gn_refs_cmd.append(arg) 215 SystemUtil.exec_command(gn_refs_cmd) 216 else: 217 raise OHOSException( 218 '"{}" Not a build directory.'.format(out_dir), '3004') 219 220 '''Description: Execute 'gn format' command using registed args 221 @parameter: kwargs TBD 222 @return: None 223 ''' 224 225 @throw_exception 226 def _execute_gn_format_cmd(self, **kwargs): 227 gn_format_cmd = [self.exec, 'format'] 228 args_file = Arg.read_args_file(ModuleType.TOOL)['format'] 229 for arg in kwargs.get("args_list"): 230 if (arg.endswith('.gn')): 231 if (os.path.exists(arg)): 232 gn_format_cmd.append(arg) 233 else: 234 raise OHOSException( 235 "ERROR Couldn't read '{}'".format(arg), '3005') 236 else: 237 if arg.startswith('-'): 238 self._check_options_validity(arg, args_file) 239 gn_format_cmd.append(arg) 240 SystemUtil.exec_command(gn_format_cmd) 241 242 '''Description: Execute 'gn clean' command using registed args 243 @parameter: kwargs TBD 244 @return: None 245 ''' 246 247 @throw_exception 248 def _execute_gn_clean_cmd(self, **kwargs): 249 out_dir = kwargs.get("out_dir") 250 if (os.path.exists(os.path.join(out_dir, "args.gn"))): 251 gn_clean_cmd = [self.exec, 'clean', out_dir] 252 SystemUtil.exec_command(gn_clean_cmd) 253 else: 254 raise OHOSException('"{}" Not a build directory.' 255 'Usage: "gn clean <out_dir>"'.format(out_dir), '3004') 256 257 '''Description: Convert all registed args into a list 258 @parameter: none 259 @return: list of all registed args 260 ''' 261 262 def _convert_args(self) -> list: 263 args_list = [] 264 265 for key, value in self.args_dict.items(): 266 if isinstance(value, bool): 267 args_list.append('{}={}'.format(key, str(value).lower())) 268 269 elif isinstance(value, str): 270 args_list.append('{}="{}"'.format(key, value)) 271 272 elif isinstance(value, int): 273 args_list.append('{}={}'.format(key, value)) 274 275 elif isinstance(value, list): 276 args_list.append('{}="{}"'.format(key, "&&".join(value))) 277 278 return args_list 279 280 '''Description: Convert all registed flags into a list 281 @parameter: none 282 @return: list of all registed flags 283 ''' 284 285 def _convert_flags(self) -> list: 286 flags_list = [] 287 288 for key, value in self.flags_dict.items(): 289 if key == 'gn_flags' and isinstance(value, list): 290 flags_list += value 291 elif value == '': 292 flags_list.append('{}'.format(key)) 293 else: 294 flags_list.append('{}={}'.format(key, str(value)).lower()) 295 296 return flags_list 297 298 '''Description: Option validity check 299 @parameter: "option": Option to be checked 300 "args_file": Option config file 301 @return: Inspection result(True|False) 302 ''' 303 304 def _check_options_validity(self, option: str, args_file: dict): 305 support_sub_options = args_file.get( 306 "arg_attribute").get("support_sub_options") 307 option_name = option.lstrip('-') 308 option_value = "" 309 if '=' in option: 310 option_name, option_value = option.lstrip('-').split('=') 311 if option_name in support_sub_options: 312 sub_optional_list = support_sub_options.get( 313 option_name).get("arg_attribute").get("optional") 314 if sub_optional_list and option_value not in sub_optional_list: 315 if not len(option_value): 316 raise OHOSException('ERROR argument "--{}": Invalid choice "{}". ' 317 'choose from {}'.format(option_name, option_value, sub_optional_list), '3006') 318 else: 319 raise OHOSException('ERROR argument "--{}": Invalid choice "{}". ' 320 'choose from {}'.format(option_name, option_value, sub_optional_list), '3003') 321 else: 322 raise OHOSException('ERROR argument "{}": Invalid choice "{}". ' 323 'choose from {}'.format(args_file.get("arg_name"), 324 option, list(support_sub_options.keys())), '3003')