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 os.mkdir(lib_path) 144 for dso in sorted(options.dso): 145 shutil.copy(dso, lib_path) 146 packing_cmd.extend(['--lib-path', lib_path]) 147 148 build_utils.check_output(packing_cmd) 149 150 sign_hap(options.hapsigner, options.private_key_path, 151 options.sign_algo, options.certificate_profile, 152 options.keystore_path, options.keystorepasswd, 153 options.keyalias, options.certificate_file, output.name, 154 signed_hap) 155 156 157def parse_args(args): 158 args = build_utils.expand_file_args(args) 159 160 parser = optparse.OptionParser() 161 build_utils.add_depfile_option(parser) 162 parser.add_option('--hap-path', help='path to output hap') 163 parser.add_option('--hapsigner', help='path to signer') 164 parser.add_option('--assets', help='path to assets') 165 parser.add_option('--dso', 166 action="append", 167 help='path to dynamic shared objects') 168 parser.add_option('--hap-profile', help='path to hap profile') 169 parser.add_option('--nodejs-path', help='path to node') 170 parser.add_option('--js2abc-js', help='path to ts2abc.js') 171 parser.add_option('--enable-ark', 172 action='store_true', 173 default=False, 174 help='whether to transform js to ark bytecode') 175 parser.add_option('--hap-packing-tool', help='path to hap packing tool') 176 parser.add_option('--private-key-path', help='path to private key') 177 parser.add_option('--sign-algo', help='signature algorithm') 178 parser.add_option('--certificate-profile', 179 help='path to certificate profile') 180 parser.add_option('--keyalias', help='keyalias') 181 parser.add_option('--keystore-path', help='path to keystore') 182 parser.add_option('--keystorepasswd', help='password of keystore') 183 parser.add_option('--certificate-file', help='path to certificate file') 184 parser.add_option('--packaged-resources', 185 help='path to packaged resources') 186 parser.add_option('--packaged-js-assets', 187 help='path to packaged js assets') 188 parser.add_option('--app-profile', default=False, 189 help='path to packaged js assets') 190 parser.add_option('--build-mode', help='debug mode or release mode') 191 192 options, _ = parser.parse_args(args) 193 if options.assets: 194 options.assets = build_utils.parse_gn_list(options.assets) 195 return options 196 197 198def main(args): 199 options = parse_args(args) 200 201 inputs = [ 202 options.hap_profile, options.packaged_js_assets, 203 options.packaged_resources, options.certificate_file, 204 options.keystore_path, options.certificate_profile 205 ] 206 depfiles = [] 207 for dire in options.assets: 208 depfiles += (build_utils.get_all_files(dire)) 209 if options.dso: 210 depfiles.extend(options.dso) 211 212 build_utils.call_and_write_depfile_if_stale( 213 lambda: create_hap(options, options.hap_path), 214 options, 215 depfile_deps=depfiles, 216 input_paths=inputs + depfiles, 217 input_strings=[ 218 options.keystorepasswd, options.keyalias, options.sign_algo, 219 options.private_key_path 220 ], 221 output_paths=([options.hap_path]), 222 force=False, 223 add_pydeps=False) 224 225 226if __name__ == '__main__': 227 sys.exit(main(sys.argv[1:])) 228