1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2021 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import optparse 17import subprocess 18import sys 19import shutil 20import os 21import tempfile 22import json 23from util import build_utils # noqa: E402 24 25 26def sign_hap(options, unsigned_hap_path: str, signed_hap_path: str): 27 cmd = ['python3', options.sign_hap_py_path] 28 cmd.extend(['--hapsigner', options.hapsigner]) 29 cmd.extend(['--sign-algo', options.sign_algo]) 30 cmd.extend(['--keyalias', options.keyalias]) 31 cmd.extend(['--inFile', unsigned_hap_path]) 32 cmd.extend(['--outFile', signed_hap_path]) 33 cmd.extend(['--profileFile', options.certificate_profile]) 34 cmd.extend(['--keystoreFile', options.keystore_path]) 35 cmd.extend(['--keystorePwd', options.keystorepasswd]) 36 cmd.extend(['--keyPwd', options.private_key_path]) 37 cmd.extend(['--certificate-file', options.certificate_file]) 38 cmd.extend(['--profileSigned', '1']) 39 cmd.extend(['--inForm', 'zip']) 40 cmd.extend(['--compatible_version', options.sign_compatible_version]) 41 child = subprocess.Popen(cmd, 42 stdout=subprocess.PIPE, 43 stderr=subprocess.PIPE) 44 stdout, stderr = child.communicate() 45 if child.returncode: 46 print(stdout.decode(), stderr.decode()) 47 raise Exception("Failed to sign hap {}.".format(options.sign_hap_py_path)) 48 49 50def add_resources(packaged_resources: list, package_dir: str, packing_cmd: list): 51 if packaged_resources: 52 build_utils.extract_all(packaged_resources, 53 package_dir, 54 no_clobber=False) 55 index_file_path = os.path.join(package_dir, 'resources.index') 56 if os.path.exists(index_file_path): 57 packing_cmd.extend(['--index-path', index_file_path]) 58 resources_path = os.path.join(package_dir, 'resources') 59 if os.path.exists(resources_path): 60 packing_cmd.extend(['--resources-path', resources_path]) 61 62 63def add_assets(options, package_dir: str, packing_cmd: list): 64 packaged_js_assets, assets = options.packaged_js_assets, options.assets 65 if options.app_profile: 66 assets_dir = os.path.join(package_dir, 'ets') 67 js_assets_dir = os.path.join(package_dir, 'js') 68 else: 69 assets_dir = os.path.join(package_dir, 'assets') 70 71 if packaged_js_assets: 72 build_utils.extract_all(packaged_js_assets, 73 package_dir, 74 no_clobber=False) 75 if options.build_mode == "release": 76 for root, _, files in os.walk(assets_dir): 77 for f in files: 78 filename = os.path.join(root, f) 79 if filename.endswith('.js.map'): 80 os.unlink(filename) 81 if assets: 82 if not os.path.exists(assets_dir): 83 os.mkdir(assets_dir) 84 for item in assets: 85 if os.path.isfile(item): 86 shutil.copyfile( 87 item, os.path.join(assets_dir, os.path.basename(item))) 88 elif os.path.isdir(item): 89 shutil.copytree( 90 item, os.path.join(assets_dir, os.path.basename(item))) 91 if os.path.exists(assets_dir) and len(os.listdir(assets_dir)) != 0: 92 if options.app_profile: 93 packing_cmd.extend(['--ets-path', assets_dir]) 94 else: 95 packing_cmd.extend(['--assets-path', assets_dir]) 96 if options.app_profile: 97 if os.path.exists(js_assets_dir) and len(os.listdir(js_assets_dir)) != 0: 98 packing_cmd.extend(['--js-path', js_assets_dir]) 99 100 101def get_ark_toolchain_version(options): 102 cmd = [options.nodejs_path, options.js2abc_js, '--bc-version'] 103 return build_utils.check_output(cmd).strip('\n') 104 105 106def tweak_hap_profile(options, package_dir: str): 107 config_name = 'config.json' 108 if options.app_profile: 109 config_name = 'module.json' 110 hap_profile = os.path.join(package_dir, config_name) 111 if not os.path.exists(hap_profile): 112 raise Exception('Error: {} of hap file not exists'.format(config_name)) 113 config = {} 114 with open(hap_profile, 'r') as fileobj: 115 config = json.load(fileobj) 116 if options.app_profile: 117 config.get('module')['virtualMachine'] = 'ark{}'.format( 118 get_ark_toolchain_version(options)) 119 else: 120 config.get('module').get('distro')['virtualMachine'] = 'ark{}'.format( 121 get_ark_toolchain_version(options)) 122 build_utils.write_json(config, hap_profile) 123 124 125def create_hap(options, signed_hap: str): 126 with build_utils.temp_dir() as package_dir, tempfile.NamedTemporaryFile( 127 suffix='.hap', delete=False, dir=options.target_out_dir) as output: 128 packing_cmd = ['java', '-jar', options.hap_packing_tool] 129 packing_cmd.extend( 130 ['--mode', 'hap', '--force', 'true', '--out-path', output.name]) 131 132 hap_profile_path = os.path.join(package_dir, 133 os.path.basename(options.hap_profile)) 134 shutil.copy(options.hap_profile, hap_profile_path) 135 packing_cmd.extend(['--json-path', hap_profile_path]) 136 add_assets(options, package_dir, packing_cmd) 137 138 add_resources(options.packaged_resources, package_dir, packing_cmd) 139 if options.enable_ark: 140 tweak_hap_profile(options, package_dir) 141 if options.dso: 142 lib_path = os.path.join(package_dir, "lib") 143 hap_lib_path = os.path.join(lib_path, options.ohos_app_abi) 144 os.makedirs(hap_lib_path, exist_ok=True) 145 for dso in sorted(options.dso): 146 shutil.copy(dso, hap_lib_path) 147 packing_cmd.extend(['--lib-path', lib_path]) 148 149 build_utils.check_output(packing_cmd) 150 151 sign_hap(options, output.name, signed_hap) 152 153 154def parse_args(args): 155 args = build_utils.expand_file_args(args) 156 157 parser = optparse.OptionParser() 158 build_utils.add_depfile_option(parser) 159 parser.add_option('--hap-path', help='path to output hap') 160 parser.add_option('--hapsigner', help='path to signer') 161 parser.add_option('--assets', help='path to assets') 162 parser.add_option('--dso', 163 action="append", 164 help='path to dynamic shared objects') 165 parser.add_option('--ohos-app-abi', help='ohos app abi') 166 parser.add_option('--hap-profile', help='path to hap profile') 167 parser.add_option('--nodejs-path', help='path to node') 168 parser.add_option('--js2abc-js', help='path to ts2abc.js') 169 parser.add_option('--enable-ark', 170 action='store_true', 171 default=False, 172 help='whether to transform js to ark bytecode') 173 parser.add_option('--hap-packing-tool', help='path to hap packing tool') 174 parser.add_option('--private-key-path', help='path to private key') 175 parser.add_option('--sign-algo', help='signature algorithm') 176 parser.add_option('--certificate-profile', 177 help='path to certificate profile') 178 parser.add_option('--keyalias', help='keyalias') 179 parser.add_option('--keystore-path', help='path to keystore') 180 parser.add_option('--keystorepasswd', help='password of keystore') 181 parser.add_option('--certificate-file', help='path to certificate file') 182 parser.add_option('--packaged-resources', 183 help='path to packaged resources') 184 parser.add_option('--packaged-js-assets', 185 help='path to packaged js assets') 186 parser.add_option('--app-profile', default=False, 187 help='path to packaged js assets') 188 parser.add_option('--build-mode', help='debug mode or release mode') 189 190 parser.add_option('--sign_hap_py_path', help='sign_hap_py_path') 191 parser.add_option('--sign_compatible_version', default='', help='limit compatible_Version') 192 parser.add_option('--target-out-dir', default='', help='') 193 194 options, _ = parser.parse_args(args) 195 if options.assets: 196 options.assets = build_utils.parse_gn_list(options.assets) 197 return options 198 199 200def main(args): 201 options = parse_args(args) 202 203 inputs = [ 204 options.hap_profile, options.packaged_js_assets, 205 options.packaged_resources, options.certificate_file, 206 options.keystore_path, options.certificate_profile 207 ] 208 depfiles = [] 209 for dire in options.assets: 210 depfiles += (build_utils.get_all_files(dire)) 211 if options.dso: 212 depfiles.extend(options.dso) 213 214 build_utils.call_and_write_depfile_if_stale( 215 lambda: create_hap(options, options.hap_path), 216 options, 217 depfile_deps=depfiles, 218 input_paths=inputs + depfiles, 219 input_strings=[ 220 options.keystorepasswd, options.keyalias, options.sign_algo, 221 options.private_key_path 222 ], 223 output_paths=([options.hap_path]), 224 force=False, 225 add_pydeps=False) 226 227 228if __name__ == '__main__': 229 sys.exit(main(sys.argv[1:])) 230