• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 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 fs from 'fs';
17import * as path from 'path';
18import * as os from 'os';
19import * as childProcess from 'child_process';
20import * as process from 'process';
21import { logger } from './compile_info';
22import { projectConfig } from '../main';
23import {
24  FAIL,
25  FILESINFO_TXT,
26  MODULES_CACHE,
27  NPMENTRIES_TXT,
28  NODE_MODULES,
29  PACKAGES,
30  PATCH_SYMBOL_TABLE,
31  EXTNAME_PROTO_BIN
32} from './pre_define';
33import {
34  EntryInfo,
35  ModuleInfo,
36  initAbcEnv
37} from './gen_abc_plugin';
38import {
39  mkdirsSync,
40  toUnixPath,
41  validateFilePathLength
42} from './utils';
43
44const red: string = '\u001b[31m';
45const reset: string = '\u001b[39m';
46
47function generateCompileFilesInfo(moduleInfos: Array<ModuleInfo>) {
48  const tempModuleInfos: ModuleInfo[] = Array<ModuleInfo>();
49  moduleInfos.forEach((item) => {
50    let check: boolean = tempModuleInfos.every((newItem) => {
51      return item.tempFilePath !== newItem.tempFilePath;
52    });
53    if (check) {
54      tempModuleInfos.push(item);
55    }
56  });
57  moduleInfos = tempModuleInfos;
58
59  const filesInfoPath: string = path.join(process.env.cachePath, FILESINFO_TXT);
60  validateFilePathLength(filesInfoPath, logger);
61  let filesInfo: string = '';
62  moduleInfos.forEach(info => {
63    const moduleType: string = info.isCommonJs ? 'commonjs' : 'esm';
64    const sourceFile: string = info.filePath.replace(projectConfig.projectRootPath + path.sep, '');
65    filesInfo += `${info.tempFilePath};${info.recordName};${moduleType};${toUnixPath(sourceFile)};${info.packageName}\n`;
66  });
67  fs.writeFileSync(filesInfoPath, filesInfo, 'utf-8');
68}
69
70export function generateNpmEntriesInfo(entryInfos: Map<string, EntryInfo>) {
71  const npmEntriesInfoPath: string = path.join(process.env.cachePath, NPMENTRIES_TXT);
72  validateFilePathLength(npmEntriesInfoPath, logger);
73  let entriesInfo: string = '';
74  for (const value of entryInfos.values()) {
75    const buildPath: string =
76      value.buildPath.replace(toUnixPath(projectConfig.nodeModulesPath), '').replace(new RegExp(NODE_MODULES, 'g'), PACKAGES);
77    const entryFile: string = toUnixPath(path.join(buildPath, value.entry));
78    const entry: string = entryFile.substring(0, entryFile.lastIndexOf('.')).replace(new RegExp(NODE_MODULES, 'g'), PACKAGES);
79    entriesInfo +=
80      `${toUnixPath(path.join(PACKAGES, buildPath))}:${toUnixPath(path.join(PACKAGES, entry))}\n`;
81  }
82  fs.writeFileSync(npmEntriesInfoPath, entriesInfo, 'utf-8');
83}
84
85function generateAbcCacheFilesInfo(moduleInfos: Array<ModuleInfo>, npmEntriesInfoPath: string, cacheFilePath: string): void {
86  let abcCacheFilesInfo: string = '';
87
88  // generate source file cache
89  moduleInfos.forEach((info) => {
90    let tempFilePathWithoutExt: string = info.tempFilePath.substring(0, info.tempFilePath.lastIndexOf('.'));
91    let abcCacheFilePath: string = tempFilePathWithoutExt + EXTNAME_PROTO_BIN;
92    abcCacheFilesInfo += `${info.tempFilePath};${abcCacheFilePath}\n`;
93  });
94
95  // generate npm entries cache
96  let npmEntriesCacheFileWithoutExt: string = npmEntriesInfoPath.substring(0, npmEntriesInfoPath.lastIndexOf('.'));
97  let npmEntriesCacheFilePath: string = npmEntriesCacheFileWithoutExt + EXTNAME_PROTO_BIN;
98  abcCacheFilesInfo += `${npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`;
99
100  fs.writeFileSync(cacheFilePath, abcCacheFilesInfo, 'utf-8');
101}
102
103export function generateMergedAbc(moduleInfos: Array<ModuleInfo>, entryInfos: Map<string, EntryInfo>, outputABCPath: string) {
104  generateCompileFilesInfo(moduleInfos);
105  generateNpmEntriesInfo(entryInfos);
106
107  const filesInfoPath: string = path.join(process.env.cachePath, FILESINFO_TXT);
108  const npmEntriesInfoPath: string = path.join(process.env.cachePath, NPMENTRIES_TXT);
109  const cacheFilePath: string = path.join(process.env.cachePath, MODULES_CACHE);
110  validateFilePathLength(cacheFilePath, logger);
111  generateAbcCacheFilesInfo(moduleInfos, npmEntriesInfoPath, cacheFilePath);
112
113  const fileThreads = os.cpus().length < 16 ? os.cpus().length : 16;
114  mkdirsSync(projectConfig.buildPath);
115  let genAbcCmd: string =
116    `${initAbcEnv().join(' ')} "@${filesInfoPath}" --npm-module-entry-list "${npmEntriesInfoPath}" --output "${outputABCPath}" --file-threads "${fileThreads}"`;
117
118  projectConfig.patch = projectConfig.patch || false;
119  projectConfig.enableMap = projectConfig.enableMap || false;
120  projectConfig.inOldSymbolTablePath = projectConfig.inOldSymbolTablePath || projectConfig.projectRootPath;
121
122  if (projectConfig.patch) {
123    let oldHapSymbolTable = path.join(projectConfig.inOldSymbolTablePath, PATCH_SYMBOL_TABLE);
124    genAbcCmd += ` --input-symbol-table "${oldHapSymbolTable}" --generate-patch`;
125  }
126
127  if (!projectConfig.enableMap) {
128    genAbcCmd += ` --cache-file "@${cacheFilePath}"`;
129  } else {
130    // when generating map, cache is forbiden to avoid uncomplete symbol table
131    let oldHapSymbolTable = path.join(projectConfig.inOldSymbolTablePath, PATCH_SYMBOL_TABLE);
132    genAbcCmd += ` --dump-symbol-table "${oldHapSymbolTable}"`;
133  }
134
135  logger.debug('gen abc cmd is: ', genAbcCmd);
136  try {
137    if (process.env.watchMode === 'true') {
138      childProcess.execSync(genAbcCmd);
139    } else {
140      const child = childProcess.exec(genAbcCmd);
141      child.on('exit', (code: any) => {
142        if (code === 1) {
143          logger.debug(red, "ArkTS:ERROR failed to execute es2abc", reset);
144          process.exit(FAIL);
145        }
146      });
147
148      child.on('error', (err: any) => {
149        logger.debug(red, err.toString(), reset);
150        process.exit(FAIL);
151      });
152
153      child.stderr.on('data', (data: any) => {
154        if (projectConfig.patch) {
155          let patchErr :string[] =
156            data.split(os.EOL).filter(line => line.includes("[Patch]") || line.includes("Error:"));
157          logger.error(red, patchErr.join(os.EOL), reset);
158        } else {
159          logger.error(red, data.toString(), reset);
160        }
161      });
162    }
163  } catch (e) {
164    logger.debug(red, `ArkTS:ERROR failed to generate abc with filesInfo ${filesInfoPath}. Error message: ${e}`, reset);
165    process.env.abcCompileSuccess = 'false';
166    if (process.env.watchMode !== 'true') {
167      process.exit(FAIL);
168    }
169  }
170}
171