• 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    this.triggerAsync(() => {
315      fs.writeFile(this.sourceMapPath, JSON.stringify(this.cacheSourceMapObject, null, 2), 'utf-8', (err) => {
316        if (err) {
317          this.throwArkTsCompilerError('ArkTS:ERROR failed to write sourceMaps');
318        }
319        fs.copyFileSync(this.sourceMapPath, this.cacheSourceMapPath);
320        this.triggerEndSignal();
321      });
322    });
323  }
324
325  generateEs2AbcCmd() {
326    const fileThreads = getEs2abcFileThreadNumber();
327    this.cmdArgs.push(`"@${this.filesInfoPath}"`);
328    this.cmdArgs.push('--npm-module-entry-list');
329    this.cmdArgs.push(`"${this.npmEntriesInfoPath}"`);
330    this.cmdArgs.push('--output');
331    this.cmdArgs.push(`"${this.moduleAbcPath}"`);
332    this.cmdArgs.push('--file-threads');
333    this.cmdArgs.push(`"${fileThreads}"`);
334    this.cmdArgs.push('--merge-abc');
335  }
336
337  addCacheFileArgs() {
338    this.cmdArgs.push('--cache-file');
339    this.cmdArgs.push(`"@${this.cacheFilePath}"`);
340  }
341
342  private generateCompileFilesInfo() {
343    let filesInfo: string = '';
344    this.moduleInfos.forEach((info) => {
345      const moduleType: string = info.isCommonJs ? COMMONJS : ESM;
346      filesInfo += `${info.cacheFilePath};${info.recordName};${moduleType};${info.sourceFile};${info.packageName}\n`;
347    });
348    fs.writeFileSync(this.filesInfoPath, filesInfo, 'utf-8');
349  }
350
351  private generateNpmEntriesInfo() {
352    let entriesInfo: string = '';
353    for (const value of this.pkgEntryInfos.values()) {
354      entriesInfo += `${value.pkgEntryPath}:${value.pkgBuildPath}\n`;
355    }
356    fs.writeFileSync(this.npmEntriesInfoPath, entriesInfo, 'utf-8');
357  }
358
359  private generateAbcCacheFilesInfo(): void {
360    let abcCacheFilesInfo: string = '';
361
362    // generate source file cache
363    this.moduleInfos.forEach((info) => {
364      let abcCacheFilePath: string = changeFileExtension(info.cacheFilePath, EXTNAME_PROTO_BIN);
365      abcCacheFilesInfo += `${info.cacheFilePath};${abcCacheFilePath}\n`;
366    });
367
368    // generate npm entries cache
369    let npmEntriesCacheFilePath: string = changeFileExtension(this.npmEntriesInfoPath, EXTNAME_PROTO_BIN);
370    abcCacheFilesInfo += `${this.npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`;
371
372    fs.writeFileSync(this.cacheFilePath, abcCacheFilesInfo, 'utf-8');
373  }
374
375  private genDescriptionsForMergedEs2abc() {
376    this.generateCompileFilesInfo();
377    this.generateNpmEntriesInfo();
378    this.generateAbcCacheFilesInfo();
379  }
380
381  generateMergedAbcOfEs2Abc() {
382    // collect data error from subprocess
383    let errMsg: string = '';
384    this.genDescriptionsForMergedEs2abc();
385    const genAbcCmd: string = this.cmdArgs.join(' ');
386    try {
387      const child = this.triggerAsync(() => {
388        return childProcess.exec(genAbcCmd, { windowsHide: true });
389      });
390      child.on('exit', (code: any) => {
391        if (code === FAIL) {
392          this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc');
393        }
394        this.triggerEndSignal();
395      });
396
397      child.on('error', (err: any) => {
398        this.throwArkTsCompilerError(err.toString());
399      });
400
401      child.stderr.on('data', (data: any) => {
402        errMsg += data.toString();
403      });
404
405      child.stderr.on('end', (data: any) => {
406        if (errMsg !== undefined && errMsg.length > 0) {
407          this.logger.error(red, errMsg, reset);
408        }
409      });
410    } catch (e) {
411      this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc. Error message: ' + e.toString());
412    }
413  }
414
415  filterModulesByHashJson() {
416    if (this.hashJsonFilePath.length === 0 || !fs.existsSync(this.hashJsonFilePath)) {
417      for (const key of this.moduleInfos.keys()) {
418        this.filterModuleInfos.set(key, this.moduleInfos.get(key));
419      }
420      return;
421    }
422
423    let updatedJsonObject: any = {};
424    let jsonObject: any = {};
425    let jsonFile: string = '';
426
427    if (fs.existsSync(this.hashJsonFilePath)) {
428      jsonFile = fs.readFileSync(this.hashJsonFilePath).toString();
429      jsonObject = JSON.parse(jsonFile);
430      this.filterModuleInfos = new Map<string, ModuleInfo>();
431      for (const [key, value] of this.moduleInfos) {
432        const cacheFilePath: string = value.cacheFilePath;
433        const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN);
434        if (!fs.existsSync(cacheFilePath)) {
435          this.throwArkTsCompilerError(`ArkTS:ERROR ${cacheFilePath} is lost`);
436        }
437        if (fs.existsSync(cacheProtoFilePath)) {
438          const hashCacheFileContentData: any = toHashData(cacheFilePath);
439          const hashProtoFileContentData: any = toHashData(cacheProtoFilePath);
440          if (jsonObject[cacheFilePath] === hashCacheFileContentData &&
441            jsonObject[cacheProtoFilePath] === hashProtoFileContentData) {
442            updatedJsonObject[cacheFilePath] = cacheFilePath;
443            updatedJsonObject[cacheProtoFilePath] = cacheProtoFilePath;
444            continue;
445          }
446        }
447        this.filterModuleInfos.set(key, value);
448      }
449    }
450
451    this.hashJsonObject = updatedJsonObject;
452  }
453
454  getSplittedModulesByNumber() {
455    const result: any = [];
456    if (this.filterModuleInfos.size < this.workerNumber) {
457      for (const value of this.filterModuleInfos.values()) {
458        result.push([value]);
459      }
460      return result;
461    }
462
463    for (let i = 0; i < this.workerNumber; ++i) {
464      result.push([]);
465    }
466
467    let pos: number = 0;
468    for (const value of this.filterModuleInfos.values()) {
469      const chunk = pos % this.workerNumber;
470      result[chunk].push(value);
471      pos++;
472    }
473
474    return result;
475  }
476
477  invokeTs2AbcWorkersToGenProto(splittedModules) {
478    let ts2abcCmdArgs: string[] = this.cmdArgs.slice(0);
479    ts2abcCmdArgs.push('--output-proto');
480    ts2abcCmdArgs.push('--merge-abc');
481    ts2abcCmdArgs.push('--input-file');
482    if (isMasterOrPrimary()) {
483      this.setupCluster(cluster);
484      this.workerNumber = splittedModules.length;
485      for (let i = 0; i < this.workerNumber; ++i) {
486        const sn: number = i + 1;
487        const workerFileName: string = `${FILESINFO}_${sn}${EXTNAME_TXT}`;
488        const workerData: any = {
489          inputs: JSON.stringify(splittedModules[i]),
490          cmd: ts2abcCmdArgs.join(' '),
491          workerFileName: workerFileName,
492          mode: ESMODULE,
493          cachePath: this.projectConfig.cachePath
494        };
495        this.triggerAsync(() => {
496          const worker: any = cluster.fork(workerData);
497          worker.on('message', (errorMsg) => {
498            this.logger.error(red, errorMsg.data.toString(), reset);
499            this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc');
500          });
501        });
502      }
503    }
504  }
505
506  processTs2abcWorkersToGenAbc() {
507    this.generateNpmEntriesInfo();
508    let workerCount: number = 0;
509    if (isMasterOrPrimary()) {
510      cluster.on('exit', (worker, code, signal) => {
511        if (code === FAIL) {
512          this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc');
513        }
514        workerCount++;
515        if (workerCount === this.workerNumber) {
516          this.generateNpmEntryToGenProto();
517          this.generateProtoFilesInfo();
518          this.mergeProtoToAbc();
519          this.processAotIfNeeded();
520          this.afterCompilationProcess();
521        }
522        this.triggerEndSignal();
523      });
524      if (this.workerNumber == 0) {
525        // process aot for no source file changed.
526        this.processAotIfNeeded();
527      }
528    }
529  }
530
531  private processAotIfNeeded(): void {
532    if (!needAotCompiler(this.projectConfig)) {
533      return;
534    }
535    let faultHandler: FaultHandler = ((error: string) => { this.throwArkTsCompilerError(error); })
536    const builtinAbcPath: string = generateBuiltinAbc(this.arkConfig.arkRootPath, this.cmdArgs,
537      this.projectConfig.cachePath, this.logger, faultHandler);
538    generateAot(this.arkConfig.arkRootPath, builtinAbcPath, this.projectConfig, this.logger, faultHandler);
539  }
540
541  private genFileCachePath(filePath: string, projectRootPath: string, cachePath: string): string {
542    const sufStr: string = toUnixPath(filePath).replace(toUnixPath(projectRootPath), '');
543    const output: string = path.join(cachePath, sufStr);
544    return output;
545  }
546
547  private getPkgModulesFilePkgName(pkgPath: string) {
548    pkgPath = toUnixPath(pkgPath);
549    const packageDir: string = this.projectConfig.packageDir;
550    const projectRootPath = toUnixPath(this.projectConfig.projectRootPath);
551    const projectPkgModulesPath: string = toUnixPath(path.join(projectRootPath, packageDir));
552    let pkgName: string = '';
553    if (pkgPath.includes(projectPkgModulesPath)) {
554      pkgName = path.join(PACKAGES, pkgPath.replace(projectPkgModulesPath, ''));
555    } else {
556      for (const key in this.projectConfig.modulePathMap) {
557        const value: string = this.projectConfig.modulePathMap[key];
558        const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir));
559        if (pkgPath.indexOf(fakeModulePkgModulesPath) !== -1) {
560          const tempFilePath: string = pkgPath.replace(projectRootPath, '');
561          pkgName = path.join(`${PACKAGES}@${key}`,
562            tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1));
563          break;
564        }
565      }
566    }
567
568    return pkgName.replace(new RegExp(packageDir, 'g'), PACKAGES);
569  }
570
571  private generateProtoFilesInfo() {
572    validateFilePathLength(this.protoFilePath, this.logger);
573    mkdirsSync(path.dirname(this.protoFilePath));
574    let protoFilesInfo: string = '';
575    const sortModuleInfos: any = new Map([...this.moduleInfos].sort());
576    for (const value of sortModuleInfos.values()) {
577      const cacheProtoPath: string = changeFileExtension(value.cacheFilePath, EXTNAME_PROTO_BIN);
578      protoFilesInfo += `${toUnixPath(cacheProtoPath)}\n`;
579    }
580    if (this.pkgEntryInfos.size > 0) {
581      protoFilesInfo += `${toUnixPath(this.npmEntriesProtoFilePath)}\n`;
582    }
583    fs.writeFileSync(this.protoFilePath, protoFilesInfo, 'utf-8');
584  }
585
586  private mergeProtoToAbc() {
587    mkdirsSync(this.projectConfig.aceModuleBuild);
588    const cmd: any = `"${this.arkConfig.mergeAbcPath}" --input "@${this.protoFilePath}" --outputFilePath "${
589      this.projectConfig.aceModuleBuild}" --output ${MODULES_ABC} --suffix protoBin`;
590    try {
591      childProcess.execSync(cmd, { windowsHide: true });
592    } catch (e) {
593      this.throwArkTsCompilerError(`ArkTS:ERROR failed to merge proto file to abc, error message:` + e.toString());
594    }
595  }
596
597  private afterCompilationProcess() {
598    this.writeHashJson();
599  }
600
601  private writeHashJson() {
602    if (this.hashJsonFilePath.length === 0) {
603      return;
604    }
605
606    for (const value of this.filterModuleInfos.values()) {
607      const cacheFilePath: string = value.cacheFilePath;
608      const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN);
609      if (!fs.existsSync(cacheFilePath) || !fs.existsSync(cacheProtoFilePath)) {
610        this.throwArkTsCompilerError(
611          `ArkTS:ERROR ${cacheFilePath} or  ${cacheProtoFilePath} is lost`
612        );
613      }
614      const hashCacheFileContentData: any = toHashData(cacheFilePath);
615      const hashCacheProtoContentData: any = toHashData(cacheProtoFilePath);
616      this.hashJsonObject[cacheFilePath] = hashCacheFileContentData;
617      this.hashJsonObject[cacheProtoFilePath] = hashCacheProtoContentData;
618    }
619
620    fs.writeFileSync(this.hashJsonFilePath, JSON.stringify(this.hashJsonObject));
621  }
622
623  private generateNpmEntryToGenProto() {
624    if (this.pkgEntryInfos.size <= 0) {
625      return;
626    }
627    mkdirsSync(path.dirname(this.npmEntriesProtoFilePath));
628    const cmd: string = `"${this.arkConfig.js2abcPath}" --compile-npm-entries "${
629      this.npmEntriesInfoPath}" "${this.npmEntriesProtoFilePath}"`;
630    try {
631      childProcess.execSync(cmd, { windowsHide: true });
632    } catch (e) {
633      this.throwArkTsCompilerError(`ArkTS:ERROR failed to generate npm proto file to abc. Error message: ` + e.toString());
634    }
635  }
636
637  private removeCompilationCache(): void {
638    if (isEs2Abc(this.projectConfig)) {
639      this.removeEs2abcCompilationCache();
640    } else if (isTs2Abc(this.projectConfig)) {
641      this.removeTs2abcCompilationCache();
642    } else {
643      this.throwArkTsCompilerError(`Invalid projectConfig.pandaMode for module build, should be either
644      "${TS2ABC}" or "${ES2ABC}"`);
645    }
646  }
647
648  private removeEs2abcCompilationCache(): void {
649    if (fs.existsSync(this.cacheFilePath)) {
650      const data: string = fs.readFileSync(this.cacheFilePath, 'utf-8');
651      const lines: any = data.split(/\r?\n/);
652      lines.forEach(line => {
653        const [, abcCacheFilePath]: any = line.split(';');
654        if (fs.existsSync(abcCacheFilePath)) {
655          fs.unlinkSync(abcCacheFilePath);
656        }
657      });
658      fs.unlinkSync(this.cacheFilePath);
659    }
660  }
661
662  private removeTs2abcCompilationCache(): void {
663    if (fs.existsSync(this.hashJsonFilePath)) {
664      fs.unlinkSync(this.hashJsonFilePath);
665    }
666    if (fs.existsSync(this.protoFilePath)) {
667      const data: string = fs.readFileSync(this.protoFilePath, 'utf-8');
668      const lines: any = data.split(/\r?\n/);
669      lines.forEach(line => {
670        const protoFilePath: string = line;
671        if (fs.existsSync(protoFilePath)) {
672          fs.unlinkSync(protoFilePath);
673        }
674      });
675      fs.unlinkSync(this.protoFilePath);
676    }
677  }
678}
679