• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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