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 subprocess
185f9996aaSopenharmony_ciimport sys
195f9996aaSopenharmony_ciimport shutil
205f9996aaSopenharmony_ciimport os
215f9996aaSopenharmony_ciimport tempfile
225f9996aaSopenharmony_ciimport json
235f9996aaSopenharmony_cifrom util import build_utils  # noqa: E402
245f9996aaSopenharmony_ci
255f9996aaSopenharmony_ci
265f9996aaSopenharmony_cidef sign_hap(options, unsigned_hap_path: str, signed_hap_path: str):
275f9996aaSopenharmony_ci    cmd = ['python3', options.sign_hap_py_path]
285f9996aaSopenharmony_ci    cmd.extend(['--hapsigner', options.hapsigner])
295f9996aaSopenharmony_ci    cmd.extend(['--sign-algo', options.sign_algo])
305f9996aaSopenharmony_ci    cmd.extend(['--keyalias', options.keyalias])
315f9996aaSopenharmony_ci    cmd.extend(['--inFile', unsigned_hap_path])
325f9996aaSopenharmony_ci    cmd.extend(['--outFile', signed_hap_path])
335f9996aaSopenharmony_ci    cmd.extend(['--profileFile', options.certificate_profile])
345f9996aaSopenharmony_ci    cmd.extend(['--keystoreFile', options.keystore_path])
355f9996aaSopenharmony_ci    cmd.extend(['--keystorePwd', options.keystorepasswd])
365f9996aaSopenharmony_ci    cmd.extend(['--keyPwd', options.private_key_path])
375f9996aaSopenharmony_ci    cmd.extend(['--certificate-file', options.certificate_file])
385f9996aaSopenharmony_ci    cmd.extend(['--profileSigned', '1'])
395f9996aaSopenharmony_ci    cmd.extend(['--inForm', 'zip'])
405f9996aaSopenharmony_ci    cmd.extend(['--compatible_version', options.sign_compatible_version])
415f9996aaSopenharmony_ci    child = subprocess.Popen(cmd,
425f9996aaSopenharmony_ci                             stdout=subprocess.PIPE,
435f9996aaSopenharmony_ci                             stderr=subprocess.PIPE)
445f9996aaSopenharmony_ci    stdout, stderr = child.communicate()
455f9996aaSopenharmony_ci    if child.returncode:
465f9996aaSopenharmony_ci        print(stdout.decode(), stderr.decode())
475f9996aaSopenharmony_ci        raise Exception("Failed to sign hap {}.".format(options.sign_hap_py_path))
485f9996aaSopenharmony_ci
495f9996aaSopenharmony_ci
505f9996aaSopenharmony_cidef add_resources(packaged_resources: list, package_dir: str, packing_cmd: list):
515f9996aaSopenharmony_ci    if packaged_resources:
525f9996aaSopenharmony_ci        build_utils.extract_all(packaged_resources,
535f9996aaSopenharmony_ci                                package_dir,
545f9996aaSopenharmony_ci                                no_clobber=False)
555f9996aaSopenharmony_ci        index_file_path = os.path.join(package_dir, 'resources.index')
565f9996aaSopenharmony_ci        if os.path.exists(index_file_path):
575f9996aaSopenharmony_ci            packing_cmd.extend(['--index-path', index_file_path])
585f9996aaSopenharmony_ci            resources_path = os.path.join(package_dir, 'resources')
595f9996aaSopenharmony_ci            if os.path.exists(resources_path):
605f9996aaSopenharmony_ci                packing_cmd.extend(['--resources-path', resources_path])
615f9996aaSopenharmony_ci
625f9996aaSopenharmony_ci
635f9996aaSopenharmony_cidef add_assets(options, package_dir: str, packing_cmd: list):
645f9996aaSopenharmony_ci    packaged_js_assets, assets = options.packaged_js_assets, options.assets
655f9996aaSopenharmony_ci    if options.app_profile:
665f9996aaSopenharmony_ci        assets_dir = os.path.join(package_dir, 'ets')
675f9996aaSopenharmony_ci        js_assets_dir = os.path.join(package_dir, 'js')
685f9996aaSopenharmony_ci    else:
695f9996aaSopenharmony_ci        assets_dir = os.path.join(package_dir, 'assets')
705f9996aaSopenharmony_ci
715f9996aaSopenharmony_ci    if packaged_js_assets:
725f9996aaSopenharmony_ci        build_utils.extract_all(packaged_js_assets,
735f9996aaSopenharmony_ci                                package_dir,
745f9996aaSopenharmony_ci                                no_clobber=False)
755f9996aaSopenharmony_ci    if options.build_mode == "release":
765f9996aaSopenharmony_ci        for root, _, files in os.walk(assets_dir):
775f9996aaSopenharmony_ci            for f in files:
785f9996aaSopenharmony_ci                filename = os.path.join(root, f)
795f9996aaSopenharmony_ci                if filename.endswith('.js.map'):
805f9996aaSopenharmony_ci                    os.unlink(filename)
815f9996aaSopenharmony_ci    if assets:
825f9996aaSopenharmony_ci        if not os.path.exists(assets_dir):
835f9996aaSopenharmony_ci            os.mkdir(assets_dir)
845f9996aaSopenharmony_ci        for item in assets:
855f9996aaSopenharmony_ci            if os.path.isfile(item):
865f9996aaSopenharmony_ci                shutil.copyfile(
875f9996aaSopenharmony_ci                    item, os.path.join(assets_dir, os.path.basename(item)))
885f9996aaSopenharmony_ci            elif os.path.isdir(item):
895f9996aaSopenharmony_ci                shutil.copytree(
905f9996aaSopenharmony_ci                    item, os.path.join(assets_dir, os.path.basename(item)))
915f9996aaSopenharmony_ci    if os.path.exists(assets_dir) and len(os.listdir(assets_dir)) != 0:
925f9996aaSopenharmony_ci        if options.app_profile:
935f9996aaSopenharmony_ci            packing_cmd.extend(['--ets-path', assets_dir])
945f9996aaSopenharmony_ci        else:
955f9996aaSopenharmony_ci            packing_cmd.extend(['--assets-path', assets_dir])
965f9996aaSopenharmony_ci    if options.app_profile:
975f9996aaSopenharmony_ci        if os.path.exists(js_assets_dir) and len(os.listdir(js_assets_dir)) != 0:
985f9996aaSopenharmony_ci            packing_cmd.extend(['--js-path', js_assets_dir])
995f9996aaSopenharmony_ci
1005f9996aaSopenharmony_ci
1015f9996aaSopenharmony_cidef get_ark_toolchain_version(options):
1025f9996aaSopenharmony_ci    cmd = [options.nodejs_path, options.js2abc_js, '--bc-version']
1035f9996aaSopenharmony_ci    return build_utils.check_output(cmd).strip('\n')
1045f9996aaSopenharmony_ci
1055f9996aaSopenharmony_ci
1065f9996aaSopenharmony_cidef tweak_hap_profile(options, package_dir: str):
1075f9996aaSopenharmony_ci    config_name = 'config.json'
1085f9996aaSopenharmony_ci    if options.app_profile:
1095f9996aaSopenharmony_ci        config_name = 'module.json'
1105f9996aaSopenharmony_ci    hap_profile = os.path.join(package_dir, config_name)
1115f9996aaSopenharmony_ci    if not os.path.exists(hap_profile):
1125f9996aaSopenharmony_ci        raise Exception('Error: {} of hap file not exists'.format(config_name))
1135f9996aaSopenharmony_ci    config = {}
1145f9996aaSopenharmony_ci    with open(hap_profile, 'r') as fileobj:
1155f9996aaSopenharmony_ci        config = json.load(fileobj)
1165f9996aaSopenharmony_ci        if options.app_profile:
1175f9996aaSopenharmony_ci            config.get('module')['virtualMachine'] = 'ark{}'.format(
1185f9996aaSopenharmony_ci                get_ark_toolchain_version(options))
1195f9996aaSopenharmony_ci        else:
1205f9996aaSopenharmony_ci            config.get('module').get('distro')['virtualMachine'] = 'ark{}'.format(
1215f9996aaSopenharmony_ci                get_ark_toolchain_version(options))
1225f9996aaSopenharmony_ci    build_utils.write_json(config, hap_profile)
1235f9996aaSopenharmony_ci
1245f9996aaSopenharmony_ci
1255f9996aaSopenharmony_cidef create_hap(options, signed_hap: str):
1265f9996aaSopenharmony_ci    with build_utils.temp_dir() as package_dir, tempfile.NamedTemporaryFile(
1275f9996aaSopenharmony_ci            suffix='.hap') as output:
1285f9996aaSopenharmony_ci        packing_cmd = ['java', '-jar', options.hap_packing_tool]
1295f9996aaSopenharmony_ci        packing_cmd.extend(
1305f9996aaSopenharmony_ci            ['--mode', 'hap', '--force', 'true', '--out-path', output.name])
1315f9996aaSopenharmony_ci
1325f9996aaSopenharmony_ci        hap_profile_path = os.path.join(package_dir,
1335f9996aaSopenharmony_ci                                        os.path.basename(options.hap_profile))
1345f9996aaSopenharmony_ci        shutil.copy(options.hap_profile, hap_profile_path)
1355f9996aaSopenharmony_ci        packing_cmd.extend(['--json-path', hap_profile_path])
1365f9996aaSopenharmony_ci        add_assets(options, package_dir, packing_cmd)
1375f9996aaSopenharmony_ci
1385f9996aaSopenharmony_ci        add_resources(options.packaged_resources, package_dir, packing_cmd)
1395f9996aaSopenharmony_ci        if options.enable_ark:
1405f9996aaSopenharmony_ci            tweak_hap_profile(options, package_dir)
1415f9996aaSopenharmony_ci        if options.dso:
1425f9996aaSopenharmony_ci            lib_path = os.path.join(package_dir, "lib")
1435f9996aaSopenharmony_ci            hap_lib_path = os.path.join(lib_path, options.ohos_app_abi)
1445f9996aaSopenharmony_ci            os.makedirs(hap_lib_path, exist_ok=True)
1455f9996aaSopenharmony_ci            for dso in sorted(options.dso):
1465f9996aaSopenharmony_ci                shutil.copy(dso, hap_lib_path)
1475f9996aaSopenharmony_ci            packing_cmd.extend(['--lib-path', lib_path])
1485f9996aaSopenharmony_ci
1495f9996aaSopenharmony_ci        build_utils.check_output(packing_cmd)
1505f9996aaSopenharmony_ci
1515f9996aaSopenharmony_ci        sign_hap(options, output.name, signed_hap)
1525f9996aaSopenharmony_ci
1535f9996aaSopenharmony_ci
1545f9996aaSopenharmony_cidef parse_args(args):
1555f9996aaSopenharmony_ci    args = build_utils.expand_file_args(args)
1565f9996aaSopenharmony_ci
1575f9996aaSopenharmony_ci    parser = optparse.OptionParser()
1585f9996aaSopenharmony_ci    build_utils.add_depfile_option(parser)
1595f9996aaSopenharmony_ci    parser.add_option('--hap-path', help='path to output hap')
1605f9996aaSopenharmony_ci    parser.add_option('--hapsigner', help='path to signer')
1615f9996aaSopenharmony_ci    parser.add_option('--assets', help='path to assets')
1625f9996aaSopenharmony_ci    parser.add_option('--dso',
1635f9996aaSopenharmony_ci                      action="append",
1645f9996aaSopenharmony_ci                      help='path to dynamic shared objects')
1655f9996aaSopenharmony_ci    parser.add_option('--ohos-app-abi', help='ohos app abi')
1665f9996aaSopenharmony_ci    parser.add_option('--hap-profile', help='path to hap profile')
1675f9996aaSopenharmony_ci    parser.add_option('--nodejs-path', help='path to node')
1685f9996aaSopenharmony_ci    parser.add_option('--js2abc-js', help='path to ts2abc.js')
1695f9996aaSopenharmony_ci    parser.add_option('--enable-ark',
1705f9996aaSopenharmony_ci                      action='store_true',
1715f9996aaSopenharmony_ci                      default=False,
1725f9996aaSopenharmony_ci                      help='whether to transform js to ark bytecode')
1735f9996aaSopenharmony_ci    parser.add_option('--hap-packing-tool', help='path to hap packing tool')
1745f9996aaSopenharmony_ci    parser.add_option('--private-key-path', help='path to private key')
1755f9996aaSopenharmony_ci    parser.add_option('--sign-algo', help='signature algorithm')
1765f9996aaSopenharmony_ci    parser.add_option('--certificate-profile',
1775f9996aaSopenharmony_ci                      help='path to certificate profile')
1785f9996aaSopenharmony_ci    parser.add_option('--keyalias', help='keyalias')
1795f9996aaSopenharmony_ci    parser.add_option('--keystore-path', help='path to keystore')
1805f9996aaSopenharmony_ci    parser.add_option('--keystorepasswd', help='password of keystore')
1815f9996aaSopenharmony_ci    parser.add_option('--certificate-file', help='path to certificate file')
1825f9996aaSopenharmony_ci    parser.add_option('--packaged-resources',
1835f9996aaSopenharmony_ci                      help='path to packaged resources')
1845f9996aaSopenharmony_ci    parser.add_option('--packaged-js-assets',
1855f9996aaSopenharmony_ci                      help='path to packaged js assets')
1865f9996aaSopenharmony_ci    parser.add_option('--app-profile', default=False,
1875f9996aaSopenharmony_ci                      help='path to packaged js assets')
1885f9996aaSopenharmony_ci    parser.add_option('--build-mode', help='debug mode or release mode')
1895f9996aaSopenharmony_ci
1905f9996aaSopenharmony_ci    parser.add_option('--sign_hap_py_path', help='sign_hap_py_path')
1915f9996aaSopenharmony_ci    parser.add_option('--sign_compatible_version', default='', help='limit compatible_Version')
1925f9996aaSopenharmony_ci
1935f9996aaSopenharmony_ci    options, _ = parser.parse_args(args)
1945f9996aaSopenharmony_ci    if options.assets:
1955f9996aaSopenharmony_ci        options.assets = build_utils.parse_gn_list(options.assets)
1965f9996aaSopenharmony_ci    return options
1975f9996aaSopenharmony_ci
1985f9996aaSopenharmony_ci
1995f9996aaSopenharmony_cidef main(args):
2005f9996aaSopenharmony_ci    options = parse_args(args)
2015f9996aaSopenharmony_ci
2025f9996aaSopenharmony_ci    inputs = [
2035f9996aaSopenharmony_ci        options.hap_profile, options.packaged_js_assets,
2045f9996aaSopenharmony_ci        options.packaged_resources, options.certificate_file,
2055f9996aaSopenharmony_ci        options.keystore_path, options.certificate_profile
2065f9996aaSopenharmony_ci    ]
2075f9996aaSopenharmony_ci    depfiles = []
2085f9996aaSopenharmony_ci    for dire in options.assets:
2095f9996aaSopenharmony_ci        depfiles += (build_utils.get_all_files(dire))
2105f9996aaSopenharmony_ci    if options.dso:
2115f9996aaSopenharmony_ci        depfiles.extend(options.dso)
2125f9996aaSopenharmony_ci
2135f9996aaSopenharmony_ci    build_utils.call_and_write_depfile_if_stale(
2145f9996aaSopenharmony_ci        lambda: create_hap(options, options.hap_path),
2155f9996aaSopenharmony_ci        options,
2165f9996aaSopenharmony_ci        depfile_deps=depfiles,
2175f9996aaSopenharmony_ci        input_paths=inputs + depfiles,
2185f9996aaSopenharmony_ci        input_strings=[
2195f9996aaSopenharmony_ci            options.keystorepasswd, options.keyalias, options.sign_algo,
2205f9996aaSopenharmony_ci            options.private_key_path
2215f9996aaSopenharmony_ci        ],
2225f9996aaSopenharmony_ci        output_paths=([options.hap_path]),
2235f9996aaSopenharmony_ci        force=False,
2245f9996aaSopenharmony_ci        add_pydeps=False)
2255f9996aaSopenharmony_ci
2265f9996aaSopenharmony_ci
2275f9996aaSopenharmony_ciif __name__ == '__main__':
2285f9996aaSopenharmony_ci    sys.exit(main(sys.argv[1:]))
229