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 os 18import sys 19import json 20 21from zipfile import ZipFile # noqa: E402 22from util import build_utils # noqa: E402 23 24 25def parse_args(args): 26 args = build_utils.expand_file_args(args) 27 28 parser = optparse.OptionParser() 29 build_utils.add_depfile_option(parser) 30 parser.add_option('--output', help='stamp file') 31 parser.add_option('--js-assets-dir', help='js assets directory') 32 parser.add_option('--ets-assets-dir', help='ets assets directory') 33 parser.add_option('--nodejs-path', help='path to nodejs app') 34 parser.add_option('--webpack-js', help='path to webpack.js') 35 parser.add_option('--webpack-config-js', help='path to webpack.config.js') 36 parser.add_option('--webpack-config-ets', help='path to webpack.rich.config.js') 37 parser.add_option('--hap-profile', help='path to hap profile') 38 parser.add_option('--build-mode', help='debug mode or release mode') 39 parser.add_option('--js-sources-file', help='path to js sources file') 40 parser.add_option('--js2abc', 41 action='store_true', 42 default=False, 43 help='whether to transform js to ark bytecode') 44 parser.add_option('--ets2abc', 45 action='store_true', 46 default=False, 47 help='whether to transform ets to ark bytecode') 48 parser.add_option('--ark-frontend-dir', help='path to ark frontend dir') 49 parser.add_option('--ace-loader-home', help='path to ace-loader dir.') 50 parser.add_option('--ets-loader-home', help='path to ets-loader dir.') 51 parser.add_option('--app-profile', default=False, help='path to app-profile.') 52 53 options, _ = parser.parse_args(args) 54 options.js_assets_dir = build_utils.parse_gn_list(options.js_assets_dir) 55 options.ets_assets_dir = build_utils.parse_gn_list(options.ets_assets_dir) 56 return options 57 58def make_my_env(build_dir, options, js2abc, ability_index): 59 out_dir = os.path.abspath(os.path.dirname(options.output)) 60 assets_dir = os.path.join(out_dir, "assets") 61 if options.app_profile: 62 if js2abc: 63 assets_dir = os.path.join(assets_dir, "js") 64 else: 65 assets_dir = os.path.join(assets_dir, "ets") 66 gen_dir = os.path.join(out_dir, "gen") 67 my_env = { 68 "aceModuleBuild": assets_dir, 69 "buildMode": options.build_mode, 70 "PATH": os.environ.get('PATH'), 71 "appResource": os.path.join(gen_dir, "ResourceTable.txt") 72 } 73 with open(options.hap_profile) as profile: 74 config = json.load(profile) 75 ability_cnt = len(config['module']['abilities']) 76 if ability_index < ability_cnt and (options.js_asset_cnt > 1 or options.ets_asset_cnt > 1): 77 if config['module']['abilities'][ability_index].__contains__('forms'): 78 my_env["abilityType"] = 'form' 79 else: 80 my_env["abilityType"] = config['module']['abilities'][ability_index]['type'] 81 elif config['module'].__contains__('testRunner'): 82 my_env["abilityType"] = 'testrunner' 83 84 if options.app_profile: 85 my_env["aceProfilePath"] = os.path.join(gen_dir, "resources/base/profile") 86 my_env["aceModuleJsonPath"] = os.path.abspath(options.hap_profile) 87 else: 88 manifest = os.path.join(build_dir, 'manifest.json') 89 my_env["aceManifestPath"] = manifest 90 return my_env 91 92def make_manifest_data(config, options, js2abc, ability_index): 93 data = dict() 94 data['appID'] = config['app']['bundleName'] 95 ability_cnt = len(config['module']['abilities']) 96 assets_cnt = 0 97 if js2abc: 98 assets_cnt = options.js_asset_cnt 99 else: 100 assets_cnt = options.ets_asset_cnt 101 if ability_index < ability_cnt and config['module']['abilities'][ability_index].__contains__("label"): 102 data['appName'] = config['module']['abilities'][ability_index]['label'] 103 if options.app_profile: 104 data['versionName'] = config['app']['versionName'] 105 data['versionCode'] = config['app']['versionCode'] 106 data['pages'] = config['module']['pages'] 107 data['deviceType'] = config['module']['deviceTypes'] 108 else: 109 data['versionName'] = config['app']['version']['name'] 110 data['versionCode'] = config['app']['version']['code'] 111 for js_module in config['module']['js']: 112 ability_name = '' 113 js_module_name = js_module.get('name').split('.')[-1] 114 if ability_index < ability_cnt: 115 ability_name = config['module']['abilities'][ability_index]['name'].split('.')[-1] 116 if js_module_name != ability_name: 117 if (js_module_name == 'default' and ability_name == 'MainAbility') or assets_cnt == 1: 118 ability_name = 'default' 119 if js_module_name == ability_name: 120 data['pages'] = js_module.get('pages') 121 data['window'] = js_module.get('window') 122 if js_module.get('type') == 'form': 123 data['pages'] = [] 124 data['type'] = 'form' 125 data['deviceType'] = config['module']['deviceType'] 126 if js2abc and (config['module']['abilities'][0].get('srcLanguage') == 'ets' or ability_index >= ability_cnt): 127 for js_page in config['module']['js']: 128 if js_page.get('type') == 'form': 129 data['pages'] = js_page.get('pages') 130 data['type'] = js_page.get('type') 131 data['window'] = js_page.get('window') 132 if not js2abc: 133 if not options.app_profile and ability_index < len(config['module']['js']): 134 data['mode'] = config['module']['js'][ability_index].get('mode') 135 return data 136 137def build_ace(cmd, options, js2abc, loader_home, assets_dir): 138 gen_dir = '' 139 src_path = '' 140 if js2abc: 141 for asset_index in range(options.js_asset_cnt): 142 ability_dir = os.path.relpath(assets_dir[asset_index], loader_home) 143 if options.js_sources_file: 144 with open(options.js_sources_file, 'wb') as js_sources_file: 145 sources = get_all_js_sources(ability_dir) 146 js_sources_file.write('\n'.join(sources).encode()) 147 with build_utils.temp_dir() as build_dir: 148 my_env = make_my_env(build_dir, options, js2abc, asset_index) 149 my_env["aceModuleRoot"] = ability_dir 150 gen_dir = my_env.get("aceModuleBuild") 151 if options.app_profile: 152 gen_dir = os.path.dirname(gen_dir) 153 my_env.update({"cachePath": os.path.join(build_dir, ".cache")}) 154 if not options.app_profile: 155 src_path = 'default' 156 manifest = os.path.join(build_dir, 'manifest.json') 157 if not os.path.exists(manifest) and not options.app_profile: 158 with open(options.hap_profile) as profile: 159 config = json.load(profile) 160 data = make_manifest_data(config, options, js2abc, asset_index) 161 if config['module'].__contains__('testRunner'): 162 src_path = config['module']['testRunner']['srcPath'] 163 if options.js_asset_cnt > 1 and asset_index < len(config['module']['abilities']): 164 if 'srcPath' in config['module']['abilities'][asset_index]: 165 src_path = config['module']['abilities'][asset_index]['srcPath'] 166 if config['module']['abilities'][0].get('srcLanguage') == 'ets': 167 for ability in config['module']['abilities']: 168 if ability.__contains__('forms'): 169 src_path = ability['forms'][0].get('name') 170 if asset_index >= len(config['module']['abilities']): 171 for ability in config['module']['abilities']: 172 if ability.__contains__('forms'): 173 src_path = ability['forms'][0].get('name') 174 175 build_utils.write_json(data, manifest) 176 my_env["aceModuleBuild"] = os.path.join(my_env.get("aceModuleBuild"), src_path) 177 build_utils.check_output( 178 cmd, cwd=loader_home, env=my_env) 179 else: 180 for asset_index in range(options.ets_asset_cnt): 181 ability_dir = os.path.relpath(assets_dir[asset_index], loader_home) 182 if options.js_sources_file: 183 with open(options.js_sources_file, 'wb') as js_sources_file: 184 sources = get_all_js_sources(ability_dir) 185 js_sources_file.write('\n'.join(sources).encode()) 186 with build_utils.temp_dir() as build_dir: 187 my_env = make_my_env(build_dir, options, js2abc, asset_index) 188 my_env["aceModuleRoot"] = ability_dir 189 gen_dir = my_env.get("aceModuleBuild") 190 if options.app_profile: 191 gen_dir = os.path.dirname(gen_dir) 192 if not options.app_profile: 193 src_path = 'default' 194 manifest = os.path.join(build_dir, 'manifest.json') 195 if not os.path.exists(manifest) and not options.app_profile: 196 with open(options.hap_profile) as profile: 197 config = json.load(profile) 198 data = make_manifest_data(config, options, js2abc, asset_index) 199 if 'srcPath' in config['module']['abilities'][asset_index]: 200 src_path = config['module']['abilities'][asset_index]['srcPath'] 201 build_utils.write_json(data, manifest) 202 my_env["aceModuleBuild"] = os.path.join(my_env.get("aceModuleBuild"), src_path) 203 build_utils.check_output( 204 cmd, cwd=loader_home, env=my_env) 205 if options.app_profile: 206 build_utils.zip_dir(options.output, 207 gen_dir, 208 zip_prefix_path=src_path) 209 elif not options.app_profile and not options.hap_profile: 210 build_utils.zip_dir(options.output, 211 gen_dir, 212 zip_prefix_path='assets/js/{}/'.format(src_path)) 213 else: 214 build_utils.zip_dir(options.output, 215 gen_dir, 216 zip_prefix_path='assets/js/') 217 218 219def get_all_js_sources(base): 220 sources = [] 221 for root, _, files in os.walk(base): 222 for file in files: 223 if file[-3:] in ('.js', '.ts'): 224 sources.append(os.path.join(root, file)) 225 226 return sources 227 228 229def main(args): 230 options = parse_args(args) 231 232 inputs = [ 233 options.nodejs_path, options.webpack_js, options.webpack_config_js, options.webpack_config_ets 234 ] 235 depfiles = [] 236 options.js_asset_cnt = 0 237 options.ets_asset_cnt = 0 238 if options.js_assets_dir: 239 options.js_asset_cnt = len(options.js_assets_dir) 240 for asset_index in range(options.js_asset_cnt): 241 depfiles.extend(build_utils.get_all_files(options.js_assets_dir[asset_index])) 242 if options.ets_assets_dir: 243 options.ets_asset_cnt = len(options.ets_assets_dir) 244 for asset_index in range(options.ets_asset_cnt): 245 depfiles.extend(build_utils.get_all_files(options.ets_assets_dir[asset_index])) 246 if not options.js_assets_dir and not options.ets_assets_dir: 247 with ZipFile(options.output, 'w') as file: 248 return 249 250 if options.ark_frontend_dir: 251 depfiles.extend(build_utils.get_all_files(options.ark_frontend_dir)) 252 253 depfiles.append(options.webpack_js) 254 depfiles.append(options.webpack_config_js) 255 depfiles.append(options.webpack_config_ets) 256 depfiles.extend(build_utils.get_all_files(options.ace_loader_home)) 257 depfiles.extend(build_utils.get_all_files(options.ets_loader_home)) 258 259 node_js = os.path.relpath(options.nodejs_path, options.ace_loader_home) 260 261 if options.js_assets_dir: 262 js2abc = True 263 loader_home = options.ace_loader_home 264 assets_dir = options.js_assets_dir 265 cmd = [ 266 node_js, 267 os.path.relpath( 268 options.webpack_js, options.ace_loader_home), 269 '--config', 270 os.path.relpath( 271 options.webpack_config_js, options.ace_loader_home) 272 ] 273 ark_frontend_dir = os.path.relpath( 274 options.ark_frontend_dir, options.ace_loader_home) 275 if options.app_profile: 276 cmd.extend(['--env', 'buildMode={}'.format(options.build_mode), 'compilerType=ark', 277 'arkFrontendDir={}'.format(ark_frontend_dir), 'nodeJs={}'.format(node_js)]) 278 else: 279 cmd.extend(['--env', 'compilerType=ark', 280 'arkFrontendDir={}'.format(ark_frontend_dir), 'nodeJs={}'.format(node_js)]) 281 build_utils.call_and_write_depfile_if_stale( 282 lambda: build_ace(cmd, options, js2abc, loader_home, assets_dir), 283 options, 284 depfile_deps=depfiles, 285 input_paths=depfiles + inputs, 286 input_strings=cmd + [options.build_mode], 287 output_paths=([options.output]), 288 force=False, 289 add_pydeps=False) 290 291 if options.ets_assets_dir: 292 js2abc = False 293 loader_home = options.ets_loader_home 294 assets_dir = options.ets_assets_dir 295 cmd = [ 296 node_js, 297 os.path.relpath( 298 options.webpack_js, options.ets_loader_home), 299 '--config', 300 os.path.relpath( 301 options.webpack_config_ets, options.ets_loader_home) 302 ] 303 ark_frontend_dir = os.path.relpath( 304 options.ark_frontend_dir, options.ets_loader_home) 305 if options.app_profile: 306 cmd.extend(['--env', 'buildMode={}'.format(options.build_mode), 'compilerType=ark', 307 'arkFrontendDir={}'.format(ark_frontend_dir), 'nodeJs={}'.format(node_js)]) 308 else: 309 cmd.extend(['--env', 'compilerType=ark', 310 'arkFrontendDir={}'.format(ark_frontend_dir), 'nodeJs={}'.format(node_js)]) 311 build_utils.call_and_write_depfile_if_stale( 312 lambda: build_ace(cmd, options, js2abc, loader_home, assets_dir), 313 options, 314 depfile_deps=depfiles, 315 input_paths=depfiles + inputs, 316 input_strings=cmd + [options.build_mode], 317 output_paths=([options.output]), 318 force=False, 319 add_pydeps=False) 320 321 322if __name__ == '__main__': 323 sys.exit(main(sys.argv[1:])) 324