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(hapsigner, private_key_path, sign_algo, certificate_profile, 27 keystore_path, keystorepasswd, keyalias, certificate_file, 28 unsigned_hap_path, signed_hap_path): 29 cmd = ['java', '-jar', hapsigner, 'sign-app'] 30 cmd.extend(['-mode', 'localsign']) 31 cmd.extend(['-signAlg', sign_algo]) 32 cmd.extend(['-keyAlias', private_key_path]) 33 cmd.extend(['-inFile', unsigned_hap_path]) 34 cmd.extend(['-outFile', signed_hap_path]) 35 cmd.extend(['-profileFile', certificate_profile]) 36 cmd.extend(['-keystoreFile', keystore_path]) 37 cmd.extend(['-keystorePwd', keystorepasswd]) 38 cmd.extend(['-keyPwd', keyalias]) 39 cmd.extend(['-appCertFile', certificate_file]) 40 cmd.extend(['-profileSigned', '1']) 41 cmd.extend(['-inForm','zip']) 42 child = subprocess.Popen(cmd, 43 stdout=subprocess.PIPE, 44 stderr=subprocess.PIPE) 45 stdout, stderr = child.communicate() 46 if child.returncode: 47 print(stdout.decode(), stderr.decode()) 48 raise Exception("Failed to sign hap") 49 50 51def add_resources(packaged_resources, package_dir, packing_cmd): 52 if packaged_resources: 53 build_utils.extract_all(packaged_resources, 54 package_dir, 55 no_clobber=False) 56 index_file_path = os.path.join(package_dir, 'resources.index') 57 if os.path.exists(index_file_path): 58 packing_cmd.extend(['--index-path', index_file_path]) 59 resources_path = os.path.join(package_dir, 'resources') 60 if os.path.exists(resources_path): 61 packing_cmd.extend(['--resources-path', resources_path]) 62 63 64def add_assets(options, package_dir, packing_cmd): 65 packaged_js_assets, assets = options.packaged_js_assets, options.assets 66 if options.app_profile: 67 assets_dir = os.path.join(package_dir, 'ets') 68 js_assets_dir = os.path.join(package_dir, 'js') 69 else: 70 assets_dir = os.path.join(package_dir, 'assets') 71 72 if packaged_js_assets: 73 build_utils.extract_all(packaged_js_assets, 74 package_dir, 75 no_clobber=False) 76 if options.build_mode == "release": 77 for root, _, files in os.walk(assets_dir): 78 for f in files: 79 filename = os.path.join(root, f) 80 if filename.endswith('.js.map'): 81 os.unlink(filename) 82 if assets: 83 if not os.path.exists(assets_dir): 84 os.mkdir(assets_dir) 85 for item in assets: 86 if os.path.isfile(item): 87 shutil.copyfile( 88 item, os.path.join(assets_dir, os.path.basename(item))) 89 elif os.path.isdir(item): 90 shutil.copytree( 91 item, os.path.join(assets_dir, os.path.basename(item))) 92 if os.path.exists(assets_dir) and len(os.listdir(assets_dir)) != 0: 93 if options.app_profile: 94 packing_cmd.extend(['--ets-path', assets_dir]) 95 else: 96 packing_cmd.extend(['--assets-path', assets_dir]) 97 if options.app_profile: 98 if os.path.exists(js_assets_dir) and len(os.listdir(js_assets_dir)) != 0: 99 packing_cmd.extend(['--js-path', js_assets_dir]) 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): 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): 126 with build_utils.temp_dir() as package_dir, tempfile.NamedTemporaryFile( 127 suffix='.hap') 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) 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.hapsigner, options.private_key_path, 152 options.sign_algo, options.certificate_profile, 153 options.keystore_path, options.keystorepasswd, 154 options.keyalias, options.certificate_file, output.name, 155 signed_hap) 156 157 158def parse_args(args): 159 args = build_utils.expand_file_args(args) 160 161 parser = optparse.OptionParser() 162 build_utils.add_depfile_option(parser) 163 parser.add_option('--hap-path', help='path to output hap') 164 parser.add_option('--hapsigner', help='path to signer') 165 parser.add_option('--assets', help='path to assets') 166 parser.add_option('--dso', 167 action="append", 168 help='path to dynamic shared objects') 169 parser.add_option('--ohos-app-abi', help='ohos app abi') 170 parser.add_option('--hap-profile', help='path to hap profile') 171 parser.add_option('--nodejs-path', help='path to node') 172 parser.add_option('--js2abc-js', help='path to ts2abc.js') 173 parser.add_option('--enable-ark', 174 action='store_true', 175 default=False, 176 help='whether to transform js to ark bytecode') 177 parser.add_option('--hap-packing-tool', help='path to hap packing tool') 178 parser.add_option('--private-key-path', help='path to private key') 179 parser.add_option('--sign-algo', help='signature algorithm') 180 parser.add_option('--certificate-profile', 181 help='path to certificate profile') 182 parser.add_option('--keyalias', help='keyalias') 183 parser.add_option('--keystore-path', help='path to keystore') 184 parser.add_option('--keystorepasswd', help='password of keystore') 185 parser.add_option('--certificate-file', help='path to certificate file') 186 parser.add_option('--packaged-resources', 187 help='path to packaged resources') 188 parser.add_option('--packaged-js-assets', 189 help='path to packaged js assets') 190 parser.add_option('--app-profile', default=False, 191 help='path to packaged js assets') 192 parser.add_option('--build-mode', help='debug mode or release mode') 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