15f9996aaSopenharmony_ci#!/usr/bin/env python
25f9996aaSopenharmony_ci# -*- coding: utf-8 -*-
35f9996aaSopenharmony_ci# Copyright (c) 2021 Huawei Device Co., Ltd.
45f9996aaSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
55f9996aaSopenharmony_ci# you may not use this file except in compliance with the License.
65f9996aaSopenharmony_ci# You may obtain a copy of the License at
75f9996aaSopenharmony_ci#
85f9996aaSopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
95f9996aaSopenharmony_ci#
105f9996aaSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
115f9996aaSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
125f9996aaSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f9996aaSopenharmony_ci# See the License for the specific language governing permissions and
145f9996aaSopenharmony_ci# limitations under the License.
155f9996aaSopenharmony_ci
165f9996aaSopenharmony_ciimport optparse
175f9996aaSopenharmony_ciimport os
185f9996aaSopenharmony_ciimport sys
195f9996aaSopenharmony_ciimport json
205f9996aaSopenharmony_ci
215f9996aaSopenharmony_cifrom zipfile import ZipFile  # noqa: E402
225f9996aaSopenharmony_cifrom util import build_utils  # noqa: E402
235f9996aaSopenharmony_ci
245f9996aaSopenharmony_ci
255f9996aaSopenharmony_cidef parse_args(args):
265f9996aaSopenharmony_ci    args = build_utils.expand_file_args(args)
275f9996aaSopenharmony_ci
285f9996aaSopenharmony_ci    parser = optparse.OptionParser()
295f9996aaSopenharmony_ci    build_utils.add_depfile_option(parser)
305f9996aaSopenharmony_ci    parser.add_option('--output', help='stamp file')
315f9996aaSopenharmony_ci    parser.add_option('--js-assets-dir', help='js assets directory')
325f9996aaSopenharmony_ci    parser.add_option('--ets-assets-dir', help='ets assets directory')
335f9996aaSopenharmony_ci    parser.add_option('--js-forms-dir', help='js forms directory')
345f9996aaSopenharmony_ci    parser.add_option('--testrunner-dir', help='testrunner directory')
355f9996aaSopenharmony_ci    parser.add_option('--nodejs-path', help='path to nodejs app')
365f9996aaSopenharmony_ci    parser.add_option('--webpack-js', help='path to js webpack.js')
375f9996aaSopenharmony_ci    parser.add_option('--webpack-ets', help='path to ets webpack.js')
385f9996aaSopenharmony_ci    parser.add_option('--webpack-config-js', help='path to webpack.config.js')
395f9996aaSopenharmony_ci    parser.add_option('--webpack-config-ets', help='path to webpack.rich.config.js')
405f9996aaSopenharmony_ci    parser.add_option('--hap-profile', help='path to hap profile')
415f9996aaSopenharmony_ci    parser.add_option('--build-mode', help='debug mode or release mode')
425f9996aaSopenharmony_ci    parser.add_option('--js-sources-file', help='path to js sources file')
435f9996aaSopenharmony_ci    parser.add_option('--js2abc',
445f9996aaSopenharmony_ci                      action='store_true',
455f9996aaSopenharmony_ci                      default=False,
465f9996aaSopenharmony_ci                      help='whether to transform js to ark bytecode')
475f9996aaSopenharmony_ci    parser.add_option('--ets2abc',
485f9996aaSopenharmony_ci                      action='store_true',
495f9996aaSopenharmony_ci                      default=False,
505f9996aaSopenharmony_ci                      help='whether to transform ets to ark bytecode')
515f9996aaSopenharmony_ci    parser.add_option('--ark-es2abc-dir', help='path to ark es2abc dir')
525f9996aaSopenharmony_ci    parser.add_option('--ace-loader-home', help='path to ace-loader dir.')
535f9996aaSopenharmony_ci    parser.add_option('--ets-loader-home', help='path to ets-loader dir.')
545f9996aaSopenharmony_ci    parser.add_option('--app-profile', default=False, help='path to app-profile.')
555f9996aaSopenharmony_ci    parser.add_option('--manifest-file-path', help='path to manifest.json dir.')
565f9996aaSopenharmony_ci
575f9996aaSopenharmony_ci    options, _ = parser.parse_args(args)
585f9996aaSopenharmony_ci    options.js_assets_dir = build_utils.parse_gn_list(options.js_assets_dir)
595f9996aaSopenharmony_ci    options.ets_assets_dir = build_utils.parse_gn_list(options.ets_assets_dir)
605f9996aaSopenharmony_ci    options.js_forms_dir = build_utils.parse_gn_list(options.js_forms_dir)
615f9996aaSopenharmony_ci    options.testrunner_dir = build_utils.parse_gn_list(options.testrunner_dir)
625f9996aaSopenharmony_ci    return options
635f9996aaSopenharmony_ci
645f9996aaSopenharmony_ci
655f9996aaSopenharmony_cidef make_my_env(options, js2abc: bool) -> dict:
665f9996aaSopenharmony_ci    out_dir = os.path.abspath(os.path.dirname(options.output))
675f9996aaSopenharmony_ci    gen_dir = os.path.join(out_dir, "gen")
685f9996aaSopenharmony_ci    assets_dir = os.path.join(out_dir, "assets")
695f9996aaSopenharmony_ci    if options.app_profile:
705f9996aaSopenharmony_ci        if js2abc:
715f9996aaSopenharmony_ci            assets_dir = os.path.join(assets_dir, "js")
725f9996aaSopenharmony_ci        else:
735f9996aaSopenharmony_ci            assets_dir = os.path.join(assets_dir, "ets")
745f9996aaSopenharmony_ci    my_env = {
755f9996aaSopenharmony_ci        "aceModuleBuild": assets_dir,
765f9996aaSopenharmony_ci        "buildMode": options.build_mode,
775f9996aaSopenharmony_ci        "PATH": os.environ.get('PATH'),
785f9996aaSopenharmony_ci        "appResource": os.path.join(gen_dir, "ResourceTable.txt")
795f9996aaSopenharmony_ci    }
805f9996aaSopenharmony_ci    if options.app_profile:
815f9996aaSopenharmony_ci        my_env["aceProfilePath"] = os.path.join(gen_dir, "resources/base/profile")
825f9996aaSopenharmony_ci        my_env["aceModuleJsonPath"] = os.path.abspath(options.hap_profile)
835f9996aaSopenharmony_ci    return my_env
845f9996aaSopenharmony_ci
855f9996aaSopenharmony_ci
865f9996aaSopenharmony_cidef make_manifest_data(config: dict, options, js2abc: bool, asset_index: int, assets_cnt: int, src_path: str) -> dict:
875f9996aaSopenharmony_ci    data = dict()
885f9996aaSopenharmony_ci    data['appID'] = config['app']['bundleName']
895f9996aaSopenharmony_ci    if options.app_profile:
905f9996aaSopenharmony_ci        data['versionName'] = config['app']['versionName']
915f9996aaSopenharmony_ci        data['versionCode'] = config['app']['versionCode']
925f9996aaSopenharmony_ci        data['pages'] = config['module']['pages']
935f9996aaSopenharmony_ci        data['deviceType'] = config['module']['deviceTypes']
945f9996aaSopenharmony_ci    else:
955f9996aaSopenharmony_ci        data['appName'] = config['module']['abilities'][asset_index].get('label')
965f9996aaSopenharmony_ci        data['versionName'] = config['app']['version']['name']
975f9996aaSopenharmony_ci        data['versionCode'] = config['app']['version']['code']
985f9996aaSopenharmony_ci        data['deviceType'] = config['module']['deviceType']
995f9996aaSopenharmony_ci        for js_module in config['module']['js']:
1005f9996aaSopenharmony_ci            js_module_name = js_module.get('name').split('.')[-1]
1015f9996aaSopenharmony_ci
1025f9996aaSopenharmony_ci            # According to the page name and ability entry match the corresponding page for ability
1035f9996aaSopenharmony_ci            # Compatibility with mismatches due to "MainAbility" and "default"
1045f9996aaSopenharmony_ci            if js_module_name == src_path or (js_module_name == 'MainAbility' and src_path == 'default') \
1055f9996aaSopenharmony_ci               or (js_module_name == 'default' and src_path == 'MainAbility'):
1065f9996aaSopenharmony_ci                data['pages'] = js_module.get('pages')
1075f9996aaSopenharmony_ci                data['window'] = js_module.get('window')
1085f9996aaSopenharmony_ci                if js_module.get('type') == 'form' and options.js_forms_dir:
1095f9996aaSopenharmony_ci                    data['type'] = 'form'
1105f9996aaSopenharmony_ci            if not js2abc:
1115f9996aaSopenharmony_ci                data['mode'] = js_module.get('mode')
1125f9996aaSopenharmony_ci    return data
1135f9996aaSopenharmony_ci
1145f9996aaSopenharmony_ci
1155f9996aaSopenharmony_cidef build_ace(cmd: str, options, js2abc: bool, loader_home: str, assets_dir: str, assets_name: str):
1165f9996aaSopenharmony_ci    my_env = make_my_env(options, js2abc)
1175f9996aaSopenharmony_ci    gen_dir = my_env.get("aceModuleBuild")
1185f9996aaSopenharmony_ci    assets_cnt = len(assets_dir)
1195f9996aaSopenharmony_ci    use_compile_cache = os.environ.get("USE_COMPILE_CACHE", "false").lower() == "true"
1205f9996aaSopenharmony_ci    for asset_index in range(assets_cnt):
1215f9996aaSopenharmony_ci        ability_dir = os.path.relpath(assets_dir[asset_index], loader_home)
1225f9996aaSopenharmony_ci        my_env["aceModuleRoot"] = ability_dir
1235f9996aaSopenharmony_ci        if options.js_sources_file:
1245f9996aaSopenharmony_ci            with open(options.js_sources_file, 'wb') as js_sources_file:
1255f9996aaSopenharmony_ci                sources = get_all_js_sources(ability_dir)
1265f9996aaSopenharmony_ci                js_sources_file.write('\n'.join(sources).encode())
1275f9996aaSopenharmony_ci        src_path = os.path.basename(assets_dir[asset_index])
1285f9996aaSopenharmony_ci
1295f9996aaSopenharmony_ci        # Create a fixed directory for manifest.json
1305f9996aaSopenharmony_ci        if js2abc:
1315f9996aaSopenharmony_ci            build_dir = os.path.abspath(os.path.join(options.manifest_file_path, 'js', src_path))
1325f9996aaSopenharmony_ci            my_env.update({"cachePath": os.path.join(build_dir, ".cache")})
1335f9996aaSopenharmony_ci        else:
1345f9996aaSopenharmony_ci            build_dir = os.path.abspath(os.path.join(options.manifest_file_path, 'ets', src_path))
1355f9996aaSopenharmony_ci        if not os.path.exists(build_dir):
1365f9996aaSopenharmony_ci            os.makedirs(build_dir, exist_ok=True)
1375f9996aaSopenharmony_ci        manifest = os.path.join(build_dir, 'manifest.json')
1385f9996aaSopenharmony_ci
1395f9996aaSopenharmony_ci        # Determine abilityType according to config.json
1405f9996aaSopenharmony_ci        if assets_name == 'testrunner_dir':
1415f9996aaSopenharmony_ci            my_env["abilityType"] = 'testrunner'
1425f9996aaSopenharmony_ci        elif assets_name != 'js_forms_dir' and not options.app_profile and assets_cnt > 1:
1435f9996aaSopenharmony_ci            with open(options.hap_profile) as profile:
1445f9996aaSopenharmony_ci                config = json.load(profile)
1455f9996aaSopenharmony_ci                if config['module']['abilities'][asset_index].__contains__('forms'):
1465f9996aaSopenharmony_ci                    my_env["abilityType"] = 'form'
1475f9996aaSopenharmony_ci                else:
1485f9996aaSopenharmony_ci                    my_env["abilityType"] = config['module']['abilities'][asset_index]['type']
1495f9996aaSopenharmony_ci        else:
1505f9996aaSopenharmony_ci            my_env["abilityType"] = 'page'
1515f9996aaSopenharmony_ci
1525f9996aaSopenharmony_ci        # Generate manifest.json only when abilityType is page
1535f9996aaSopenharmony_ci        data = dict()
1545f9996aaSopenharmony_ci        if my_env["abilityType"] == 'page':
1555f9996aaSopenharmony_ci            with open(options.hap_profile) as profile:
1565f9996aaSopenharmony_ci                config = json.load(profile)
1575f9996aaSopenharmony_ci                data = make_manifest_data(config, options, js2abc, asset_index, assets_cnt, src_path)
1585f9996aaSopenharmony_ci                build_utils.write_json(data, manifest)
1595f9996aaSopenharmony_ci
1605f9996aaSopenharmony_ci            # If missing page, skip it
1615f9996aaSopenharmony_ci            if not data.__contains__('pages'):
1625f9996aaSopenharmony_ci                print('Warning: There is no page matching this {}'.format(src_path))
1635f9996aaSopenharmony_ci                continue
1645f9996aaSopenharmony_ci
1655f9996aaSopenharmony_ci        if not options.app_profile:
1665f9996aaSopenharmony_ci            my_env["aceManifestPath"] = manifest
1675f9996aaSopenharmony_ci            my_env["aceModuleBuild"] = os.path.join(gen_dir, src_path)
1685f9996aaSopenharmony_ci
1695f9996aaSopenharmony_ci        enable_compile_cache(asset_index, assets_cnt, my_env, use_compile_cache)
1705f9996aaSopenharmony_ci        build_utils.check_output(cmd, cwd=loader_home, env=my_env)
1715f9996aaSopenharmony_ci
1725f9996aaSopenharmony_ci    if options.app_profile:
1735f9996aaSopenharmony_ci        gen_dir = os.path.dirname(gen_dir)
1745f9996aaSopenharmony_ci        build_utils.zip_dir(options.output, gen_dir)
1755f9996aaSopenharmony_ci    else:
1765f9996aaSopenharmony_ci        build_utils.zip_dir(options.output, gen_dir, zip_prefix_path='assets/js/')
1775f9996aaSopenharmony_ci
1785f9996aaSopenharmony_ci
1795f9996aaSopenharmony_cidef enable_compile_cache(asset_index: int, assets_cnt: int, env: dict, use_compile_cache: bool):
1805f9996aaSopenharmony_ci    if use_compile_cache:
1815f9996aaSopenharmony_ci        env["useCompileCache"] = "true"
1825f9996aaSopenharmony_ci        if assets_cnt > 1 and asset_index == assets_cnt - 1:
1835f9996aaSopenharmony_ci            env["addTestRunner"] = "true"
1845f9996aaSopenharmony_ci
1855f9996aaSopenharmony_ci
1865f9996aaSopenharmony_cidef get_all_js_sources(base) -> list:
1875f9996aaSopenharmony_ci    sources = []
1885f9996aaSopenharmony_ci    for root, _, files in os.walk(base):
1895f9996aaSopenharmony_ci        for file in files:
1905f9996aaSopenharmony_ci            if file[-3:] in ('.js', '.ts'):
1915f9996aaSopenharmony_ci                sources.append(os.path.join(root, file))
1925f9996aaSopenharmony_ci
1935f9996aaSopenharmony_ci    return sources
1945f9996aaSopenharmony_ci
1955f9996aaSopenharmony_ci
1965f9996aaSopenharmony_cidef main(args):
1975f9996aaSopenharmony_ci    options = parse_args(args)
1985f9996aaSopenharmony_ci
1995f9996aaSopenharmony_ci    inputs = [
2005f9996aaSopenharmony_ci        options.nodejs_path, options.webpack_js, options.webpack_ets,
2015f9996aaSopenharmony_ci        options.webpack_config_js, options.webpack_config_ets
2025f9996aaSopenharmony_ci    ]
2035f9996aaSopenharmony_ci    depfiles = []
2045f9996aaSopenharmony_ci    if not options.js_assets_dir and not options.ets_assets_dir:
2055f9996aaSopenharmony_ci        with ZipFile(options.output, 'w') as file:
2065f9996aaSopenharmony_ci            return
2075f9996aaSopenharmony_ci
2085f9996aaSopenharmony_ci    if options.ark_es2abc_dir:
2095f9996aaSopenharmony_ci        depfiles.extend(build_utils.get_all_files(options.ark_es2abc_dir))
2105f9996aaSopenharmony_ci
2115f9996aaSopenharmony_ci    depfiles.append(options.webpack_js)
2125f9996aaSopenharmony_ci    depfiles.append(options.webpack_ets)
2135f9996aaSopenharmony_ci    depfiles.append(options.webpack_config_js)
2145f9996aaSopenharmony_ci    depfiles.append(options.webpack_config_ets)
2155f9996aaSopenharmony_ci    depfiles.extend(build_utils.get_all_files(options.ace_loader_home))
2165f9996aaSopenharmony_ci    depfiles.extend(build_utils.get_all_files(options.ets_loader_home))
2175f9996aaSopenharmony_ci
2185f9996aaSopenharmony_ci    node_js = os.path.relpath(options.nodejs_path, options.ace_loader_home)
2195f9996aaSopenharmony_ci    assets_dict = dict()
2205f9996aaSopenharmony_ci    if options.js_assets_dir:
2215f9996aaSopenharmony_ci        assets_dict['js_assets_dir'] = options.js_assets_dir
2225f9996aaSopenharmony_ci    if options.ets_assets_dir:
2235f9996aaSopenharmony_ci        assets_dict['ets_assets_dir'] = options.ets_assets_dir
2245f9996aaSopenharmony_ci    if options.js_forms_dir:
2255f9996aaSopenharmony_ci        assets_dict['js_forms_dir'] = options.js_forms_dir
2265f9996aaSopenharmony_ci    if options.testrunner_dir:
2275f9996aaSopenharmony_ci        assets_dict['testrunner_dir'] = options.testrunner_dir
2285f9996aaSopenharmony_ci
2295f9996aaSopenharmony_ci    for assets_name, assets_dir in assets_dict.items():
2305f9996aaSopenharmony_ci        for asset in assets_dir:
2315f9996aaSopenharmony_ci            depfiles.extend(build_utils.get_all_files(asset))
2325f9996aaSopenharmony_ci        if assets_name == 'ets_assets_dir':
2335f9996aaSopenharmony_ci            js2abc = False
2345f9996aaSopenharmony_ci            loader_home = options.ets_loader_home
2355f9996aaSopenharmony_ci            webpack_config = options.webpack_config_ets
2365f9996aaSopenharmony_ci            webpack_path = options.webpack_ets
2375f9996aaSopenharmony_ci        else:
2385f9996aaSopenharmony_ci            js2abc = True
2395f9996aaSopenharmony_ci            loader_home = options.ace_loader_home
2405f9996aaSopenharmony_ci            webpack_config = options.webpack_config_js
2415f9996aaSopenharmony_ci            webpack_path = options.webpack_js
2425f9996aaSopenharmony_ci        cmd = [
2435f9996aaSopenharmony_ci            node_js,
2445f9996aaSopenharmony_ci            os.path.relpath(webpack_path, loader_home),
2455f9996aaSopenharmony_ci            '--config',
2465f9996aaSopenharmony_ci            os.path.relpath(webpack_config, loader_home)
2475f9996aaSopenharmony_ci        ]
2485f9996aaSopenharmony_ci        ark_es2abc_dir = os.path.relpath(options.ark_es2abc_dir, loader_home)
2495f9996aaSopenharmony_ci        if options.app_profile:
2505f9996aaSopenharmony_ci            cmd.extend(['--env', 'buildMode={}'.format(options.build_mode), 'compilerType=ark',
2515f9996aaSopenharmony_ci                        'arkFrontendDir={}'.format(ark_es2abc_dir), 'nodeJs={}'.format(node_js)])
2525f9996aaSopenharmony_ci        else:
2535f9996aaSopenharmony_ci            cmd.extend(['--env', 'compilerType=ark',
2545f9996aaSopenharmony_ci                        'arkFrontendDir={}'.format(ark_es2abc_dir), 'nodeJs={}'.format(node_js)])
2555f9996aaSopenharmony_ci        build_utils.call_and_write_depfile_if_stale(
2565f9996aaSopenharmony_ci            lambda: build_ace(cmd, options, js2abc, loader_home, assets_dir, assets_name),
2575f9996aaSopenharmony_ci            options,
2585f9996aaSopenharmony_ci            depfile_deps=depfiles,
2595f9996aaSopenharmony_ci            input_paths=depfiles + inputs,
2605f9996aaSopenharmony_ci            input_strings=cmd + [options.build_mode],
2615f9996aaSopenharmony_ci            output_paths=([options.output]),
2625f9996aaSopenharmony_ci            force=False,
2635f9996aaSopenharmony_ci            add_pydeps=False)
2645f9996aaSopenharmony_ci
2655f9996aaSopenharmony_ciif __name__ == '__main__':
2665f9996aaSopenharmony_ci    sys.exit(main(sys.argv[1:]))
267