1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4""" 5Copyright (c) 2021 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17 18Description: Use ark to execute js files 19""" 20 21import argparse 22import os 23import platform 24import json 25import sys 26import signal 27import re 28import fileinput 29import subprocess 30from utils import * 31from config import * 32 33 34def parse_args(): 35 parser = argparse.ArgumentParser() 36 parser.add_argument('--ark-tool', 37 default=DEFAULT_ARK_TOOL, 38 required=False, 39 help="ark's binary tool") 40 parser.add_argument('--ark-aot', action='store_true', 41 required=False, 42 help="Run test262 with aot") 43 parser.add_argument('--ark-aot-tool', 44 default=DEFAULT_ARK_AOT_TOOL, 45 required=False, 46 help="ark's aot tool") 47 parser.add_argument("--libs-dir", 48 default=DEFAULT_LIBS_DIR, 49 required=False, 50 help="The path collection of dependent so has been divided by':'") 51 parser.add_argument("--js-file", 52 required=True, 53 help="js file") 54 parser.add_argument('--ark-frontend', 55 default=DEFAULT_ARK_FRONTEND, 56 required=False, 57 nargs='?', choices=ARK_FRONTEND_LIST, type=str, 58 help="Choose one of them") 59 parser.add_argument('--ark-frontend-binary', 60 default=DEFAULT_ARK_FRONTEND_BINARY, 61 required=False, 62 help="ark frontend conversion binary tool") 63 parser.add_argument('--ark-arch', 64 default=DEFAULT_ARK_ARCH, 65 required=False, 66 nargs='?', choices=ARK_ARCH_LIST, type=str, 67 help="Choose one of them") 68 parser.add_argument('--ark-arch-root', 69 default=DEFAULT_ARK_ARCH, 70 required=False, 71 help="the root path for qemu-aarch64 or qemu-arm") 72 parser.add_argument('--opt-level', 73 default=DEFAULT_OPT_LEVEL, 74 required=False, 75 help="the opt level for es2abc") 76 parser.add_argument('--es2abc-thread-count', 77 default=DEFAULT_ES2ABC_THREAD_COUNT, 78 required=False, 79 help="the thread count for es2abc") 80 parser.add_argument('--merge-abc-binary', 81 default=DEFAULT_MERGE_ABC_BINARY, 82 required=False, 83 help="frontend merge abc binary tool") 84 parser.add_argument('--merge-abc-mode', 85 default=DEFAULT_MERGE_ABC_MODE, 86 required=False, 87 help="run test for merge abc mode") 88 parser.add_argument('--product-name', 89 default=DEFAULT_PRODUCT_NAME, 90 required=False, 91 help="ark's product name") 92 arguments = parser.parse_args() 93 return arguments 94 95 96ICU_PATH = f"--icu-data-path={CODE_ROOT}/third_party/icu/ohos_icu4j/data" 97if platform.system() == "Windows" : 98 ICU_PATH = ICU_PATH.replace("/","\\") 99ARK_TOOL = DEFAULT_ARK_TOOL 100LIBS_DIR = DEFAULT_LIBS_DIR 101ARK_AOT_TOOL = DEFAULT_ARK_AOT_TOOL 102ARK_FRONTEND = DEFAULT_ARK_FRONTEND 103ARK_FRONTEND_BINARY = DEFAULT_ARK_FRONTEND_BINARY 104ARK_ARCH = DEFAULT_ARK_ARCH 105PROTO_BIN_SUFFIX = "protoBin" 106 107 108def output(retcode, msg): 109 if retcode == 0: 110 if msg != '': 111 print(str(msg)) 112 elif retcode == -6: 113 sys.stderr.write("Aborted (core dumped)") 114 elif retcode == -4: 115 sys.stderr.write("Aborted (core dumped)") 116 elif retcode == -11: 117 sys.stderr.write("Segmentation fault (core dumped)") 118 elif msg != '': 119 sys.stderr.write(str(msg)) 120 else: 121 sys.stderr.write("Unknown Error: " + str(retcode)) 122 123 124def exec_command(cmd_args, timeout=DEFAULT_TIMEOUT): 125 proc = subprocess.Popen(cmd_args, 126 stderr=subprocess.PIPE, 127 stdout=subprocess.PIPE, 128 close_fds=True, 129 start_new_session=True) 130 cmd_string = " ".join(cmd_args) 131 code_format = 'utf-8' 132 if platform.system() == "Windows": 133 code_format = 'gbk' 134 135 try: 136 (output_res, errs) = proc.communicate(timeout=timeout) 137 ret_code = proc.poll() 138 139 if errs.decode(code_format, 'ignore') != '': 140 output(1, errs.decode(code_format, 'ignore')) 141 return 1 142 143 if ret_code and ret_code != 1: 144 code = ret_code 145 msg = f"Command {cmd_string}: \n" 146 msg += f"error: {str(errs.decode(code_format,'ignore'))}" 147 else: 148 code = 0 149 msg = str(output_res.decode(code_format, 'ignore')) 150 151 except subprocess.TimeoutExpired: 152 proc.kill() 153 proc.terminate() 154 os.kill(proc.pid, signal.SIGTERM) 155 code = 1 156 msg = f"Timeout:'{cmd_string}' timed out after' {str(timeout)} seconds" 157 except Exception as err: 158 code = 1 159 msg = f"{cmd_string}: unknown error: {str(err)}" 160 output(code, msg) 161 return code 162 163def print_command(cmd_args): 164 sys.stderr.write("\n") 165 sys.stderr.write(" ".join(cmd_args)) 166 sys.stderr.write("\n") 167 168# for debug use, to keep aot file 169def run_command(cmd_args): 170 return subprocess.run(" ".join(cmd_args)) 171 172class ArkProgram(): 173 def __init__(self, args): 174 self.args = args 175 self.ark_tool = ARK_TOOL 176 self.ark_aot = False 177 self.ark_aot_tool = ARK_AOT_TOOL 178 self.libs_dir = LIBS_DIR 179 self.ark_frontend = ARK_FRONTEND 180 self.ark_frontend_binary = ARK_FRONTEND_BINARY 181 self.module_list = [] 182 self.dynamicImport_list = [] 183 self.js_file = "" 184 self.module = False 185 self.abc_file = "" 186 self.arch = ARK_ARCH 187 self.arch_root = "" 188 self.opt_level = DEFAULT_OPT_LEVEL 189 self.es2abc_thread_count = DEFAULT_ES2ABC_THREAD_COUNT 190 self.merge_abc_binary = DEFAULT_MERGE_ABC_BINARY 191 self.merge_abc_mode = DEFAULT_MERGE_ABC_MODE 192 193 def proce_parameters(self): 194 if self.args.ark_tool: 195 self.ark_tool = self.args.ark_tool 196 197 if self.args.ark_aot: 198 self.ark_aot = self.args.ark_aot 199 200 if self.args.ark_aot_tool: 201 self.ark_aot_tool = self.args.ark_aot_tool 202 203 if self.args.ark_frontend_binary: 204 self.ark_frontend_binary = self.args.ark_frontend_binary 205 206 if self.args.libs_dir: 207 self.libs_dir = self.args.libs_dir 208 209 if self.args.ark_frontend: 210 self.ark_frontend = self.args.ark_frontend 211 212 if self.args.opt_level: 213 self.opt_level = self.args.opt_level 214 215 if self.args.es2abc_thread_count: 216 self.es2abc_thread_count = self.args.es2abc_thread_count 217 218 if self.args.merge_abc_binary: 219 self.merge_abc_binary = self.args.merge_abc_binary 220 221 if self.args.merge_abc_mode: 222 self.merge_abc_mode = self.args.merge_abc_mode 223 224 self.module_list = MODULE_LIST 225 226 self.dynamicImport_list = DYNAMIC_IMPORT_LIST 227 228 self.js_file = self.args.js_file 229 230 self.arch = self.args.ark_arch 231 232 self.arch_root = self.args.ark_arch_root 233 234 def check_compile_mode(self, file): 235 with open(file, 'r', encoding='utf-8') as check_file: 236 content_file = check_file.read() 237 module_pattern = '((?:export|import)\s+(?:{[\s\S]+}|\*))|' 238 module_pattern += '(export\s+(?:let|const|var|function|class|default))|' 239 module_pattern += '(import\s+[\'\"].+[\'\"])' 240 module_mode_list = re.findall(module_pattern, content_file) 241 242 for module_mode in list(set(module_mode_list)): 243 if len(module_mode[0]) != 0 or len(module_mode[1]) != 0 or \ 244 len(module_mode[2]) != 0: 245 return True 246 247 if "flags: [module]" in content_file or "/language/module-code/" in self.js_file: 248 return True 249 250 return False 251 252 def get_all_skip_force_gc_tests(self): 253 SKIP_FORCE_GC_LIST_FILES.append(TS2ABC_SKIP_FORCE_GC_LIST_FILE) 254 255 for file in SKIP_FORCE_GC_LIST_FILES: 256 with open(file) as jsonfile: 257 json_data = json.load(jsonfile) 258 for key in json_data: 259 FORCE_GC_SKIP_TESTS.extend(key["files"]) 260 261 def gen_dependency_proto(self, dependency): 262 cmd_args = [] 263 output_file = os.path.splitext(dependency.replace(DATA_DIR, BASE_OUT_DIR))[0] 264 output_abc = f"{output_file}.abc" 265 frontend_tool = self.ark_frontend_binary 266 merge_abc_binary = self.args.merge_abc_binary 267 merge_abc_mode = self.merge_abc_mode 268 compile_as_module = self.check_compile_mode(dependency) 269 270 if self.ark_frontend == ARK_FRONTEND_LIST[0]: 271 if merge_abc_mode != "0": 272 cmd_args = ['node', '--expose-gc', frontend_tool, dependency, 273 '--output-proto', '--merge-abc'] 274 else: 275 # for testing no-record-name abc 276 cmd_args = ['node', '--expose-gc', frontend_tool, dependency, 277 '-o', output_abc] 278 if compile_as_module: 279 mod_opt_index = 6 280 cmd_args.insert(mod_opt_index, "--modules") 281 elif self.ark_frontend == ARK_FRONTEND_LIST[1]: 282 if merge_abc_mode != "0": 283 proto_bin_file = output_file + "." + PROTO_BIN_SUFFIX 284 cmd_args = [frontend_tool, dependency, '--outputProto', 285 proto_bin_file, '--merge-abc'] 286 else: 287 # for testing no-record-name abc 288 cmd_args = [frontend_tool, dependency, '--output', output_abc] 289 if compile_as_module: 290 mod_opt_index = 4 291 cmd_args.insert(mod_opt_index, "--module") 292 proc = subprocess.Popen(cmd_args) 293 proc.wait() 294 295 def gen_apart_abc(self, dependencies): 296 merge_abc_binary = self.args.merge_abc_binary 297 retcode = 0 298 for dependency in list(set(dependencies)): 299 cmd_args = [] 300 output_file = os.path.splitext(dependency.replace(DATA_DIR, BASE_OUT_DIR))[0] 301 output_abc = os.path.basename(f"{output_file}.abc") 302 file_dir = os.path.split(self.js_file)[0] 303 is_apart_abc_existed = os.path.exists(file_dir + "/" + output_abc) 304 dependency_file_prefix = os.path.basename(dependency)[:-3] 305 dependency_bin_file = '%s/%s.%s' % (file_dir, 306 dependency_file_prefix, PROTO_BIN_SUFFIX) 307 cmd_args = [merge_abc_binary, '--input', dependency_bin_file, 308 '--suffix', PROTO_BIN_SUFFIX, '--outputFilePath', 309 file_dir, '--output', output_abc] 310 if not is_apart_abc_existed: 311 retcode = exec_command(cmd_args) 312 return retcode 313 314 def gen_merged_abc(self, dependencies, file_name_pre, proto_bin_file): 315 merge_abc_binary = self.args.merge_abc_binary 316 file_dir = os.path.split(self.js_file)[0] 317 proto_abc_file = ".".join([os.path.splitext(os.path.basename(self.js_file))[0], "abc"]) 318 generate_merged_abc = True 319 # collect protoBin file into new-made testcase dir 320 if (len(dependencies) != 0): 321 if os.path.exists(file_name_pre): 322 subprocess.run(['rm', '-rf', file_name_pre]) 323 subprocess.run(['mkdir', file_name_pre]) 324 325 for dependency in list(set(dependencies)): 326 dependency_file_prefix = os.path.basename(dependency)[:-3] 327 dependency_bin_file = '%s/%s.%s' % (file_dir, 328 dependency_file_prefix, PROTO_BIN_SUFFIX) 329 # test262 report syntax error cases 330 if not os.path.exists(dependency_bin_file): 331 generate_merged_abc = False 332 else: 333 subprocess.run(['cp', dependency_bin_file, file_name_pre]) 334 335 if not os.path.exists(proto_bin_file): 336 generate_merged_abc = False 337 else: 338 subprocess.run(['cp', proto_bin_file, file_name_pre]) 339 340 if (len(dependencies) != 0) and generate_merged_abc: 341 # module test262 cases 342 cmd_args = [merge_abc_binary, '--input', file_name_pre, 343 '--suffix', PROTO_BIN_SUFFIX, '--outputFilePath', 344 file_dir, '--output', proto_abc_file] 345 self.abc_file = f'{file_name_pre}.abc' 346 return exec_command(cmd_args) 347 elif os.path.exists(proto_bin_file): 348 cmd_args = [merge_abc_binary, '--input', proto_bin_file, 349 '--suffix', PROTO_BIN_SUFFIX, '--outputFilePath', 350 file_dir, '--output', proto_abc_file] 351 self.abc_file = f'{file_name_pre}.abc' 352 return exec_command(cmd_args) 353 return 0 354 355 def gen_abc(self): 356 js_file = self.js_file 357 file_name_pre = os.path.splitext(js_file)[0] 358 file_name = os.path.basename(js_file) 359 file_dir = os.path.split(js_file)[0] 360 out_file = f"{file_name_pre}.abc" 361 out_proto = f"{file_name_pre}.proto" 362 proto_bin_file = file_name_pre + "." + PROTO_BIN_SUFFIX 363 self.abc_file = out_file 364 mod_opt_index = 0 365 compile_as_module = False 366 cmd_args = [] 367 dependency_cmd_args = [] 368 frontend_tool = self.ark_frontend_binary 369 merge_abc_mode = self.merge_abc_mode 370 dependencies = [] 371 merge_abc_binary = self.args.merge_abc_binary 372 retcode = 0 373 374 # pre-generate the dependencies' abc when ark_frontend is [es2panda] 375 if (file_name in self.module_list or file_name in self.dynamicImport_list): 376 if ("dynamic-import" in js_file): 377 search_dir = os.path.dirname(js_file) 378 else: 379 search_dir = os.path.dirname(js_file.replace(BASE_OUT_DIR, DATA_DIR)) 380 dependencies = collect_module_dependencies(js_file, search_dir, []) 381 compile_as_module = self.check_compile_mode(js_file) 382 if (self.ark_frontend == ARK_FRONTEND_LIST[1]): 383 if list(set(dependencies)): 384 for dependency in list(set(dependencies)): 385 dependency_file = os.path.basename(dependency) 386 dependency_name = os.path.splitext(dependency_file)[0] 387 out_dependency_pre = '%s/%s' % (file_dir, dependency_name) 388 out_dependency_proto = f"{out_dependency_pre}.protoBin" 389 is_dependency_proto_existed = os.path.exists(out_dependency_proto) 390 if not is_dependency_proto_existed: 391 self.gen_dependency_proto(dependency) 392 393 if self.ark_frontend == ARK_FRONTEND_LIST[0]: 394 mod_opt_index = 3 395 if merge_abc_mode != "0": 396 cmd_args = ['node', '--expose-gc', frontend_tool, js_file, 397 '--output-proto', '--merge-abc'] 398 else: 399 # for testing no-record-name abc 400 cmd_args = ['node', '--expose-gc', frontend_tool, js_file, 401 '-o', out_file] 402 if compile_as_module: 403 cmd_args.insert(mod_opt_index, "-m") 404 self.module = True 405 elif self.ark_frontend == ARK_FRONTEND_LIST[1]: 406 mod_opt_index = 1 407 if merge_abc_mode != "0": 408 # '--merge-abc' is added due to 'merge-abc' is not opened as default in es2abc, should be removed later 409 cmd_args = [frontend_tool, '--function-threads=' + 410 str(self.es2abc_thread_count), '--outputProto', 411 proto_bin_file, js_file, '--merge-abc', '--opt-level=' + str(self.opt_level)] 412 else: 413 # for testing no-record-name abc 414 cmd_args = [frontend_tool, '--opt-level=' + str(self.opt_level), 415 '--function-threads=' + 416 str(self.es2abc_thread_count), '--output', 417 out_file, js_file] 418 if compile_as_module: 419 cmd_args.insert(mod_opt_index, "--module") 420 self.module = True 421 # get abc file list from import statement 422 if merge_abc_mode == "0" and self.ark_aot and self.module: 423 self.abc_file = os.path.abspath(out_file) 424 js_dir = os.path.dirname(js_file) 425 for line in fileinput.input(js_file): 426 import_line = re.findall(r"^(?:ex|im)port.*\.js", line) 427 if len(import_line): 428 import_file = re.findall(r"['\"].*\.js", import_line[0]) 429 if len(import_file): 430 abc_file = import_file[0][1:].replace(".js", ".abc") 431 abc_file = os.path.abspath(f'{js_dir}/{abc_file}') 432 if self.abc_file.find(abc_file) < 0: 433 self.abc_file += f':{abc_file}' 434 435 if not os.path.exists(out_proto): 436 retcode = exec_command(cmd_args) 437 if retcode == 1: 438 return retcode 439 self.abc_cmd = cmd_args 440 441 if self.ark_frontend == ARK_FRONTEND_LIST[1]: 442 if ("dynamic-import" in js_file and not os.path.exists(out_file)): 443 file_dir = os.path.split(self.js_file)[0] 444 proto_abc_file = ".".join([os.path.splitext(os.path.basename(self.js_file))[0], "abc"]) 445 cmd_args = [merge_abc_binary, '--input', proto_bin_file, 446 '--suffix', PROTO_BIN_SUFFIX, '--outputFilePath', 447 file_dir, '--output', proto_abc_file] 448 retcode = exec_command(cmd_args) 449 if retcode == 1: 450 return retcode 451 self.abc_cmd = cmd_args 452 453 if merge_abc_mode != "0": 454 if "dynamic-import" in js_file: 455 return self.gen_apart_abc(dependencies) 456 else: 457 return self.gen_merged_abc(dependencies, file_name_pre, proto_bin_file) 458 459 return retcode 460 461 def compile_aot(self): 462 os.environ["LD_LIBRARY_PATH"] = self.libs_dir 463 file_name_pre = os.path.splitext(self.js_file)[0] 464 cmd_args = [] 465 if self.arch == ARK_ARCH_LIST[1]: 466 cmd_args = [self.ark_aot_tool, ICU_PATH, 467 f'--compiler-target-triple=aarch64-unknown-linux-gnu', 468 f'--aot-file={file_name_pre}', 469 self.abc_file] 470 elif self.arch == ARK_ARCH_LIST[2]: 471 cmd_args = [self.ark_aot_tool, ICU_PATH, 472 f'--compiler-target-triple=arm-unknown-linux-gnu', 473 f'--aot-file={file_name_pre}', 474 self.abc_file] 475 elif self.arch == ARK_ARCH_LIST[0]: 476 cmd_args = [self.ark_aot_tool, ICU_PATH, 477 f'--aot-file={file_name_pre}', 478 self.abc_file] 479 retcode = exec_command(cmd_args, 180000) 480 if retcode: 481 print_command(self.abc_cmd) 482 print_command(cmd_args) 483 484 def execute_aot(self): 485 unforce_gc = False 486 os.environ["LD_LIBRARY_PATH"] = self.libs_dir 487 file_name_pre = os.path.splitext(self.js_file)[0] 488 cmd_args = [] 489 if self.arch == ARK_ARCH_LIST[1]: 490 qemu_tool = "qemu-aarch64" 491 qemu_arg1 = "-L" 492 qemu_arg2 = self.arch_root 493 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 494 ICU_PATH, 495 f'--aot-file={file_name_pre}', 496 f'{file_name_pre}.abc'] 497 elif self.arch == ARK_ARCH_LIST[2]: 498 qemu_tool = "qemu-arm" 499 qemu_arg1 = "-L" 500 qemu_arg2 = self.arch_root 501 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 502 ICU_PATH, 503 f'--aot-file={file_name_pre}', 504 f'{file_name_pre}.abc'] 505 elif self.arch == ARK_ARCH_LIST[0]: 506 if file_name_pre in FORCE_GC_SKIP_TESTS: 507 unforce_gc = True 508 asm_arg1 = "--enable-force-gc=true" 509 if unforce_gc: 510 asm_arg1 = "--enable-force-gc=false" 511 cmd_args = [self.ark_tool, ICU_PATH, asm_arg1, 512 f'--aot-file={file_name_pre}', 513 f'{file_name_pre}.abc'] 514 515 record_name = os.path.splitext(os.path.split(self.js_file)[1])[0] 516 cmd_args.insert(-1, f'--entry-point={record_name}') 517 retcode = exec_command(cmd_args) 518 if retcode: 519 print_command(cmd_args) 520 return retcode 521 522 def execute(self): 523 unforce_gc = False 524 if platform.system() == "Windows" : 525 #add env path for cmd/powershell execute 526 libs_dir = self.libs_dir.replace(":", ";") 527 libs_dir = libs_dir.replace("/", "\\") 528 os.environ["PATH"] = libs_dir + ";" + os.environ["PATH"] 529 elif platform.system() == "Linux" : 530 os.environ["LD_LIBRARY_PATH"] = self.libs_dir 531 else : 532 sys.exit(f" test262 on {platform.system()} not supported") 533 file_name_pre = os.path.splitext(self.js_file)[0] 534 # In the case of Windows, it is necessary to convert ' \\' to '/', otherwise there will be a crash or the file cannot be found 535 # Maintain consistent interface path with DevEco Studio 536 if platform.system() == "Windows": 537 file_name_pre = file_name_pre.replace("\\","/") 538 cmd_args = [] 539 if self.arch == ARK_ARCH_LIST[1]: 540 qemu_tool = "qemu-aarch64" 541 qemu_arg1 = "-L" 542 qemu_arg2 = self.arch_root 543 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 544 ICU_PATH, 545 f'{file_name_pre}.abc'] 546 elif self.arch == ARK_ARCH_LIST[2]: 547 qemu_tool = "qemu-arm" 548 qemu_arg1 = "-L" 549 qemu_arg2 = self.arch_root 550 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 551 ICU_PATH, 552 f'{file_name_pre}.abc'] 553 elif self.arch == ARK_ARCH_LIST[0]: 554 if file_name_pre in FORCE_GC_SKIP_TESTS: 555 unforce_gc = True 556 asm_arg1 = "--enable-force-gc=true" 557 if unforce_gc: 558 asm_arg1 = "--enable-force-gc=false" 559 cmd_args = [self.ark_tool, ICU_PATH, asm_arg1, 560 f'{file_name_pre}.abc'] 561 562 record_name = os.path.splitext(os.path.split(self.js_file)[1])[0] 563 cmd_args.insert(-1, f'--entry-point={record_name}') 564 retcode = exec_command(cmd_args) 565 if retcode: 566 print_command(cmd_args) 567 return retcode 568 569 def is_legal_frontend(self): 570 if self.ark_frontend not in ARK_FRONTEND_LIST: 571 sys.stderr.write("Wrong ark front-end option") 572 return False 573 return True 574 575 def execute_ark(self): 576 self.proce_parameters() 577 self.get_all_skip_force_gc_tests() 578 if not self.is_legal_frontend(): 579 return 580 if self.gen_abc(): 581 return 582 if self.ark_aot: 583 self.compile_aot() 584 self.execute_aot() 585 else: 586 self.execute() 587 588def main(): 589 args = parse_args() 590 591 ark = ArkProgram(args) 592 ark.execute_ark() 593 594 595if __name__ == "__main__": 596 sys.exit(main()) 597