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, 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, 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}" ` +
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