• 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(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