1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (c) 2023 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 argparse 17import os 18import sys 19import subprocess 20import shutil 21import json5 22 23from util import build_utils 24from util import file_utils 25 26 27def parse_args(args): 28 parser = argparse.ArgumentParser() 29 build_utils.add_depfile_option(parser) 30 31 parser.add_argument('--nodejs', help='nodejs path') 32 parser.add_argument('--cwd', help='app project directory') 33 parser.add_argument('--sdk-home', help='sdk home') 34 parser.add_argument('--enable-debug', action='store_true', help='if enable debuggable') 35 parser.add_argument('--build-level', default='project', help='module or project') 36 parser.add_argument('--assemble-type', default='assembleApp', help='assemble type') 37 parser.add_argument('--output-file', help='output file') 38 parser.add_argument('--build-profile', help='build profile file') 39 parser.add_argument('--system-lib-module-info-list', nargs='+', help='system lib module info list') 40 parser.add_argument('--ohos-app-abi', help='ohos app abi') 41 parser.add_argument('--ohpm-registry', help='ohpm registry', nargs='?') 42 parser.add_argument('--hap-out-dir', help='hap out dir') 43 parser.add_argument('--hap-name', help='hap name') 44 parser.add_argument('--test-hap', help='build ohosTest if enable', action='store_true') 45 parser.add_argument('--test-module', help='specify the module within ohosTest', default='entry') 46 parser.add_argument('--module-libs-dir', help='', default='entry') 47 parser.add_argument('--sdk-type-name', help='sdk type name', nargs='+', default=['sdk.dir']) 48 49 options = parser.parse_args(args) 50 return options 51 52 53def make_env(build_profile, cwd, ohpm_registry): 54 ''' 55 Set up the application compilation environment and run "ohpm install" 56 :param build_profile: module compilation information file 57 :param cwd: app project directory 58 :param ohpm_registry: ohpm registry 59 :return: None 60 ''' 61 cur_dir = os.getcwd() 62 with open(build_profile, 'r') as input_f: 63 build_info = json5.load(input_f) 64 modules_list = build_info.get('modules') 65 ohpm_install_cmd = ['ohpm', 'install'] 66 if ohpm_registry: 67 ohpm_install_cmd.append('--registry=' + ohpm_registry) 68 os.chdir(cwd) 69 if os.path.exists(os.path.join(cwd, 'oh_modules')): 70 subprocess.run(['rm', '-rf', 'oh_modules']) 71 subprocess.run(['chmod', '+x', 'hvigorw']) 72 if os.path.exists(os.path.join(cwd, '.arkui-x/android/gradlew')): 73 subprocess.run(['chmod', '+x', '.arkui-x/android/gradlew']) 74 75 proc = subprocess.Popen(ohpm_install_cmd, 76 stdout=subprocess.PIPE, 77 stderr=subprocess.PIPE, 78 encoding='utf-8') 79 stdout, stderr = proc.communicate() 80 if proc.returncode: 81 raise Exception('ReturnCode:{}. ohpm install failed. {}'.format( 82 proc.returncode, stderr)) 83 84 for module in modules_list: 85 src_path = module.get('srcPath') 86 ohpm_install_path = os.path.join(cwd, src_path) 87 if os.path.exists(os.path.join(ohpm_install_path, 'oh_modules')): 88 subprocess.run(['rm', '-rf', os.path.join(ohpm_install_path, 'oh_modules')]) 89 proc = subprocess.Popen(ohpm_install_cmd, 90 cwd=ohpm_install_path, 91 stdout=subprocess.PIPE, 92 stderr=subprocess.PIPE, 93 encoding='utf-8') 94 stdout, stderr = proc.communicate() 95 if proc.returncode: 96 raise Exception('ReturnCode:{}. ohpm install module failed. {}'.format( 97 proc.returncode, stderr)) 98 os.chdir(cur_dir) 99 100 101def gen_unsigned_hap_path_json(build_profile, cwd, options): 102 ''' 103 Generate unsigned_hap_path_list 104 :param build_profile: module compilation information file 105 :param cwd: app project directory 106 :return: None 107 ''' 108 unsigned_hap_path_json = {} 109 unsigned_hap_path_list = [] 110 with open(build_profile, 'r') as input_f: 111 build_info = json5.load(input_f) 112 modules_list = build_info.get('modules') 113 for module in modules_list: 114 src_path = module.get('srcPath') 115 if options.test_hap: 116 unsigned_hap_path = os.path.join( 117 cwd, src_path, 'build/default/outputs/ohosTest') 118 else: 119 unsigned_hap_path = os.path.join( 120 cwd, src_path, 'build/default/outputs/default') 121 hap_file = build_utils.find_in_directory( 122 unsigned_hap_path, '*-unsigned.hap') 123 unsigned_hap_path_list.extend(hap_file) 124 unsigned_hap_path_json['unsigned_hap_path_list'] = unsigned_hap_path_list 125 file_utils.write_json_file(options.output_file, unsigned_hap_path_json) 126 127 128def copy_libs(cwd, system_lib_module_info_list, ohos_app_abi, module_libs_dir): 129 ''' 130 Obtain the output location of system library .so by reading the module compilation information file, 131 and copy it to the app project directory 132 :param cwd: app project directory 133 :param system_lib_module_info_list: system library module compilation information file 134 :param ohos_app_abi: app abi 135 :return: None 136 ''' 137 for _lib_info in system_lib_module_info_list: 138 lib_info = file_utils.read_json_file(_lib_info) 139 lib_path = lib_info.get('source') 140 if os.path.exists(lib_path): 141 lib_name = os.path.basename(lib_path) 142 dest = os.path.join(cwd, f'{module_libs_dir}/libs', ohos_app_abi, lib_name) 143 if not os.path.exists(os.path.dirname(dest)): 144 os.makedirs(os.path.dirname(dest), exist_ok=True) 145 shutil.copyfile(lib_path, dest) 146 147 148def hvigor_build(cwd, options): 149 ''' 150 Run hvigorw to build the app or hap 151 :param cwd: app project directory 152 :param options: command line parameters 153 :return: None 154 ''' 155 if options.test_hap: 156 cmd = ['bash', './hvigorw', '--mode', 'module', '-p', 157 f'module={options.test_module}@ohosTest', 'assembleHap'] 158 else: 159 cmd = ['bash', './hvigorw', '--mode', 160 options.build_level, '-p', 'product=default', options.assemble_type] 161 if options.enable_debug: 162 cmd.extend(['-p', 'debuggable=true']) 163 else: 164 cmd.extend(['-p', 'debuggable=false']) 165 cmd.extend(['--no-daemon']) 166 sdk_dir = options.sdk_home 167 nodejs_dir = os.path.abspath( 168 os.path.dirname(os.path.dirname(options.nodejs))) 169 170 with open(os.path.join(cwd, 'local.properties'), 'w') as f: 171 for sdk_type in options.sdk_type_name: 172 f.write(f'{sdk_type}={sdk_dir}\n') 173 f.write(f'nodejs.dir={nodejs_dir}\n') 174 print("[0/0] Hvigor clean start") 175 subprocess.run(['bash', './hvigorw', '--sync', '--no-daemon'], cwd=cwd) 176 print("[0/0] Hvigor build start") 177 proc = subprocess.Popen(cmd, 178 cwd=cwd, 179 stdout=subprocess.PIPE, 180 stderr=subprocess.PIPE, 181 encoding='utf-8') 182 stdout, stderr = proc.communicate() 183 for line in stdout.splitlines(): 184 print(f"[1/1] Hvigor info: {line}") 185 for line in stderr.splitlines(): 186 print(f"[2/2] Hvigor warning: {line}") 187 os.makedirs(os.path.join(cwd, 'build'), exist_ok=True) 188 with open(os.path.join(cwd, 'build', 'build.log'), 'w') as f: 189 f.write(f'{stdout}\n') 190 f.write(f'{stderr}\n') 191 if proc.returncode or "ERROR: BUILD FAILED" in stderr or "ERROR: BUILD FAILED" in stdout: 192 raise Exception('ReturnCode:{}. Hvigor build failed: {}'.format(proc.returncode, stderr)) 193 print("[0/0] Hvigor build end") 194 195 196def main(args): 197 options = parse_args(args) 198 cwd = os.path.abspath(options.cwd) 199 200 # copy system lib deps to app libs dir 201 if options.system_lib_module_info_list: 202 copy_libs(cwd, options.system_lib_module_info_list, 203 options.ohos_app_abi, options.module_libs_dir) 204 205 os.environ['PATH'] = '{}:{}'.format(os.path.dirname( 206 os.path.abspath(options.nodejs)), os.environ.get('PATH')) 207 208 # add arkui-x to PATH 209 os.environ['PATH'] = f'{cwd}/.arkui-x/android:{os.environ.get("PATH")}' 210 211 # generate unsigned_hap_path_list and run ohpm install 212 make_env(options.build_profile, cwd, options.ohpm_registry) 213 214 # invoke hvigor to build hap or app 215 hvigor_build(cwd, options) 216 217 # generate a json file to record the path of all unsigned haps, and When signing hap later, 218 # this json file will serve as input to provide path information for each unsigned hap. 219 gen_unsigned_hap_path_json(options.build_profile, cwd, options) 220 221if __name__ == '__main__': 222 sys.exit(main(sys.argv[1:])) 223