• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2022 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18from __future__ import print_function
19from datetime import datetime
20from fnmatch import fnmatch
21import errno
22import json
23import os
24import platform
25import subprocess
26import sys
27
28CURRENT_FILENAME = os.path.basename(__file__)
29
30
31def str_of_time_now() -> str:
32    return datetime.now().strftime("%Y-%m-%d-%H-%M-%S-%f")[:-3]
33
34
35def _call(cmd: str):
36    print("# %s" % cmd)
37    return subprocess.call(cmd, shell=True)
38
39
40def _write(filename: str, content: str, mode: str):
41    with open(filename, mode) as f:
42        f.write(content)
43
44
45def call_with_output(cmd: str, file: str):
46    print("# %s" % cmd)
47    host = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
48    while True:
49        try:
50            build_data = host.stdout.readline().decode('utf-8')
51            sys.stdout.flush()
52            print(build_data)
53            _write(file, build_data, "a")
54        except OSError as error:
55            if error == errno.ENOENT:
56                print("no such file")
57            elif error == errno.EPERM:
58                print("permission denied")
59            break
60        if not build_data:
61            break
62    host.wait()
63    return host.returncode
64
65
66class ArkPy:
67    # constants determined by designer of this class
68    NAME_OF_OUT_DIR_OF_FIRST_LEVEL = "out"
69    DELIMITER_BETWEEN_OS_CPU_MODE_FOR_COMMAND = "."
70    DELIMITER_FOR_SECOND_OUT_DIR_NAME = "."
71    GN_TARGET_LOG_FILE_NAME = "build.log"
72    UNITTEST_LOG_FILE_NAME = "unittest.log"
73    TEST262_LOG_FILE_NAME = "test262.log"
74    REGRESS_TEST_LOG_FILE_NAME = "regresstest.log"
75    PREBUILTS_DOWNLOAD_CONFIG_FILE_PATH = \
76        "./arkcompiler/toolchain/build/prebuilts_download/prebuilts_download_config.json"
77    INDENTATION_STRING_PER_LEVEL = "  " # for help message
78    # In ARG_DICT, "flags" and "description" are must-keys for the leaf-dicts in it.
79    # (Future designer need know.)
80    ARG_DICT = {
81        "os_cpu": {
82            "linux_x64": {
83                "flags": ["linux_x64", "x64"],
84                "description":
85                    "Build for arkcompiler target of target-operating-system linux and "
86                    "target-central-processing-unit x64.",
87                "gn_args": ["target_os=\"linux\"", "target_cpu=\"x64\""],
88                "prefix_of_name_of_out_dir_of_second_level": "x64",
89            },
90            "linux_x86": {
91                "flags": ["linux_x86", "x86"],
92                "description":
93                    "Build for arkcompiler target of target-operating-system linux and "
94                    "target-central-processing-unit x86.",
95                "gn_args": ["target_os=\"linux\"", "target_cpu=\"x86\""],
96                "prefix_of_name_of_out_dir_of_second_level": "x86",
97            },
98            "ohos_arm": {
99                "flags": ["ohos_arm", "arm"],
100                "description":
101                    "Build for arkcompiler target of target-operating-system ohos and "
102                    "target-central-processing-unit arm.",
103                "gn_args": ["target_os=\"ohos\"", "target_cpu=\"arm\""],
104                "prefix_of_name_of_out_dir_of_second_level": "arm",
105            },
106            "ohos_arm64": {
107                "flags": ["ohos_arm64", "arm64"],
108                "description":
109                    "Build for arkcompiler target of target-operating-system ohos and "
110                    "target-central-processing-unit arm64.",
111                "gn_args": ["target_os=\"ohos\"", "target_cpu=\"arm64\""],
112                "prefix_of_name_of_out_dir_of_second_level": "arm64",
113            },
114            "android_arm64": {
115                "flags": ["android_arm64"],
116                "description":
117                    "Build for arkcompiler target of target-operating-system android and "
118                    "target-central-processing-unit arm64.",
119                "gn_args": ["target_os=\"android\"", "target_cpu=\"arm64\""],
120                "prefix_of_name_of_out_dir_of_second_level": "android_arm64",
121            },
122            "mingw_x86_64": {
123                "flags": ["mingw_x86_64"],
124                "description":
125                    "Build for arkcompiler target of target-operating-system MinGW(Minimalist GNU on Windows) and "
126                    "target-central-processing-unit x86_64.",
127                "gn_args": ["target_os=\"mingw\"", "target_cpu=\"x86_64\""],
128                "prefix_of_name_of_out_dir_of_second_level": "mingw_x86_64",
129            },
130            "ohos_mipsel": {
131                "flags": ["ohos_mipsel", "mipsel"],
132                "description":
133                    "Build for arkcompiler target of target-operating-system ohos and "
134                    "target-central-processing-unit mipsel(32-bit little-endian mips).",
135                "gn_args": ["target_os=\"ohos\"", "target_cpu=\"mipsel\""],
136                "prefix_of_name_of_out_dir_of_second_level": "mipsel",
137            },
138        },
139        "mode": {
140            "release": {
141                "flags": ["release", "r"],
142                "description": "Build for arkcompiler target(executables and libraries) for distribution.",
143                "gn_args": ["is_debug=false"],
144                "suffix_of_name_of_out_dir_of_second_level": "release",
145            },
146            "debug": {
147                "flags": ["debug", "d"],
148                "description": "Build for arkcompiler target(executables and libraries) for debugging.",
149                "gn_args": ["is_debug=true"],
150                "suffix_of_name_of_out_dir_of_second_level": "debug",
151            },
152        },
153        "target": {
154            "test262": {
155                "flags": ["test262", "test-262", "test_262", "262test", "262-test", "262_test", "262"],
156                "description": "Compile arkcompiler target and run test262 with arkcompiler target.",
157                "gn_targets_depend_on": ["default"],
158            },
159            "unittest": {
160                "flags": ["unittest", "ut"],
161                "description":
162                    "Compile and run unittest of arkcompiler target. "
163                    "Add --gn-args=\"run_with_qemu=true\" to command when running unittest of non-host type with qemu.",
164                "gn_targets_depend_on": ["unittest_packages"],
165            },
166            "workload": {
167                "flags": ["workload", "work-load", "work_load"],
168                "description": "Compile arkcompiler target and run workload with arkcompiler target.",
169                "gn_targets_depend_on": ["default"],
170            },
171            "regresstest": {
172                "flags": ["regresstest", "regress_test", "regress", "testregress", "test_regress"],
173                "description": "Compile arkcompiler target and run regresstest with arkcompiler target.",
174                "gn_targets_depend_on": ["default"],
175            },
176            "gn_target": {
177                "flags": ["<name of target in \"*.gn*\" file>"], # any other flags
178                "description":
179                    "Build for arkcompiler target assigned by user. Targets include group(ets_runtime), "
180                    "ohos_executable(ark_js_vm), ohos_shared_library(libark_jsruntime), "
181                    "ohos_static_library(static_icuuc), ohos_source_set(libark_jsruntime_set), "
182                    "ohos_unittest(EcmaVm_001_Test), action(EcmaVm_001_TestAction) and other target of user-defined "
183                    "template type in \"*.gn*\" file.",
184                "gn_targets_depend_on": [], # not need, depend on deps of itself in "*.gn*" file
185            },
186        },
187        "option": {
188            "clean": {
189                "flags": ["--clean", "-clean"],
190                "description":
191                    "Clean the root-out-dir(x64.release-->out/x64.release) execept for file args.gn. "
192                    "Then exit.",
193            },
194            "clean-continue": {
195                "flags": ["--clean-continue", "-clean-continue"],
196                "description":
197                    "Clean the root-out-dir(x64.release-->out/x64.release) execept for file args.gn. "
198                    "Then continue to build.",
199            },
200            "gn-args": {
201                "flags": ["--gn-args=*", "-gn-args=*"],
202                "description":
203                    "Pass args(*) to gn command. Example: python3 ark.py x64.release "
204                    "--gn-args=\"bool_declared_in_src_gn=true string_declared_in_src_gn=\\\"abcd\\\" "
205                    "list_declared_in_src_gn=[ \\\"element0\\\", \\\"element1\\\" ] print(list_declared_in_src_gn) "
206                    "exec_script(\\\"script_in_src\\\", [ \\\"arg_to_script\\\" ])\"  .",
207            },
208            "keepdepfile": {
209                "flags": ["--keepdepfile", "-keepdepfile"],
210                "description":
211                    "Keep depfile(\"*.o.d\") generated by commands(CXX, CC ...) called by ninja during compilation.",
212            },
213            "verbose": {
214                "flags": ["--verbose", "-verbose"],
215                "description": "Print full commands(CXX, CC, LINK ...) called by ninja during compilation.",
216            },
217        },
218        "help": {
219            "flags": ["help", "--help", "--h", "-help", "-h"],
220            "description": "Show the usage of ark.py.",
221        },
222    }
223
224    # variables which would change with the change of host_os or host_cpu
225    gn_binary_path = ""
226    ninja_binary_path = ""
227
228    # variables which would change with the change of ark.py command
229    has_cleaned = False
230    enable_verbose = False
231    enable_keepdepfile = False
232
233    def get_binaries(self):
234        host_os = sys.platform
235        host_cpu = platform.machine()
236        try:
237            with open(self.PREBUILTS_DOWNLOAD_CONFIG_FILE_PATH) as file_prebuilts_download_config:
238                prebuilts_download_config_dict = json.load(file_prebuilts_download_config)
239                file_prebuilts_download_config.close()
240            for element in prebuilts_download_config_dict[host_os][host_cpu]["copy_config"]:
241                if element["unzip_filename"] == "gn":
242                    self.gn_binary_path = os.path.join(element["unzip_dir"], element["unzip_filename"])
243                elif element["unzip_filename"] == "ninja":
244                    self.ninja_binary_path = os.path.join(element["unzip_dir"], element["unzip_filename"])
245        except Exception as error:
246            print("\nLogic of getting gn binary or ninja binary does not match logic of prebuilts_download." \
247                  "\nCheck func \033[92m{0} of class {1} in file {2}\033[0m against file {3} if the name of this " \
248                  "file had not changed!\n".format(
249                    sys._getframe().f_code.co_name, self.__class__.__name__, CURRENT_FILENAME,
250                    self.PREBUILTS_DOWNLOAD_CONFIG_FILE_PATH))
251            raise error
252        if self.gn_binary_path == "" or self.ninja_binary_path == "":
253            print("\nLogic of prebuilts_download may be wrong." \
254                  "\nCheck \033[92mdata in file {0}\033[0m against func {1} of class {2} in file {3}!\n".format(
255                    self.PREBUILTS_DOWNLOAD_CONFIG_FILE_PATH, sys._getframe().f_code.co_name, self.__class__.__name__,
256                    CURRENT_FILENAME))
257            sys.exit(0)
258        if not os.path.isfile(self.gn_binary_path) or not os.path.isfile(self.ninja_binary_path):
259            print("\nStep for prebuilts_download may be ommited. (\033[92m./prebuilts_download.sh\033[0m)" \
260            "\nCheck \033[92mwhether gn binary and ninja binary are under directory prebuilts\033[0m!\n".format())
261            sys.exit(0)
262        return
263
264    @staticmethod
265    def is_dict_flags_match_arg(dict_to_match: dict, arg_to_match: str) -> bool:
266        for flag in dict_to_match["flags"]:
267            if fnmatch(arg_to_match, flag):
268                return True
269        return False
270
271    def which_dict_flags_match_arg(self, dict_including_dicts_to_match: dict, arg_to_match: str) -> str:
272        for key in dict_including_dicts_to_match.keys():
273            if self.is_dict_flags_match_arg(dict_including_dicts_to_match[key], arg_to_match):
274                return key
275        return ""
276
277    def dict_in_os_cpu_mode_match_arg(self, arg: str) -> [bool, str, str]:
278        os_cpu_part = ""
279        mode_part = ""
280        match_success = True
281        key_to_dict_in_os_cpu_matched_arg = ""
282        key_to_dict_in_mode_matched_arg = ""
283        arg_to_list = arg.split(self.DELIMITER_BETWEEN_OS_CPU_MODE_FOR_COMMAND)
284        if len(arg_to_list) == 1:
285            os_cpu_part = arg_to_list[0]
286            mode_part = "release"
287            key_to_dict_in_os_cpu_matched_arg = self.which_dict_flags_match_arg(self.ARG_DICT["os_cpu"], os_cpu_part)
288            key_to_dict_in_mode_matched_arg = self.which_dict_flags_match_arg(self.ARG_DICT["mode"], mode_part)
289        elif len(arg_to_list) == 2:
290            os_cpu_part = arg_to_list[0]
291            mode_part = arg_to_list[1]
292            key_to_dict_in_os_cpu_matched_arg = self.which_dict_flags_match_arg(self.ARG_DICT["os_cpu"], os_cpu_part)
293            key_to_dict_in_mode_matched_arg = self.which_dict_flags_match_arg(self.ARG_DICT["mode"], mode_part)
294        else:
295            print("\"\033[92m{0}\033[0m\" combined with more than 2 flags is not supported.".format(arg))
296        if (key_to_dict_in_os_cpu_matched_arg == "") | (key_to_dict_in_mode_matched_arg == ""):
297            match_success = False
298        return [match_success, key_to_dict_in_os_cpu_matched_arg, key_to_dict_in_mode_matched_arg]
299
300    def get_help_msg_of_dict(self, dict_in: dict, indentation_str_current: str, indentation_str_per_level: str) -> str:
301        help_msg = "".format()
302        for key in dict_in.keys():
303            if isinstance(dict_in[key], dict):
304                help_msg += "{0}{1}:\n".format(indentation_str_current, key)
305                help_msg += self.get_help_msg_of_dict(
306                    dict_in[key], indentation_str_current + indentation_str_per_level, indentation_str_per_level)
307            elif key == "flags":
308                help_msg += "{0}{1}: \033[92m{2}\033[0m\n".format(indentation_str_current, key, " ".join(dict_in[key]))
309            elif key == "description":
310                help_msg += "{0}{1}: {2}\n".format(indentation_str_current, key, dict_in[key])
311        return help_msg
312
313    def get_help_msg_of_all(self) -> str:
314        help_msg = "".format()
315        # Command template
316        help_msg += "\033[32mCommand template:\033[0m\n{}\n\n".format(
317            "  python3 ark.py \033[92m[os_cpu].[mode] [gn_target] [option]\033[0m\n"
318            "  python3 ark.py \033[92m[os_cpu].[mode] [test262] [none or --aot] " \
319              "[none or --pgo] [none, file or dir] [option]\033[0m\n"
320            "  python3 ark.py \033[92m[os_cpu].[mode] [unittest] [option]\033[0m\n"
321            "  python3 ark.py \033[92m[os_cpu].[mode] [regresstest] [none, file or dir] [option]\033[0m\n")
322        # Command examples
323        help_msg += "\033[32mCommand examples:\033[0m\n{}\n\n".format(
324            "  python3 ark.py \033[92mx64.release\033[0m\n"
325            "  python3 ark.py \033[92mx64.release ets_runtime\033[0m\n"
326            "  python3 ark.py \033[92mx64.release ark_js_vm es2panda\033[0m\n"
327            "  python3 ark.py \033[92mx64.release ark_js_vm es2panda --clean\033[0m\n"
328            "  python3 ark.py \033[92mx64.release test262\033[0m\n"
329            "  python3 ark.py \033[92mx64.release test262 --aot --pgo\033[0m\n"
330            "  python3 ark.py \033[92mx64.release test262 built-ins/Array\033[0m\n"
331            "  python3 ark.py \033[92mx64.release test262 built-ins/Array/name.js\033[0m\n"
332            "  python3 ark.py \033[92mx64.release unittest\033[0m\n"
333            "  python3 ark.py \033[92mx64.release regresstest\033[0m\n"
334            "  python3 ark.py \033[92mx64.release workload\033[0m\n"
335            "  python3 ark.py \033[92mx64.release workload report\033[0m\n"
336            "  python3 ark.py \033[92mx64.release workload report dev\033[0m\n"
337            "  python3 ark.py \033[92mx64.release workload report dev -20\033[0m\n")
338        # Arguments
339        help_msg += "\033[32mArguments:\033[0m\n{}".format(
340            self.get_help_msg_of_dict(
341                self.ARG_DICT, self.INDENTATION_STRING_PER_LEVEL, self.INDENTATION_STRING_PER_LEVEL))
342        return help_msg
343
344    def clean(self, out_path: str):
345        if not os.path.exists(out_path):
346            print("Path \"{}\" does not exist! No need to clean it.".format(out_path))
347        else:
348            print("=== clean start ===")
349            code = _call("{0} clean {1}".format(self.gn_binary_path, out_path))
350            if code != 0:
351                print("=== clean failed! ===\n")
352                sys.exit(code)
353            print("=== clean success! ===\n")
354        return
355
356    def build_for_gn_target(self, out_path: str, gn_args: list, arg_list: list, log_file_name: str):
357        # prepare log file
358        build_log_path = os.path.join(out_path, log_file_name)
359        str_to_build_log = "================================\nbuild_time: {0}\nbuild_target: {1}\n\n".format(
360            str_of_time_now(), " ".join(arg_list))
361        _write(build_log_path, str_to_build_log, "a")
362        # gn command
363        print("=== gn gen start ===")
364        code = call_with_output(
365            "{0} gen {1} --args=\"{2}\"".format(
366                self.gn_binary_path, out_path, " ".join(gn_args).replace("\"", "\\\"")),
367            build_log_path)
368        if code != 0:
369            print("=== gn gen failed! ===\n")
370            sys.exit(code)
371        else:
372            print("=== gn gen success! ===\n")
373        # ninja command
374        # Always add " -d keeprsp" to ninja command to keep response file("*.rsp"), thus we could get shared libraries
375        # of an excutable from its response file.
376        ninja_cmd = \
377        self.ninja_binary_path + \
378        (" -v" if self.enable_verbose else "") + \
379        (" -d keepdepfile" if self.enable_keepdepfile else "") + \
380        " -d keeprsp" + \
381        " -C {}".format(out_path) + \
382        " {}".format(" ".join(arg_list))
383        code = call_with_output(ninja_cmd, build_log_path)
384        if code != 0:
385            print("=== ninja failed! ===\n")
386            sys.exit(code)
387        else:
388            print("=== ninja success! ===\n")
389        return
390
391    def build_for_test262(self, out_path, gn_args: list, arg_list: list, log_file_name: str,
392     aot_mode: bool, run_pgo=False):
393        args_to_test262_cmd = ""
394        if len(arg_list) == 0:
395            args_to_test262_cmd = "--es2021 all"
396        elif len(arg_list) == 1:
397            if ".js" in arg_list[0]:
398                args_to_test262_cmd = "--file test262/data/test_es2021/{}".format(arg_list[0])
399            else:
400                args_to_test262_cmd = "--dir test262/data/test_es2021/{}".format(arg_list[0])
401        else:
402            print("\033[92m\"test262\" not support multiple additional arguments.\033[0m\n".format())
403            sys.exit(0)
404        x64_out_path = ""
405        if any('target_cpu="arm64"' in arg for arg in gn_args):
406            if 'release' in out_path:
407                x64_out_path = 'out/x64.release'
408            if 'debug' in out_path:
409                x64_out_path = 'out/x64.debug'
410            gn_args.append("so_dir_for_qemu=\"../../{0}/common/common/libc/\"".format(out_path))
411            gn_args.append("run_with_qemu=true".format(out_path))
412            if not os.path.exists(x64_out_path):
413                os.makedirs(x64_out_path)
414            self.build_for_gn_target(
415                x64_out_path, ['target_os="linux"', 'target_cpu="x64"', 'is_debug=false'],
416                self.ARG_DICT["target"]["test262"]["gn_targets_depend_on"], log_file_name)
417
418        self.build_for_gn_target(
419            out_path, gn_args, self.ARG_DICT["target"]["test262"]["gn_targets_depend_on"], log_file_name)
420        test262_cmd = self.get_test262_cmd(gn_args, out_path, x64_out_path, aot_mode, run_pgo, args_to_test262_cmd)
421        test262_log_path = os.path.join(out_path, log_file_name)
422        str_to_test262_log = "================================\ntest262_time: {0}\ntest262_target: {1}\n\n".format(
423            str_of_time_now(), args_to_test262_cmd)
424        _write(test262_log_path, str_to_test262_log, "a")
425        if aot_mode:
426            self.generate_lib_ark_builtins_abc(run_pgo, gn_args, out_path, x64_out_path, test262_log_path)
427        print("=== test262 start ===")
428        code = call_with_output(test262_cmd, test262_log_path)
429        if code != 0:
430            print("=== test262 fail! ===\n")
431            sys.exit(code)
432        print("=== test262 success! ===\n")
433        return
434
435    @staticmethod
436    def generate_lib_ark_builtins_abc(run_pgo, gn_args, out_path, x64_out_path, test262_log_path):
437        generate_abc_cmd = ""
438        root_dir = os.path.dirname(os.path.abspath(__file__))
439        if run_pgo:
440            generate_abc_cmd = "{root_dir}/{out_path}/arkcompiler/ets_frontend/es2abc" \
441                        " {root_dir}/arkcompiler/ets_runtime/ecmascript/ts_types/lib_ark_builtins.d.ts" \
442                        " --type-extractor" \
443                        " --type-dts-builtin" \
444                        " --module" \
445                        " --merge-abc" \
446                        " --extension=ts" \
447                        " --output" \
448                        " {root_dir}/arkcompiler/ets_runtime/ecmascript/ts_types/lib_ark_builtins.d.abc"
449            if any('target_cpu="arm64"' in arg for arg in gn_args):
450                generate_abc_cmd = generate_abc_cmd.format(root_dir=root_dir, out_path=x64_out_path)
451            else:
452                generate_abc_cmd = generate_abc_cmd.format(root_dir=root_dir, out_path=out_path)
453        call_with_output(generate_abc_cmd, test262_log_path)
454
455    @staticmethod
456    def get_test262_cmd(gn_args, out_path, x64_out_path, aot_mode, run_pgo, args_to_test262_cmd):
457        if aot_mode:
458            print("running test262 in AotMode\n")
459            if any('target_cpu="arm64"' in arg for arg in gn_args):
460                if run_pgo:
461                    test262_cmd = "cd arkcompiler/ets_frontend && python3 test262/run_test262.py {0} --timeout 180000" \
462                          " --libs-dir ../../{1}/arkcompiler/ets_runtime:../../{1}/thirdparty/icu:" \
463                          "../../{1}/thirdparty/zlib:../../prebuilts/clang/ohos/linux-x86_64/llvm/lib" \
464                          " --ark-arch aarch64" \
465                          " --ark-arch-root=../../{1}/common/common/libc/" \
466                          " --ark-tool=../../{1}/arkcompiler/ets_runtime/ark_js_vm" \
467                          " --ark-aot-tool=../../{1}/arkcompiler/ets_runtime/ark_aot_compiler" \
468                          " --ark-frontend-binary=../../{2}/arkcompiler/ets_frontend/es2abc" \
469                          " --merge-abc-binary=../../{2}/arkcompiler/ets_frontend/merge_abc" \
470                          " --ark-aot" \
471                          " --ark-frontend=es2panda"\
472                          "{3}".format(args_to_test262_cmd, out_path, x64_out_path, " --run-pgo")
473                else:
474                    test262_cmd = "cd arkcompiler/ets_frontend && python3 test262/run_test262.py {0} --timeout 180000" \
475                          " --libs-dir ../../prebuilts/clang/ohos/linux-x86_64/llvm/lib:../../{2}/thirdparty/icu/" \
476                          " --ark-arch aarch64" \
477                          " --ark-arch-root=../../{1}/common/common/libc/" \
478                          " --ark-aot" \
479                          " --ark-aot-tool=../../{2}/arkcompiler/ets_runtime/ark_aot_compiler" \
480                          " --ark-tool=../../{1}/arkcompiler/ets_runtime/ark_js_vm" \
481                          " --ark-frontend-binary=../../{2}/arkcompiler/ets_frontend/es2abc" \
482                          " --merge-abc-binary=../../{2}/arkcompiler/ets_frontend/merge_abc" \
483                          " --ark-frontend=es2panda".format(args_to_test262_cmd, out_path, x64_out_path)
484            else:
485                test262_cmd = "cd arkcompiler/ets_frontend && python3 test262/run_test262.py {0} --timeout 180000" \
486                          " --libs-dir ../../{1}/arkcompiler/ets_runtime:../../{1}/thirdparty/icu" \
487                          ":../../{1}/thirdparty/zlib:../../prebuilts/clang/ohos/linux-x86_64/llvm/lib" \
488                          " --ark-tool=../../{1}/arkcompiler/ets_runtime/ark_js_vm" \
489                          " --ark-aot-tool=../../{1}/arkcompiler/ets_runtime/ark_aot_compiler" \
490                          " --ark-frontend-binary=../../{1}/arkcompiler/ets_frontend/es2abc" \
491                          " --merge-abc-binary=../../{1}/arkcompiler/ets_frontend/merge_abc" \
492                          " --ark-aot" \
493                          " --ark-frontend=es2panda"\
494                          "{2}".format(args_to_test262_cmd, out_path, " --run-pgo" if run_pgo else "")
495        else:
496            print("running test262 in AsmMode\n")
497            test262_cmd = "cd arkcompiler/ets_frontend && python3 test262/run_test262.py {0} --timeout 180000" \
498                      " --libs-dir ../../prebuilts/clang/ohos/linux-x86_64/llvm/lib" \
499                      " --ark-tool=../../{1}/arkcompiler/ets_runtime/ark_js_vm" \
500                      " --ark-frontend-binary=../../{1}/arkcompiler/ets_frontend/es2abc" \
501                      " --merge-abc-binary=../../{1}/arkcompiler/ets_frontend/merge_abc" \
502                      " --ark-frontend=es2panda".format(args_to_test262_cmd, out_path)
503        return test262_cmd
504
505    def build_for_unittest(self, out_path: str, gn_args: list, log_file_name:str):
506        self.build_for_gn_target(
507            out_path, gn_args, self.ARG_DICT["target"]["unittest"]["gn_targets_depend_on"],
508            log_file_name)
509        return
510
511    def build_for_regress_test(self, out_path, gn_args: list, arg_list: list, log_file_name: str):
512        args_to_regress_test_cmd = ""
513        if len(arg_list) == 0:
514            args_to_regress_test_cmd = ""
515        elif len(arg_list) == 1:
516            if ".js" in arg_list[0]:
517                args_to_regress_test_cmd = "--test-file {}".format(arg_list[0])
518            else:
519                args_to_regress_test_cmd = "--test-dir {}".format(arg_list[0])
520        else:
521            print("\033[92m\"regresstest\" not support multiple additional arguments.\033[0m\n".format())
522            sys.exit(0)
523        self.build_for_gn_target(
524            out_path, gn_args, self.ARG_DICT["target"]["regresstest"]["gn_targets_depend_on"], log_file_name)
525        regress_test_cmd = "python3 arkcompiler/ets_runtime/test/regresstest/run_regress_test.py" \
526                      " --ark-tool ./{0}/arkcompiler/ets_runtime/ark_js_vm" \
527                      " --ark-frontend-binary ./{0}/arkcompiler/ets_frontend/es2abc" \
528                      " --LD_LIBRARY_PATH ./{0}/arkcompiler/ets_runtime:./{0}/thirdparty/icu:" \
529                      "./prebuilts/clang/ohos/linux-x86_64/llvm/lib" \
530                      " --out-dir ./{0}/ {1}".format(out_path, args_to_regress_test_cmd)
531        regress_test_log_path = os.path.join(out_path, log_file_name)
532        str_to_test_log = "============\n regresstest_time: {0}\nregresstest_target: {1}\n\n".format(
533            str_of_time_now(), regress_test_cmd)
534        _write(regress_test_log_path, str_to_test_log, "a")
535        print("=== regresstest start ===")
536        code = call_with_output(regress_test_cmd, regress_test_log_path)
537        if code != 0:
538            print("=== regresstest fail! ===\n")
539            sys.exit(code)
540        print("=== regresstest success! ===\n")
541        return
542
543    def build(self, out_path: str, gn_args: list, arg_list: list):
544        if not os.path.exists(out_path):
545            print("# mkdir -p {}".format(out_path))
546            os.makedirs(out_path)
547        if len(arg_list) == 0:
548            self.build_for_gn_target(out_path, gn_args, ["default"], self.GN_TARGET_LOG_FILE_NAME)
549        elif self.is_dict_flags_match_arg(self.ARG_DICT["target"]["workload"], arg_list[0]):
550            self.build_for_workload(arg_list, out_path, gn_args, 'workload.log')
551        elif self.is_dict_flags_match_arg(self.ARG_DICT["target"]["test262"], arg_list[0]):
552            if len(arg_list) >= 2 and arg_list[1] == "--aot":
553                if len(arg_list) >= 3 and arg_list[2] == "--pgo":
554                    self.build_for_test262(out_path, gn_args, arg_list[3:], self.TEST262_LOG_FILE_NAME, True, True)
555                else:
556                    self.build_for_test262(out_path, gn_args, arg_list[2:], self.TEST262_LOG_FILE_NAME, True)
557            else:
558                self.build_for_test262(out_path, gn_args, arg_list[1:], self.TEST262_LOG_FILE_NAME, False)
559        elif self.is_dict_flags_match_arg(self.ARG_DICT["target"]["unittest"], arg_list[0]):
560            if len(arg_list) > 1:
561                print("\033[92m\"unittest\" not support additional arguments.\033[0m\n".format())
562                sys.exit(0)
563            self.build_for_unittest(out_path, gn_args, self.UNITTEST_LOG_FILE_NAME)
564        elif self.is_dict_flags_match_arg(self.ARG_DICT["target"]["regresstest"], arg_list[0]):
565            self.build_for_regress_test(out_path, gn_args, arg_list[1:], self.REGRESS_TEST_LOG_FILE_NAME)
566        else:
567            self.build_for_gn_target(out_path, gn_args, arg_list, self.GN_TARGET_LOG_FILE_NAME)
568        return
569
570    def match_options(self, arg_list: list, out_path: str) -> [list, list]:
571        arg_list_ret = []
572        gn_args_ret = []
573        for arg in arg_list:
574            # match [option][clean] flag
575            if self.is_dict_flags_match_arg(self.ARG_DICT["option"]["clean"], arg):
576                self.clean(out_path)
577                sys.exit(0)
578            # match [option][clean-continue] flag
579            elif self.is_dict_flags_match_arg(self.ARG_DICT["option"]["clean-continue"], arg):
580                if not self.has_cleaned:
581                    self.clean(out_path)
582                    self.has_cleaned = True
583            # match [option][gn-args] flag
584            elif self.is_dict_flags_match_arg(self.ARG_DICT["option"]["gn-args"], arg):
585                gn_args_ret.append(arg[(arg.find("=") + 1):])
586            # match [option][keepdepfile] flag
587            elif self.is_dict_flags_match_arg(self.ARG_DICT["option"]["keepdepfile"], arg):
588                if not self.enable_keepdepfile:
589                    self.enable_keepdepfile = True
590            # match [option][verbose] flag
591            elif self.is_dict_flags_match_arg(self.ARG_DICT["option"]["verbose"], arg):
592                if not self.enable_verbose:
593                    self.enable_verbose = True
594            # make a new list with flag that do not match any flag in [option]
595            else:
596                arg_list_ret.append(arg)
597        return [arg_list_ret, gn_args_ret]
598
599    def build_for_workload(self, arg_list, out_path, gn_args, log_file_name):
600        root_dir = os.path.dirname(os.path.abspath(__file__))
601        report = False
602        tools = 'dev'
603        boundary_value = '-10'
604        if len(arg_list) >= 2 and arg_list[1] == 'report':
605            report = True
606        if len(arg_list) >= 3 and arg_list[2]:
607            tools = arg_list[2]
608        if len(arg_list) >= 4 and arg_list[3]:
609            boundary_value = arg_list[3]
610        self.build_for_gn_target(out_path, gn_args, ["default"], self.GN_TARGET_LOG_FILE_NAME)
611        workload_cmd = "cd arkcompiler/ets_runtime/test/workloadtest/ && python3 work_load.py" \
612          " --code-path {0}" \
613          " --report {1}" \
614          " --tools-type {2}" \
615          " --boundary-value {3}" \
616          .format(root_dir, report, tools, boundary_value)
617        workload_log_path = os.path.join(out_path, log_file_name)
618        str_to_workload_log = "================================\nwokload_time: {0}\nwokload_target: {1}\n\n".format(
619            str_of_time_now(), 'file')
620        _write(workload_log_path, str_to_workload_log, "a")
621        print("=== workload start ===")
622        code = call_with_output(workload_cmd, workload_log_path)
623        if code != 0:
624            print("=== workload fail! ===\n")
625            sys.exit(code)
626        print("=== workload success! ===\n")
627        return
628
629    def start_for_matched_os_cpu_mode(self, os_cpu_key: str, mode_key: str, arg_list: list):
630        # get binary gn and ninja
631        self.get_binaries()
632        # get out_path
633        name_of_out_dir_of_second_level = \
634            self.ARG_DICT["os_cpu"][os_cpu_key]["prefix_of_name_of_out_dir_of_second_level"] + \
635            self.DELIMITER_FOR_SECOND_OUT_DIR_NAME + \
636            self.ARG_DICT["mode"][mode_key]["suffix_of_name_of_out_dir_of_second_level"]
637        out_path = os.path.join(self.NAME_OF_OUT_DIR_OF_FIRST_LEVEL, name_of_out_dir_of_second_level)
638        # match [option] flag
639        [arg_list, gn_args] = self.match_options(arg_list, out_path)
640        # get expression which would be written to args.gn file
641        gn_args.extend(self.ARG_DICT["os_cpu"][os_cpu_key]["gn_args"])
642        gn_args.extend(self.ARG_DICT["mode"][mode_key]["gn_args"])
643        # start to build
644        self.build(out_path, gn_args, arg_list)
645        return
646
647    def __main__(self, arg_list: list):
648        # delete duplicate arg in arg_list
649        arg_list = list(dict.fromkeys(arg_list))
650        # match [help] flag
651        if len(arg_list) == 0 or (
652            True in [self.is_dict_flags_match_arg(self.ARG_DICT["help"], arg) for arg in arg_list]):
653            print(self.get_help_msg_of_all())
654            return
655        # match [[os_cpu].[mode]] flag
656        [match_success, key_to_dict_in_os_cpu, key_to_dict_in_mode] = self.dict_in_os_cpu_mode_match_arg(arg_list[0])
657        if match_success:
658            self.start_for_matched_os_cpu_mode(key_to_dict_in_os_cpu, key_to_dict_in_mode, arg_list[1:])
659        else:
660            print("\033[92mThe command is not supported! Help message shows below.\033[0m\n{}".format(
661                self.get_help_msg_of_all()))
662        return
663
664
665if __name__ == "__main__":
666    ark_py = ArkPy()
667    ark_py.__main__(sys.argv[1:])
668