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 ESMODULE, 25 AOT_FULL, 26 AOT_TYPE, 27 AOT_PARTIAL, 28 AOT_PROFILE_SUFFIX 29} from './pre_define'; 30import { 31 isWindows, 32 mkdirsSync, 33 toUnixPath, 34 validateFilePathLength, 35 validateFilePathLengths 36} from './utils'; 37import { 38 getArkBuildDir, 39 getBuildBinDir 40} from './ark_utils'; 41 42const hostToolKeyWords: string = '[HostTool] '; 43const logLevelIndex: number = 4; 44 45export interface FaultHandler { 46 (error: string): void 47} 48 49function checkAotPartialConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 50 if (buildJsonInfo.anBuildMode !== AOT_PARTIAL && !buildJsonInfo.apPath) { 51 // no AOT's partial related configuration is hit, pass the configuration to next compile mode 52 return false; 53 } 54 if (compileMode !== ESMODULE) { 55 faultHandler(`ArkTS:ERROR Aot's partial mode must config compileMode with esmodule.`); 56 return true; 57 } 58 if (buildJsonInfo.anBuildMode !== AOT_PARTIAL) { 59 faultHandler(`ArkTS:ERROR Aot's partial mode must config aotBuildMode with partial.`); 60 return true; 61 } 62 if (!buildJsonInfo.apPath) { 63 faultHandler(`ArkTS:ERROR Aot's partial mode must config a valid apPath.`); 64 return true; 65 } 66 if (path.extname(buildJsonInfo.apPath) !== AOT_PROFILE_SUFFIX) { 67 faultHandler(`ArkTS:ERROR apPath for Aot's partial mode must with suffix "${AOT_PROFILE_SUFFIX}".`); 68 return true; 69 } 70 if (!fs.existsSync(buildJsonInfo.apPath)) { 71 faultHandler(`ArkTS:ERROR apPath for Aot's partial mode is not found in "${buildJsonInfo.apPath}".`); 72 return true; 73 } 74 if (!buildJsonInfo.anBuildOutPut) { 75 faultHandler(`ArkTS:ERROR Aot's partial mode need anBuildOutPut.`); 76 return true; 77 } 78 // Aot compiler's partial mode. 79 return true; 80} 81 82function checkAotFullConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 83 if (buildJsonInfo.anBuildMode !== AOT_FULL) { 84 // no AOT's full related configuration is hit, pass the configuration to next compile mode 85 return false; 86 } 87 if (compileMode !== ESMODULE) { 88 faultHandler(`ArkTS:ERROR Aot's full mode must config compileMode with esmodule.`); 89 return true; 90 } 91 if (buildJsonInfo.apPath) { 92 faultHandler(`ArkTS:ERROR Aot's full mode do not need apPath.`); 93 return true; 94 } 95 if (!buildJsonInfo.anBuildOutPut) { 96 faultHandler(`ArkTS:ERROR Aot's full mode need anBuildOutPut.`); 97 return true; 98 } 99 // Aot compiler's full mode. 100 return true; 101} 102 103function checkAotTypeConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 104 if (buildJsonInfo.anBuildMode !== AOT_TYPE) { 105 // no AOT's type related configuration is hit, pass the configuration to next compile mode 106 return false; 107 } 108 if (compileMode !== ESMODULE) { 109 faultHandler(`ArkTS:ERROR Aot's type mode must config compileMode with esmodule.`); 110 return true; 111 } 112 if (buildJsonInfo.apPath) { 113 faultHandler(`ArkTS:ERROR Aot's type mode do not need apPath.`); 114 return true; 115 } 116 // Aot compiler's type mode. 117 return true; 118} 119 120/** 121 * Check if the AOT related configuration is hit 122 * @param compileMode CompileMode for the project, which can be jsbundle or esmodule 123 * @param buildJsonInfo buildJsonInfo which parsed from projectConfig.aceBuildJson 124 * @param faultHandler faultHandler for illegal AOT configuration 125 * @returns {Boolean} false means no AOT related configuration found, else return true 126 * @api private 127 */ 128export function checkAotConfig(compileMode: string, buildJsonInfo: any, faultHandler: FaultHandler): boolean { 129 return checkAotTypeConfig(compileMode, buildJsonInfo, faultHandler) || 130 checkAotFullConfig(compileMode, buildJsonInfo, faultHandler) || 131 checkAotPartialConfig(compileMode, buildJsonInfo, faultHandler); 132} 133 134export function generateAot(arkDir: string, builtinAbcPath: string, projectConfig: any, logger: any, faultHandler: FaultHandler): void { 135 let aotCompiler: string = path.join(getBuildBinDir(arkDir), isWindows() ? "ark_aot_compiler.exe" : "ark_aot_compiler"); 136 const appAbc: string = path.join(projectConfig.buildPath, MODULES_ABC); 137 const appAot: string = path.join(projectConfig.anBuildOutPut, projectConfig.moduleName); 138 139 if (!validateFilePathLengths([aotCompiler, appAbc, builtinAbcPath, appAot], logger)) { 140 faultHandler(`ArkTS:ERROR generateAot failed. Invalid file path.`); 141 } 142 if (!fs.existsSync(appAbc)) { 143 faultHandler(`ArkTS:ERROR generateAot failed. AppAbc not found in "${appAbc}"`); 144 } 145 const singleCmdPrefix: string = `"${aotCompiler}" --builtins-dts="${builtinAbcPath}" ` + 146 `--aot-file="${appAot}" --target-triple=aarch64-unknown-linux-gnu `; 147 let singleCmd: string = ""; 148 if (projectConfig.anBuildMode === AOT_FULL) { 149 singleCmd = singleCmdPrefix + ` "${appAbc}"`; 150 } else if (projectConfig.anBuildMode === AOT_PARTIAL) { 151 const profile: string = projectConfig.apPath; 152 if (!validateFilePathLength(profile, logger)) { 153 faultHandler(`ArkTS:ERROR generateAot failed. Invalid profile file path.`); 154 } 155 if (!fs.existsSync(profile)) { 156 faultHandler(`ArkTS:ERROR generateAot failed. Partial mode lost profile in "${profile}"`); 157 } 158 singleCmd = singleCmdPrefix + ` --enable-pgo-profiler=true --pgo-profiler-path="${profile}" "${appAbc}"`; 159 } else { 160 faultHandler(`ArkTS:ERROR generateAot failed. unknown anBuildMode: ${projectConfig.anBuildMode}`); 161 } 162 try { 163 logger.debug(`generateAot cmd: ${singleCmd}`); 164 mkdirsSync(projectConfig.anBuildOutPut); 165 childProcess.execSync(singleCmd, { windowsHide: true }); 166 } catch (e) { 167 // Extract HostTool log information from hilog, which outputs to stdout. 168 let errorMessages: string[] = []; 169 let outStream: Buffer = e.stdout; 170 outStream.toString().split(os.EOL).forEach((stdLog: string) => { 171 if (!stdLog.includes(hostToolKeyWords)) { 172 return; 173 } 174 let logHeader: string = ''; 175 let logContent: string = ''; 176 [logHeader, logContent] = stdLog.split(hostToolKeyWords); 177 if (!logHeader && !logContent) { 178 return; 179 } 180 let logLevel: string = logHeader.trim().split(/\s+/)[logLevelIndex]; 181 if (logLevel === 'F' || logLevel === 'E') { 182 errorMessages.push(`ArkTS:ERROR: ${logContent}`); 183 } 184 }); 185 faultHandler(`ArkTS:ERROR Failed to generate aot file. Error message: ${e}${errorMessages.join(os.EOL)}`); 186 } 187} 188 189export function generateBuiltinAbc(arkDir: string, abcArgs: string[], cachePath: string, 190 logger: any, faultHandler: FaultHandler): string { 191 const builtinFilePath: string = path.join(getArkBuildDir(arkDir), "aot", "src", "lib_ark_builtins.d.ts"); 192 const builtinAbcPath: string = path.join(cachePath, TEMPORARY, "aot", "lib_ark_builtins.d.abc"); 193 if (fs.existsSync(builtinAbcPath)) { 194 logger.debug(`builtin.d.abc already exists, no need to rebuild again`); 195 return builtinAbcPath; 196 } 197 mkdirsSync(path.dirname(builtinAbcPath)); 198 if (!validateFilePathLengths([builtinFilePath, builtinAbcPath], logger)) { 199 faultHandler(`ArkTS:ERROR generateBuiltinAbc failed. Invalid file path.`); 200 } 201 const tempAbcArgs: string[] = abcArgs.slice(0); 202 let singleCmd: string = `${tempAbcArgs.join(' ')} "${toUnixPath(builtinFilePath)}" -q -b -m --merge-abc -o "${builtinAbcPath}"`; 203 try { 204 logger.debug(`generateBuiltinAbc cmd: ${singleCmd}`); 205 childProcess.execSync(singleCmd, { windowsHide: true }); 206 } catch (e) { 207 faultHandler(`ArkTS:ERROR Failed to generate builtin to abc. Error message: ${e}`); 208 } 209 return builtinAbcPath; 210} 211