• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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