1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as childProcess from 'child_process'; 17import * as process from 'process'; 18import * as fs from 'fs'; 19import * as path from 'path'; 20import * as os from 'os'; 21import { 22 MODULES_ABC, 23 TEMPORARY, 24 AOT_FULL, 25 AOT_TYPE, 26 AOT_PARTIAL, 27 TS2ABC 28} from './pre_define'; 29import { 30 isWindows, 31 mkdirsSync, 32 toUnixPath, 33 validateFilePathLength, 34 validateFilePathLengths 35} from './utils'; 36import { 37 getArkBuildDir, 38 getBuildBinDir 39} from './ark_utils'; 40 41const hostToolKeyWords: string = '[HostTool] '; 42const logLevelIndex: number = 4; 43 44export interface FaultHandler { 45 (error: string): void 46} 47 48function checkAotPartialConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 49 if (buildJsonInfo.anBuildMode !== AOT_PARTIAL && !buildJsonInfo.apPath) { 50 // no AOT's partial related configuration is hit, pass the configuration to next compile mode 51 return false; 52 } 53 // Aot compiler's partial mode. 54 return true; 55} 56 57function checkAotFullConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 58 if (buildJsonInfo.anBuildMode !== AOT_FULL) { 59 // no AOT's full related configuration is hit, pass the configuration to next compile mode 60 return false; 61 } 62 // Aot compiler's full mode. 63 return true; 64} 65 66function checkAotTypeConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 67 if (buildJsonInfo.anBuildMode !== AOT_TYPE) { 68 // no AOT's type related configuration is hit, pass the configuration to next compile mode 69 return false; 70 } 71 // Aot compiler's type mode. 72 return true; 73} 74 75/** 76 * Check if the AOT related configuration is hit 77 * @param compileMode CompileMode for the project, which can be jsbundle or esmodule 78 * @param buildJsonInfo buildJsonInfo which parsed from projectConfig.aceBuildJson 79 * @param faultHandler faultHandler for illegal AOT configuration 80 * @returns {Boolean} false means no AOT related configuration found, else return true 81 * @api private 82 */ 83export function checkAotConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 84 return checkAotTypeConfig(compileMode, buildJsonInfo, faultHandler) || 85 checkAotFullConfig(compileMode, buildJsonInfo, faultHandler) || 86 checkAotPartialConfig(compileMode, buildJsonInfo, faultHandler); 87} 88 89export function generateAot(arkDir: string, builtinAbcPath: string, appAbc: string, 90 projectConfig: any, logger: any, faultHandler: FaultHandler): void { 91 let aotCompiler: string = path.join(getBuildBinDir(arkDir), isWindows() ? "ark_aot_compiler.exe" : "ark_aot_compiler"); 92 const appAot: string = path.join(projectConfig.anBuildOutPut, projectConfig.moduleName); 93 94 if (!validateFilePathLengths([aotCompiler, appAbc, builtinAbcPath, appAot], logger)) { 95 faultHandler(`ArkTS:ERROR generateAot failed. Invalid file path.`); 96 } 97 if (!fs.existsSync(appAbc)) { 98 faultHandler(`ArkTS:ERROR generateAot failed. AppAbc not found in "${appAbc}"`); 99 } 100 const singleCmdPrefix: string = `"${aotCompiler}" --builtins-dts="${builtinAbcPath}" ` + 101 `--aot-file="${appAot}" --compiler-target-triple=aarch64-unknown-linux-gnu `; 102 let singleCmd: string = ""; 103 if (projectConfig.anBuildMode === AOT_FULL) { 104 singleCmd = singleCmdPrefix + ` "${appAbc}"`; 105 } else if (projectConfig.anBuildMode === AOT_PARTIAL) { 106 const profile: string = projectConfig.apPath; 107 if (!validateFilePathLength(profile, logger)) { 108 faultHandler(`ArkTS:ERROR generateAot failed. Invalid profile file path.`); 109 } 110 if (!fs.existsSync(profile)) { 111 faultHandler(`ArkTS:ERROR generateAot failed. Partial mode lost profile in "${profile}"`); 112 } 113 singleCmd = singleCmdPrefix + ` --enable-pgo-profiler=true --compiler-pgo-profiler-path="${profile}" "${appAbc}"`; 114 } else { 115 faultHandler(`ArkTS:ERROR generateAot failed. unknown anBuildMode: ${projectConfig.anBuildMode}`); 116 } 117 try { 118 logger.debug(`generateAot cmd: ${singleCmd}`); 119 mkdirsSync(projectConfig.anBuildOutPut); 120 fs.closeSync(fs.openSync(appAot + '.an', 'w')); 121 fs.closeSync(fs.openSync(appAot + '.ai', 'w')); 122 } catch (e) { 123 // Extract HostTool log information from hilog, which outputs to stdout. 124 let outStream: Buffer = e.stdout; 125 outStream.toString().split(os.EOL).forEach((stdLog: string) => { 126 if (!stdLog.includes(hostToolKeyWords)) { 127 return; 128 } 129 let logHeader: string = ''; 130 let logContent: string = ''; 131 [logHeader, logContent] = stdLog.split(hostToolKeyWords); 132 if (!logHeader && !logContent) { 133 return; 134 } 135 let logLevel: string = logHeader.trim().split(/\s+/)[logLevelIndex]; 136 if (logLevel === 'F' || logLevel === 'E') { 137 logger.warn(`ArkTS:WARN ${logContent}`); 138 } 139 }); 140 logger.warn(`ArkTS:WARN ${e}`); 141 logger.warn(`ArkTS:WARN Failed to generate aot file, the module may run with an interpreter`); 142 } 143} 144 145export function generateBuiltinAbc(arkDir: string, abcArgs: string[], cachePath: string, 146 logger: any, faultHandler: FaultHandler, pandaMode: string | undefined): string { 147 const builtinFilePath: string = path.join(getArkBuildDir(arkDir), "aot", "src", "lib_ark_builtins.d.ts"); 148 const builtinAbcPath: string = path.join(cachePath, TEMPORARY, "aot", "lib_ark_builtins.d.abc"); 149 if (fs.existsSync(builtinAbcPath)) { 150 logger.debug(`builtin.d.abc already exists, no need to rebuild again`); 151 return builtinAbcPath; 152 } 153 mkdirsSync(path.dirname(builtinAbcPath)); 154 if (!validateFilePathLengths([builtinFilePath, builtinAbcPath], logger)) { 155 faultHandler(`ArkTS:ERROR generateBuiltinAbc failed. Invalid file path.`); 156 } 157 const tempAbcArgs: string[] = abcArgs.slice(0); 158 let singleCmd: string; 159 if (pandaMode === TS2ABC) { 160 singleCmd= `${tempAbcArgs.join(' ')} "${toUnixPath(builtinFilePath)}" -q -b -m --merge-abc -o "${builtinAbcPath}"`; 161 } else { 162 singleCmd= `${tempAbcArgs.join(' ')} "${toUnixPath(builtinFilePath)}" --type-dts-builtin --type-extractor --module --merge-abc --output "${builtinAbcPath}"`; 163 } 164 try { 165 logger.debug(`generateBuiltinAbc cmd: ${singleCmd}`); 166 childProcess.execSync(singleCmd, { windowsHide: true }); 167 } catch (e) { 168 faultHandler(`ArkTS:ERROR Failed to generate builtin to abc. Error message: ${e}`); 169 } 170 return builtinAbcPath; 171} 172