• 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 childProcess from 'child_process';
17import fs from 'fs';
18import path from 'path';
19import cluster from 'cluster';
20
21import {
22  COMMONJS,
23  ESM,
24  ESMODULE,
25  EXTNAME_CJS,
26  EXTNAME_ETS,
27  EXTNAME_JS,
28  EXTNAME_JSON,
29  EXTNAME_MJS,
30  EXTNAME_PROTO_BIN,
31  EXTNAME_TS,
32  EXTNAME_TXT,
33  FAIL,
34  FILESINFO,
35  FILESINFO_TXT,
36  MAX_WORKER_NUMBER,
37  MODULES_ABC,
38  MODULES_CACHE,
39  NPM_ENTRIES_PROTO_BIN,
40  NPMENTRIES_TXT,
41  OH_MODULES,
42  PACKAGES,
43  PROTO_FILESINFO_TXT,
44  PROTOS,
45  red,
46  reset,
47  SOURCEMAPS,
48  SOURCEMAPS_JSON,
49  WIDGETS_ABC,
50  TS2ABC,
51  ES2ABC
52} from '../common/ark_define';
53import {
54  needAotCompiler,
55  isMasterOrPrimary
56} from '../utils';
57import { CommonMode } from '../common/common_mode';
58import { newSourceMaps } from '../transform';
59import {
60  changeFileExtension,
61  getEs2abcFileThreadNumber,
62  isCommonJsPluginVirtualFile,
63  isCurrentProjectFiles
64} from '../utils';
65import {
66  isPackageModulesFile,
67  mkdirsSync,
68  toUnixPath,
69  toHashData,
70  validateFilePathLength
71} from '../../../utils';
72import {
73  getPackageInfo,
74  getOhmUrlByFilepath,
75  isTs2Abc,
76  isEs2Abc
77} from '../../../ark_utils';
78import {
79  generateAot,
80  generateBuiltinAbc,
81  FaultHandler
82} from '../../../gen_aot'
83
84export class ModuleInfo {
85  filePath: string;
86  cacheFilePath: string;
87  recordName: string;
88  isCommonJs: boolean;
89  sourceFile: string;
90  packageName: string;
91
92  constructor(filePath: string, cacheFilePath: string, isCommonJs: boolean, recordName: string, sourceFile: string,
93    packageName: string
94  ) {
95    this.filePath = filePath;
96    this.cacheFilePath = cacheFilePath;
97    this.recordName = recordName;
98    this.isCommonJs = isCommonJs;
99    this.sourceFile = sourceFile;
100    this.packageName = packageName;
101  }
102}
103
104export class PackageEntryInfo {
105  pkgEntryPath: string;
106  pkgBuildPath: string;
107  constructor(pkgEntryPath: string, pkgBuildPath: string) {
108    this.pkgEntryPath = pkgEntryPath;
109    this.pkgBuildPath = pkgBuildPath;
110  }
111}
112
113export class ModuleMode extends CommonMode {
114  moduleInfos: Map<String, ModuleInfo>;
115  pkgEntryInfos: Map<String, PackageEntryInfo>;
116  hashJsonObject: any;
117  cacheSourceMapObject: any;
118  filesInfoPath: string;
119  npmEntriesInfoPath: string;
120  moduleAbcPath: string;
121  sourceMapPath: string;
122  cacheFilePath: string;
123  cacheSourceMapPath: string;
124  workerNumber: number;
125  npmEntriesProtoFilePath: string;
126  protoFilePath: string;
127  filterModuleInfos: Map<String, ModuleInfo>;
128  symlinkMap: any;
129
130  constructor(rollupObject: any) {
131    super(rollupObject);
132    this.moduleInfos = new Map<String, ModuleInfo>();
133    this.pkgEntryInfos = new Map<String, PackageEntryInfo>();
134    this.hashJsonObject = {};
135    this.cacheSourceMapObject = {};
136    this.filesInfoPath = path.join(this.projectConfig.cachePath, FILESINFO_TXT);
137    this.npmEntriesInfoPath = path.join(this.projectConfig.cachePath, NPMENTRIES_TXT);
138    const outPutABC: string = this.projectConfig.widgetCompile ? WIDGETS_ABC : MODULES_ABC;
139    this.moduleAbcPath = path.join(this.projectConfig.aceModuleBuild, outPutABC);
140    this.sourceMapPath = this.arkConfig.isDebug ? path.join(this.projectConfig.aceModuleBuild, SOURCEMAPS) :
141      path.join(this.projectConfig.cachePath, SOURCEMAPS);
142    this.cacheFilePath = path.join(this.projectConfig.cachePath, MODULES_CACHE);
143    this.cacheSourceMapPath = path.join(this.projectConfig.cachePath, SOURCEMAPS_JSON);
144    this.workerNumber = MAX_WORKER_NUMBER;
145    this.npmEntriesProtoFilePath = path.join(this.projectConfig.cachePath, PROTOS, NPM_ENTRIES_PROTO_BIN);
146    this.protoFilePath = path.join(this.projectConfig.cachePath, PROTOS, PROTO_FILESINFO_TXT);
147    this.hashJsonObject = {};
148    this.filterModuleInfos = new Map<String, ModuleInfo>();
149    this.symlinkMap = rollupObject.share.symlinkMap;
150  }
151
152  prepareForCompilation(rollupObject: any): void {
153    this.collectModuleFileList(rollupObject, rollupObject.getModuleIds());
154    this.removeCacheInfo(rollupObject);
155  }
156
157  collectModuleFileList(module: any, fileList: IterableIterator<string>) {
158    let moduleInfos: Map<String, ModuleInfo> = new Map<String, ModuleInfo>();
159    let pkgEntryInfos: Map<String, PackageEntryInfo> = new Map<String, PackageEntryInfo>();
160    for (const moduleId of fileList) {
161      if (isCommonJsPluginVirtualFile(moduleId) || !isCurrentProjectFiles(moduleId, this.projectConfig)) {
162        continue;
163      }
164      const moduleInfo: any = module.getModuleInfo(moduleId);
165      if (moduleInfo['meta']['isNodeEntryFile']) {
166        this.getPackageEntryInfo(moduleId, moduleInfo['meta'], pkgEntryInfos);
167      }
168
169      this.processModuleInfos(moduleId, moduleInfos, moduleInfo['meta']);
170    }
171    this.moduleInfos = moduleInfos;
172    this.pkgEntryInfos = pkgEntryInfos;
173  }
174
175  private getPackageEntryInfo(filePath: string, metaInfo: any, pkgEntryInfos: Map<String, PackageEntryInfo>) {
176    if (metaInfo['isLocalDependency']) {
177      const hostModulesInfo: any = metaInfo['hostModulesInfo'];
178      const pkgBuildPath: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, metaInfo['moduleName']);
179      hostModulesInfo.forEach(hostModuleInfo => {
180        const hostDependencyName: string = hostModuleInfo['hostDependencyName'];
181        const hostModuleName: string = hostModuleInfo['hostModuleName'];
182        const pkgEntryPath: string = toUnixPath(path.join(`${PACKAGES}@${hostModuleName}`, hostDependencyName));
183        if (!pkgEntryInfos.has(pkgEntryPath)) {
184          pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath));
185        }
186      });
187      return;
188    }
189
190    if (!metaInfo['pkgPath']) {
191      this.logger.debug("Failed to get 'pkgPath' from metaInfo. File: ", filePath);
192      return;
193    }
194    const pkgPath: string = metaInfo['pkgPath'];
195    let originPkgEntryPath: string = toUnixPath(filePath.replace(pkgPath, ''));
196    if (originPkgEntryPath.startsWith('/')) {
197      originPkgEntryPath = originPkgEntryPath.slice(1, originPkgEntryPath.length);
198    }
199    const pkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(pkgPath));
200    let pkgBuildPath: string = path.join(pkgEntryPath, originPkgEntryPath);
201    pkgBuildPath = toUnixPath(pkgBuildPath.substring(0, pkgBuildPath.lastIndexOf('.')));
202    if (!pkgEntryInfos.has(pkgEntryPath)) {
203      pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath));
204    }
205    // create symlink path to actual path mapping in ohpm
206    if (this.projectConfig.packageDir == OH_MODULES && this.symlinkMap) {
207      const symlinkEntries: any = Object.entries(this.symlinkMap);
208      for (const [actualPath, symlinkPaths] of symlinkEntries) {
209        if (actualPath === pkgPath) {
210          (<string[]>symlinkPaths).forEach((symlink: string) => {
211            const symlinkPkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(symlink));
212            if (!pkgEntryInfos.has(symlinkPkgEntryPath)) {
213              pkgEntryInfos.set(symlinkPkgEntryPath, new PackageEntryInfo(symlinkPkgEntryPath, pkgEntryPath));
214            }
215          });
216          break;
217        }
218      }
219    }
220  }
221
222  private processModuleInfos(moduleId: string, moduleInfos: Map<String, ModuleInfo>, metaInfo?: any) {
223    switch (path.extname(moduleId)) {
224      case EXTNAME_ETS: {
225        const extName: string = this.projectConfig.processTs ? EXTNAME_TS : EXTNAME_JS;
226        this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos);
227        break;
228      }
229      case EXTNAME_TS: {
230        const extName: string = this.projectConfig.processTs ? '' : EXTNAME_JS;
231        this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos);
232        break;
233      }
234      case EXTNAME_JS:
235      case EXTNAME_MJS:
236      case EXTNAME_CJS: {
237        const extName: string = (moduleId.endsWith(EXTNAME_MJS) || moduleId.endsWith(EXTNAME_CJS)) ? EXTNAME_JS : '';
238        const isCommonJS: boolean = metaInfo && metaInfo['commonjs'] && metaInfo['commonjs']['isCommonJS'];
239        this.addModuleInfoItem(moduleId, isCommonJS, extName, metaInfo, moduleInfos);
240        break;
241      }
242      case EXTNAME_JSON: {
243        this.addModuleInfoItem(moduleId, false, '', metaInfo, moduleInfos);
244        break;
245      }
246      default:
247        break;
248    }
249  }
250
251  private addModuleInfoItem(filePath: string, isCommonJs: boolean, extName: string, metaInfo: any, moduleInfos: any) {
252    let namespace: string = metaInfo['moduleName'];
253    let recordName: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, namespace);
254    let sourceFile: string = filePath.replace(this.projectConfig.projectRootPath + path.sep, '');
255    let cacheFilePath: string =
256      this.genFileCachePath(filePath, this.projectConfig.projectRootPath, this.projectConfig.cachePath);
257    let packageName: string = '';
258    if (isPackageModulesFile(filePath, this.projectConfig)) {
259      packageName = this.getPkgModulesFilePkgName(metaInfo['pkgPath']);
260    } else {
261      packageName =
262        metaInfo['isLocalDependency'] ? namespace : getPackageInfo(this.projectConfig.aceModuleJsonPath)[1];
263    }
264
265    if (extName.length !== 0) {
266      cacheFilePath = changeFileExtension(cacheFilePath, extName);
267    }
268
269    cacheFilePath = toUnixPath(cacheFilePath);
270    recordName = toUnixPath(recordName);
271    sourceFile = toUnixPath(sourceFile);
272    packageName = toUnixPath(packageName);
273
274    moduleInfos.set(filePath, new ModuleInfo(filePath, cacheFilePath, isCommonJs, recordName, sourceFile, packageName));
275  }
276
277  updateCachedSourceMaps(): void {
278    if (!fs.existsSync(this.cacheSourceMapPath)) {
279      this.cacheSourceMapObject = newSourceMaps;
280      return;
281    }
282
283    this.cacheSourceMapObject = JSON.parse(fs.readFileSync(this.cacheSourceMapPath).toString());
284
285    // remove unused source files's sourceMap
286    let unusedFiles = [];
287    let compileFileList: Set<string> = new Set();
288    this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleId: string) => {
289      compileFileList.add(toUnixPath(moduleId));
290    })
291
292    Object.keys(this.cacheSourceMapObject).forEach(key => {
293      const sourceFileAbsolutePath: string = toUnixPath(path.join(this.projectConfig.projectRootPath, key));
294      if (!compileFileList.has(sourceFileAbsolutePath)) {
295        unusedFiles.push(key);
296      }
297    });
298    unusedFiles.forEach(file => {
299      delete this.cacheSourceMapObject[file];
300    })
301
302    // update sourceMap
303    Object.keys(newSourceMaps).forEach(key => {
304      this.cacheSourceMapObject[key] = newSourceMaps[key];
305    });
306  }
307
308  buildModuleSourceMapInfo() {
309    if (this.projectConfig.widgetCompile) {
310      return;
311    }
312
313    this.updateCachedSourceMaps();
314    fs.writeFile(this.sourceMapPath, JSON.stringify(this.cacheSourceMapObject, null, 2), 'utf-8', (err) => {
315      if (err) {
316        this.throwArkTsCompilerError('ArkTS:ERROR failed to write sourceMaps');
317      }
318      fs.copyFileSync(this.sourceMapPath, this.cacheSourceMapPath);
319    });
320  }
321
322  generateEs2AbcCmd() {
323    const fileThreads = getEs2abcFileThreadNumber();
324    this.cmdArgs.push(`"@${this.filesInfoPath}"`);
325    this.cmdArgs.push('--npm-module-entry-list');
326    this.cmdArgs.push(`"${this.npmEntriesInfoPath}"`);
327    this.cmdArgs.push('--output');
328    this.cmdArgs.push(`"${this.moduleAbcPath}"`);
329    this.cmdArgs.push('--file-threads');
330    this.cmdArgs.push(`"${fileThreads}"`);
331    this.cmdArgs.push('--merge-abc');
332  }
333
334  addCacheFileArgs() {
335    this.cmdArgs.push('--cache-file');
336    this.cmdArgs.push(`"@${this.cacheFilePath}"`);
337  }
338
339  private generateCompileFilesInfo() {
340    let filesInfo: string = '';
341    this.moduleInfos.forEach((info) => {
342      const moduleType: string = info.isCommonJs ? COMMONJS : ESM;
343      filesInfo += `${info.cacheFilePath};${info.recordName};${moduleType};${info.sourceFile};${info.packageName}\n`;
344    });
345    fs.writeFileSync(this.filesInfoPath, filesInfo, 'utf-8');
346  }
347
348  private generateNpmEntriesInfo() {
349    let entriesInfo: string = '';
350    for (const value of this.pkgEntryInfos.values()) {
351      entriesInfo += `${value.pkgEntryPath}:${value.pkgBuildPath}\n`;
352    }
353    fs.writeFileSync(this.npmEntriesInfoPath, entriesInfo, 'utf-8');
354  }
355
356  private generateAbcCacheFilesInfo(): void {
357    let abcCacheFilesInfo: string = '';
358
359    // generate source file cache
360    this.moduleInfos.forEach((info) => {
361      let abcCacheFilePath: string = changeFileExtension(info.cacheFilePath, EXTNAME_PROTO_BIN);
362      abcCacheFilesInfo += `${info.cacheFilePath};${abcCacheFilePath}\n`;
363    });
364
365    // generate npm entries cache
366    let npmEntriesCacheFilePath: string = changeFileExtension(this.npmEntriesInfoPath, EXTNAME_PROTO_BIN);
367    abcCacheFilesInfo += `${this.npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`;
368
369    fs.writeFileSync(this.cacheFilePath, abcCacheFilesInfo, 'utf-8');
370  }
371
372  private genDescriptionsForMergedEs2abc() {
373    this.generateCompileFilesInfo();
374    this.generateNpmEntriesInfo();
375    this.generateAbcCacheFilesInfo();
376  }
377
378  generateMergedAbcOfEs2Abc() {
379    // collect data error from subprocess
380    let errMsg: string = '';
381    this.genDescriptionsForMergedEs2abc();
382    const genAbcCmd: string = this.cmdArgs.join(' ');
383    try {
384      const child = this.triggerAsync(() => {
385        return childProcess.exec(genAbcCmd, { windowsHide: true });
386      });
387      child.on('exit', (code: any) => {
388        if (code === FAIL) {
389          this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc');
390        }
391        this.triggerEndSignal();
392      });
393
394      child.on('error', (err: any) => {
395        this.throwArkTsCompilerError(err.toString());
396      });
397
398      child.stderr.on('data', (data: any) => {
399        errMsg += data.toString();
400      });
401
402      child.stderr.on('end', (data: any) => {
403        if (errMsg !== undefined && errMsg.length > 0) {
404          this.logger.error(red, errMsg, reset);
405        }
406      });
407    } catch (e) {
408      this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc. Error message: ' + e.toString());
409    }
410  }
411
412  filterModulesByHashJson() {
413    if (this.hashJsonFilePath.length === 0 || !fs.existsSync(this.hashJsonFilePath)) {
414      for (const key of this.moduleInfos.keys()) {
415        this.filterModuleInfos.set(key, this.moduleInfos.get(key));
416      }
417      return;
418    }
419
420    let updatedJsonObject: any = {};
421    let jsonObject: any = {};
422    let jsonFile: string = '';
423
424    if (fs.existsSync(this.hashJsonFilePath)) {
425      jsonFile = fs.readFileSync(this.hashJsonFilePath).toString();
426      jsonObject = JSON.parse(jsonFile);
427      this.filterModuleInfos = new Map<string, ModuleInfo>();
428      for (const [key, value] of this.moduleInfos) {
429        const cacheFilePath: string = value.cacheFilePath;
430        const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN);
431        if (!fs.existsSync(cacheFilePath)) {
432          this.throwArkTsCompilerError(`ArkTS:ERROR ${cacheFilePath} is lost`);
433        }
434        if (fs.existsSync(cacheProtoFilePath)) {
435          const hashCacheFileContentData: any = toHashData(cacheFilePath);
436          const hashProtoFileContentData: any = toHashData(cacheProtoFilePath);
437          if (jsonObject[cacheFilePath] === hashCacheFileContentData &&
438            jsonObject[cacheProtoFilePath] === hashProtoFileContentData) {
439            updatedJsonObject[cacheFilePath] = cacheFilePath;
440            updatedJsonObject[cacheProtoFilePath] = cacheProtoFilePath;
441            continue;
442          }
443        }
444        this.filterModuleInfos.set(key, value);
445      }
446    }
447
448    this.hashJsonObject = updatedJsonObject;
449  }
450
451  getSplittedModulesByNumber() {
452    const result: any = [];
453    if (this.filterModuleInfos.size < this.workerNumber) {
454      for (const value of this.filterModuleInfos.values()) {
455        result.push([value]);
456      }
457      return result;
458    }
459
460    for (let i = 0; i < this.workerNumber; ++i) {
461      result.push([]);
462    }
463
464    let pos: number = 0;
465    for (const value of this.filterModuleInfos.values()) {
466      const chunk = pos % this.workerNumber;
467      result[chunk].push(value);
468      pos++;
469    }
470
471    return result;
472  }
473
474  invokeTs2AbcWorkersToGenProto(splittedModules) {
475    let ts2abcCmdArgs: string[] = this.cmdArgs.slice(0);
476    ts2abcCmdArgs.push('--output-proto');
477    ts2abcCmdArgs.push('--merge-abc');
478    ts2abcCmdArgs.push('--input-file');
479    if (isMasterOrPrimary()) {
480      this.setupCluster(cluster);
481      this.workerNumber = splittedModules.length;
482      for (let i = 0; i < this.workerNumber; ++i) {
483        const sn: number = i + 1;
484        const workerFileName: string = `${FILESINFO}_${sn}${EXTNAME_TXT}`;
485        const workerData: any = {
486          inputs: JSON.stringify(splittedModules[i]),
487          cmd: ts2abcCmdArgs.join(' '),
488          workerFileName: workerFileName,
489          mode: ESMODULE,
490          cachePath: this.projectConfig.cachePath
491        };
492        this.triggerAsync(() => {
493          const worker: any = cluster.fork(workerData);
494          worker.on('message', (errorMsg) => {
495            this.logger.error(red, errorMsg.data.toString(), reset);
496            this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc');
497          });
498        });
499      }
500    }
501  }
502
503  processTs2abcWorkersToGenAbc() {
504    this.generateNpmEntriesInfo();
505    let workerCount: number = 0;
506    if (isMasterOrPrimary()) {
507      cluster.on('exit', (worker, code, signal) => {
508        if (code === FAIL) {
509          this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc');
510        }
511        workerCount++;
512        if (workerCount === this.workerNumber) {
513          this.generateNpmEntryToGenProto();
514          this.generateProtoFilesInfo();
515          this.mergeProtoToAbc();
516          this.processAotIfNeeded();
517          this.afterCompilationProcess();
518        }
519        this.triggerEndSignal();
520      });
521      if (this.workerNumber == 0) {
522        // process aot for no source file changed.
523        this.processAotIfNeeded();
524      }
525    }
526  }
527
528  private processAotIfNeeded(): void {
529    if (!needAotCompiler(this.projectConfig)) {
530      return;
531    }
532    let faultHandler: FaultHandler = ((error: string) => { this.throwArkTsCompilerError(error); })
533    const builtinAbcPath: string = generateBuiltinAbc(this.arkConfig.arkRootPath, this.cmdArgs,
534      this.projectConfig.cachePath, this.logger, faultHandler);
535    generateAot(this.arkConfig.arkRootPath, builtinAbcPath, this.projectConfig, this.logger, faultHandler);
536  }
537
538  private genFileCachePath(filePath: string, projectRootPath: string, cachePath: string): string {
539    const sufStr: string = toUnixPath(filePath).replace(toUnixPath(projectRootPath), '');
540    const output: string = path.join(cachePath, sufStr);
541    return output;
542  }
543
544  private getPkgModulesFilePkgName(pkgPath: string) {
545    pkgPath = toUnixPath(pkgPath);
546    const packageDir: string = this.projectConfig.packageDir;
547    const projectRootPath = toUnixPath(this.projectConfig.projectRootPath);
548    const projectPkgModulesPath: string = toUnixPath(path.join(projectRootPath, packageDir));
549    let pkgName: string = '';
550    if (pkgPath.includes(projectPkgModulesPath)) {
551      pkgName = path.join(PACKAGES, pkgPath.replace(projectPkgModulesPath, ''));
552    } else {
553      for (const key in this.projectConfig.modulePathMap) {
554        const value: string = this.projectConfig.modulePathMap[key];
555        const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir));
556        if (pkgPath.indexOf(fakeModulePkgModulesPath) !== -1) {
557          const tempFilePath: string = pkgPath.replace(projectRootPath, '');
558          pkgName = path.join(`${PACKAGES}@${key}`,
559            tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1));
560          break;
561        }
562      }
563    }
564
565    return pkgName.replace(new RegExp(packageDir, 'g'), PACKAGES);
566  }
567
568  private generateProtoFilesInfo() {
569    validateFilePathLength(this.protoFilePath, this.logger);
570    mkdirsSync(path.dirname(this.protoFilePath));
571    let protoFilesInfo: string = '';
572    const sortModuleInfos: any = new Map([...this.moduleInfos].sort());
573    for (const value of sortModuleInfos.values()) {
574      const cacheProtoPath: string = changeFileExtension(value.cacheFilePath, EXTNAME_PROTO_BIN);
575      protoFilesInfo += `${toUnixPath(cacheProtoPath)}\n`;
576    }
577    if (this.pkgEntryInfos.size > 0) {
578      protoFilesInfo += `${toUnixPath(this.npmEntriesProtoFilePath)}\n`;
579    }
580    fs.writeFileSync(this.protoFilePath, protoFilesInfo, 'utf-8');
581  }
582
583  private mergeProtoToAbc() {
584    mkdirsSync(this.projectConfig.aceModuleBuild);
585    const cmd: any = `"${this.arkConfig.mergeAbcPath}" --input "@${this.protoFilePath}" --outputFilePath "${
586      this.projectConfig.aceModuleBuild}" --output ${MODULES_ABC} --suffix protoBin`;
587    try {
588      childProcess.execSync(cmd, { windowsHide: true });
589    } catch (e) {
590      this.throwArkTsCompilerError(`ArkTS:ERROR failed to merge proto file to abc, error message:` + e.toString());
591    }
592  }
593
594  private afterCompilationProcess() {
595    this.writeHashJson();
596  }
597
598  private writeHashJson() {
599    if (this.hashJsonFilePath.length === 0) {
600      return;
601    }
602
603    for (const value of this.filterModuleInfos.values()) {
604      const cacheFilePath: string = value.cacheFilePath;
605      const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN);
606      if (!fs.existsSync(cacheFilePath) || !fs.existsSync(cacheProtoFilePath)) {
607        this.throwArkTsCompilerError(
608          `ArkTS:ERROR ${cacheFilePath} or  ${cacheProtoFilePath} is lost`
609        );
610      }
611      const hashCacheFileContentData: any = toHashData(cacheFilePath);
612      const hashCacheProtoContentData: any = toHashData(cacheProtoFilePath);
613      this.hashJsonObject[cacheFilePath] = hashCacheFileContentData;
614      this.hashJsonObject[cacheProtoFilePath] = hashCacheProtoContentData;
615    }
616
617    fs.writeFileSync(this.hashJsonFilePath, JSON.stringify(this.hashJsonObject));
618  }
619
620  private generateNpmEntryToGenProto() {
621    if (this.pkgEntryInfos.size <= 0) {
622      return;
623    }
624    mkdirsSync(path.dirname(this.npmEntriesProtoFilePath));
625    const cmd: string = `"${this.arkConfig.js2abcPath}" --compile-npm-entries "${
626      this.npmEntriesInfoPath}" "${this.npmEntriesProtoFilePath}"`;
627    try {
628      childProcess.execSync(cmd, { windowsHide: true });
629    } catch (e) {
630      this.throwArkTsCompilerError(`ArkTS:ERROR failed to generate npm proto file to abc. Error message: ` + e.toString());
631    }
632  }
633
634  private removeCompilationCache(): void {
635    if (isEs2Abc(this.projectConfig)) {
636      this.removeEs2abcCompilationCache();
637    } else if (isTs2Abc(this.projectConfig)) {
638      this.removeTs2abcCompilationCache();
639    } else {
640      this.throwArkTsCompilerError(`Invalid projectConfig.pandaMode for module build, should be either
641      "${TS2ABC}" or "${ES2ABC}"`);
642    }
643  }
644
645  private removeEs2abcCompilationCache(): void {
646    if (fs.existsSync(this.cacheFilePath)) {
647      const data: string = fs.readFileSync(this.cacheFilePath, 'utf-8');
648      const lines: any = data.split(/\r?\n/);
649      lines.forEach(line => {
650        const [, abcCacheFilePath]: any = line.split(';');
651        if (fs.existsSync(abcCacheFilePath)) {
652          fs.unlinkSync(abcCacheFilePath);
653        }
654      });
655      fs.unlinkSync(this.cacheFilePath);
656    }
657  }
658
659  private removeTs2abcCompilationCache(): void {
660    if (fs.existsSync(this.hashJsonFilePath)) {
661      fs.unlinkSync(this.hashJsonFilePath);
662    }
663    if (fs.existsSync(this.protoFilePath)) {
664      const data: string = fs.readFileSync(this.protoFilePath, 'utf-8');
665      const lines: any = data.split(/\r?\n/);
666      lines.forEach(line => {
667        const protoFilePath: string = line;
668        if (fs.existsSync(protoFilePath)) {
669          fs.unlinkSync(protoFilePath);
670        }
671      });
672      fs.unlinkSync(this.protoFilePath);
673    }
674  }
675}
676