• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 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 cluster from 'cluster';
19import process from 'process';
20import os from 'os';
21import events from 'events';
22import Compiler from 'webpack/lib/Compiler';
23import { logger } from './compile_info';
24import * as childProcess from 'child_process';
25import {
26  toUnixPath,
27  toHashData,
28  mkdirsSync,
29  nodeLargeOrEqualTargetVersion,
30  removeDir,
31  validateFilePathLength,
32  unlinkSync,
33  isPackageModulesFile,
34  genTemporaryPath
35} from './utils';
36import {
37  buildCachePath,
38  genAbcFileName,
39  genBuildPath,
40  genMergeProtoFileName,
41  genProtoFileName,
42  getOhmUrlByFilepath,
43  getPackageInfo,
44  isEs2Abc,
45  isTs2Abc,
46  newSourceMaps,
47  removeDuplicateInfo
48} from './ark_utils';
49import { projectConfig } from '../main';
50import {
51  ESMODULE,
52  JSBUNDLE,
53  NODE_MODULES,
54  ES2ABC,
55  EXTNAME_D_ETS,
56  EXTNAME_D_TS,
57  EXTNAME_ETS,
58  EXTNAME_JS,
59  EXTNAME_TS,
60  EXTNAME_MJS,
61  EXTNAME_CJS,
62  EXTNAME_JSON,
63  EXTNAME_JS_MAP,
64  FAIL,
65  MODULELIST_JSON,
66  MODULES_ABC,
67  PREBUILDINFO_JSON,
68  SUCCESS,
69  SOURCEMAPS_JSON,
70  SOURCEMAPS,
71  TEMPORARY,
72  TS2ABC,
73  PROTO_FILESINFO_TXT,
74  NPMENTRIES_TXT,
75  EXTNAME_PROTO_BIN,
76  FILESINFO_TXT,
77  MANAGE_WORKERS_SCRIPT,
78  MAX_WORKER_NUMBER,
79  GEN_ABC_SCRIPT,
80  GEN_MODULE_ABC_SCRIPT,
81  AOT_FULL,
82  AOT_PARTIAL,
83  PACKAGES
84} from './pre_define';
85import {
86  generateMergedAbc,
87  generateNpmEntriesInfo
88} from './gen_merged_abc';
89import {
90  generateAot,
91  FaultHandler
92} from './gen_aot'
93
94let output: string;
95let isWin: boolean = false;
96let isMac: boolean = false;
97let isDebug: boolean = false;
98let arkDir: string;
99let nodeJs: string;
100
101interface File {
102  path: string,
103  size: number,
104  cacheOutputPath: string,
105  sourceFile: string
106}
107let intermediateJsBundle: Array<File> = [];
108let fileterIntermediateJsBundle: Array<File> = [];
109let moduleInfos: Array<ModuleInfo> = [];
110let filterModuleInfos: Array<ModuleInfo> = [];
111let commonJsModuleInfos: Array<ModuleInfo> = [];
112let ESMModuleInfos: Array<ModuleInfo> = [];
113let entryInfos: Map<string, EntryInfo> = new Map<string, EntryInfo>();
114let hashJsonObject = {};
115let moduleHashJsonObject = {};
116let buildPathInfo: string = '';
117let buildMapFileList: Set<string> = new Set<string>();
118let isHotReloadFirstBuild: boolean = true;
119let protoFilePath: string = '';
120
121const red: string = '\u001b[31m';
122const reset: string = '\u001b[39m';
123const blue = '\u001b[34m';
124const hashFile: string = 'gen_hash.json';
125const ARK: string = '/ark/';
126
127export class ModuleInfo {
128  filePath: string;
129  tempFilePath: string;
130  buildFilePath: string;
131  abcFilePath: string;
132  isCommonJs: boolean;
133  recordName: string;
134  sourceFile: string;
135  packageName: string;
136
137  constructor(filePath: string, tempFilePath: string, buildFilePath: string,
138              abcFilePath: string, packageName: string, isCommonJs: boolean) {
139    this.filePath = filePath;
140    this.tempFilePath = tempFilePath;
141    this.buildFilePath = buildFilePath;
142    this.abcFilePath = abcFilePath;
143    this.packageName = packageName;
144    this.isCommonJs = isCommonJs;
145    this.recordName = getOhmUrlByFilepath(filePath, projectConfig, logger);
146    this.sourceFile = filePath.replace(projectConfig.projectRootPath + path.sep, '');
147  }
148}
149
150export class EntryInfo {
151  npmInfo: string;
152  buildPath: string;
153  entry: string;
154
155  constructor(npmInfo: string, buildPath: string, entry: string) {
156    this.npmInfo = npmInfo;
157    this.buildPath = buildPath;
158    this.entry = entry;
159  }
160}
161
162export class GenAbcPlugin {
163  constructor(output_, arkDir_, nodeJs_, isDebug_) {
164    output = output_;
165    arkDir = arkDir_;
166    nodeJs = nodeJs_;
167    isDebug = isDebug_;
168  }
169  apply(compiler: Compiler) {
170    if (fs.existsSync(path.resolve(arkDir, 'build-win'))) {
171      isWin = true;
172    } else {
173      if (fs.existsSync(path.resolve(arkDir, 'build-mac'))) {
174        isMac = true;
175      } else {
176        if (!fs.existsSync(path.resolve(arkDir, 'build'))) {
177          logger.error(red, 'ArkTS:ERROR find build fail', reset);
178          process.exitCode = FAIL;
179          return;
180        }
181      }
182    }
183
184    if (!checkNodeModules()) {
185      process.exitCode = FAIL;
186      return;
187    }
188
189    if (projectConfig.compileMode === ESMODULE) {
190      if (projectConfig.cachePath && !projectConfig.xtsMode) {
191        let cachedJson: Object = {};
192        const cachePrebuildInfoPath: string = path.join(projectConfig.cachePath, PREBUILDINFO_JSON);
193        validateFilePathLength(cachePrebuildInfoPath, logger);
194        cachedJson.buildMode = projectConfig.buildArkMode;
195        cachedJson.bundleName = projectConfig.bundleName;
196        cachedJson.moduleName = projectConfig.moduleName;
197        fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8',
198          (err) => {
199            if (err) {
200              logger.error(red, `ArkTS:ERROR Failed to write module build info.`, reset);
201            }
202          }
203        );
204      }
205
206      // clear output dir
207      removeDir(output);
208      removeDir(projectConfig.nodeModulesPath);
209    }
210
211    if (projectConfig.compileMode === JSBUNDLE && process.env.minPlatformVersion) {
212      if (projectConfig.cachePath && !projectConfig.xtsMode) {
213        let cachedJson: Object = {};
214        const cachePrebuildInfoPath: string = path.join(projectConfig.cachePath, PREBUILDINFO_JSON);
215        validateFilePathLength(cachePrebuildInfoPath, logger);
216        cachedJson.minAPIVersion = process.env.minPlatformVersion;
217        fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8',
218          (err) => {
219            if (err) {
220              logger.error(red, `ArkTS:ERROR Failed to write bundle build info.`, reset);
221            }
222          }
223        );
224      }
225    }
226
227    // for preview mode max listeners
228    events.EventEmitter.defaultMaxListeners = 100;
229
230    compiler.hooks.compilation.tap('GenAbcPlugin', (compilation) => {
231      if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
232        return;
233      }
234      buildPathInfo = output;
235      compilation.hooks.finishModules.tap('finishModules', handleFinishModules.bind(this));
236    });
237
238    compiler.hooks.compilation.tap('GenAbcPlugin', (compilation) => {
239      compilation.hooks.processAssets.tap('processAssets', (assets) => {
240        if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
241          return;
242        }
243        Object.keys(compilation.assets).forEach(key => {
244          if (path.extname(key) === EXTNAME_JS || path.extname(key) === EXTNAME_JS_MAP) {
245            delete assets[key];
246          }
247        });
248      });
249    });
250
251    compiler.hooks.emit.tap('GenAbcPlugin', (compilation) => {
252      if (projectConfig.compileMode === ESMODULE) {
253        return;
254      }
255      Object.keys(compilation.assets).forEach(key => {
256        // choose *.js
257        if (output && path.extname(key) === EXTNAME_JS) {
258          const newContent: string = compilation.assets[key].source();
259          const keyPath: string = key.replace(/\.js$/, ".temp.js");
260          writeFileSync(newContent, output, keyPath, key);
261        }
262      });
263    });
264
265    compiler.hooks.afterEmit.tap('GenAbcPluginMultiThread', () => {
266      if (projectConfig.compileMode === ESMODULE) {
267        return;
268      }
269      if (intermediateJsBundle.length === 0) {
270        return;
271      }
272      buildPathInfo = output;
273      if (isTs2Abc(projectConfig) || process.env.minPlatformVersion === "8") {
274        invokeWorkersToGenAbc();
275      } else if (isEs2Abc(projectConfig)){
276        generateAbcByEs2AbcOfBundleMode(intermediateJsBundle);
277      } else {
278        logger.error(red, `ArkTS:ERROR please set panda module`, reset);
279      }
280    });
281  }
282}
283
284function clearGlobalInfo() {
285  // fix bug of multi trigger
286  if (process.env.watchMode !== 'true') {
287    intermediateJsBundle = [];
288    moduleInfos = [];
289    entryInfos = new Map<string, EntryInfo>();
290  }
291  fileterIntermediateJsBundle = [];
292  filterModuleInfos = [];
293  commonJsModuleInfos = [];
294  ESMModuleInfos = [];
295  hashJsonObject = {};
296  moduleHashJsonObject = {};
297  buildMapFileList = new Set<string>();
298}
299
300function getEntryInfo(filePath: string, resourceResolveData: Object): string {
301  if (!resourceResolveData.descriptionFilePath) {
302    return;
303  }
304
305  let isEntry: boolean = false;
306  let mainFileds: Set<string> = getEntryCandidatesFromPackageJson(resourceResolveData);
307  for (let value of mainFileds.values()) {
308    if (toUnixPath(filePath) === value) {
309      isEntry = true;
310      break;
311    }
312  }
313  const packageJsonPath: string = resourceResolveData.descriptionFilePath;
314  let npmInfoPath: string = path.resolve(packageJsonPath, '..');
315
316  let entry: string = toUnixPath(filePath.replace(npmInfoPath, ''));
317  if (entry.startsWith('/')) {
318    entry = entry.slice(1, entry.length);
319  }
320
321  const fakeEntryPath: string = path.resolve(npmInfoPath, 'fake.js');
322  const tempFakeEntryPath: string = genTemporaryPath(fakeEntryPath, projectConfig.projectPath, process.env.cachePath,
323    projectConfig);
324  const buildFakeEntryPath: string = genBuildPath(fakeEntryPath, projectConfig.projectPath, projectConfig.buildPath,
325    projectConfig);
326  npmInfoPath = toUnixPath(path.resolve(tempFakeEntryPath, '..'));
327  const buildNpmInfoPath: string = toUnixPath(path.resolve(buildFakeEntryPath, '..'));
328  if (!entryInfos.has(npmInfoPath) && isEntry) {
329    const entryInfo: EntryInfo = new EntryInfo(npmInfoPath, buildNpmInfoPath, entry);
330    entryInfos.set(npmInfoPath, entryInfo);
331  }
332
333  return buildNpmInfoPath;
334}
335
336function getEntryCandidatesFromPackageJson(resourceResolveData: Object): Set<string> {
337  let descriptionFileData: Object = resourceResolveData.descriptionFileData;
338  let packagePath: string = path.resolve(resourceResolveData.descriptionFilePath, '..');
339  let mainFileds: Set<string> = new Set<string>();
340  if (descriptionFileData.browser) {
341    if (typeof descriptionFileData.browser === 'string') {
342      mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.browser)));
343    } else {
344      Object.keys(descriptionFileData.browser).forEach(key => {
345        if (typeof key === 'string' && typeof descriptionFileData.browser[key] === 'string') {
346          mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.browser[key])));
347        }
348      });
349    }
350  }
351  if (descriptionFileData.module) {
352    mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.module)));
353  }
354  if (descriptionFileData.main) {
355    mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.main)));
356  }
357  if (mainFileds.size === 0) {
358    mainFileds.add(toUnixPath(path.join(packagePath, 'index.js')));
359    mainFileds.add(toUnixPath(path.join(packagePath, 'index.ets')));
360    mainFileds.add(toUnixPath(path.join(packagePath, 'index.ts')));
361  }
362
363  return mainFileds;
364}
365
366function processNodeModulesFile(filePath: string, tempFilePath: string, buildFilePath: string,
367  abcFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
368  let npmPkgPath: string = getEntryInfo(filePath, module.resourceResolveData);
369  const buildNpmPkgPath: string = npmPkgPath.replace(toUnixPath(projectConfig.nodeModulesPath), '');
370  const npmPkgName: string = toUnixPath(path.join(PACKAGES, buildNpmPkgPath)).replace(new RegExp(NODE_MODULES, 'g'), PACKAGES);
371
372  const descriptionFileData: Object = module.resourceResolveData.descriptionFileData;
373  if (descriptionFileData && descriptionFileData['type'] && descriptionFileData['type'] === 'module') {
374    const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, false);
375    moduleInfos.push(tempModuleInfo);
376    nodeModulesFile.push(tempFilePath);
377  } else if (filePath.endsWith(EXTNAME_MJS)) {
378    const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, false);
379    moduleInfos.push(tempModuleInfo);
380    nodeModulesFile.push(tempFilePath);
381  } else {
382    const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, true);
383    moduleInfos.push(tempModuleInfo);
384    nodeModulesFile.push(tempFilePath);
385  }
386  if (!filePath.endsWith(EXTNAME_JSON)) {
387    buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
388  }
389}
390
391function processEtsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
392  // skip declaration modules
393  if (filePath.endsWith(EXTNAME_D_ETS)) {
394    return;
395  }
396  if (projectConfig.processTs === true) {
397    tempFilePath = tempFilePath.replace(/\.ets$/, EXTNAME_TS);
398    buildFilePath = buildFilePath.replace(/\.ets$/, EXTNAME_TS);
399  } else {
400    tempFilePath = tempFilePath.replace(/\.ets$/, EXTNAME_JS);
401    buildFilePath = buildFilePath.replace(/\.ets$/, EXTNAME_JS);
402  }
403  const abcFilePath: string = genAbcFileName(tempFilePath);
404  if (isPackageModulesFile(filePath, projectConfig)) {
405    processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
406  } else {
407    const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
408    const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
409    moduleInfos.push(tempModuleInfo);
410  }
411  buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
412}
413
414function processTsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
415  // skip declaration modules
416  if (filePath.endsWith(EXTNAME_D_TS)) {
417    return;
418  }
419  if (projectConfig.processTs === false) {
420    tempFilePath = tempFilePath.replace(/\.ts$/, EXTNAME_JS);
421    buildFilePath = buildFilePath.replace(/\.ts$/, EXTNAME_JS);
422  }
423  const abcFilePath: string = genAbcFileName(tempFilePath);
424  if (isPackageModulesFile(filePath, projectConfig)) {
425    processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
426  } else {
427    const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
428    const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
429    moduleInfos.push(tempModuleInfo);
430  }
431  buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
432}
433
434function processJsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
435  const parent: string = path.join(tempFilePath, '..');
436  if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
437    mkDir(parent);
438  }
439  if (filePath.endsWith(EXTNAME_MJS) || filePath.endsWith(EXTNAME_CJS)) {
440    fs.copyFileSync(filePath, tempFilePath);
441  }
442  const abcFilePath: string = genAbcFileName(tempFilePath);
443  if (isPackageModulesFile(filePath, projectConfig)) {
444    processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
445  } else {
446    const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
447    const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
448    moduleInfos.push(tempModuleInfo);
449  }
450  buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
451}
452
453function processJsonModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
454  const abcFilePath: string = "NA";
455  if (isPackageModulesFile(filePath, projectConfig)) {
456    processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
457  } else {
458    const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
459    const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
460    moduleInfos.push(tempModuleInfo);
461  }
462}
463
464var cachedSourceMaps: Object;
465
466function updateCachedSourceMaps(): void {
467  const CACHED_SOURCEMAPS: string = path.join(process.env.cachePath, SOURCEMAPS_JSON);
468  validateFilePathLength(CACHED_SOURCEMAPS, logger);
469  if (!fs.existsSync(CACHED_SOURCEMAPS)) {
470    cachedSourceMaps = {};
471  } else {
472    cachedSourceMaps = JSON.parse(fs.readFileSync(CACHED_SOURCEMAPS).toString());
473  }
474  Object.keys(newSourceMaps).forEach(key => {
475    cachedSourceMaps[key] = newSourceMaps[key];
476  });
477}
478
479function getCachedModuleList(): Array<string> {
480  const CACHED_MODULELIST_FILE: string = path.join(process.env.cachePath, MODULELIST_JSON);
481  validateFilePathLength(CACHED_MODULELIST_FILE, logger);
482  if (!fs.existsSync(CACHED_MODULELIST_FILE)) {
483    return [];
484  }
485  const data: Object = JSON.parse(fs.readFileSync(CACHED_MODULELIST_FILE).toString());
486  const moduleList: Array<string> = data.list;
487  return moduleList;
488}
489
490function updateCachedModuleList(moduleList: Array<string>): void {
491  const CACHED_MODULELIST_FILE: string = path.join(process.env.cachePath, MODULELIST_JSON);
492  validateFilePathLength(CACHED_MODULELIST_FILE, logger);
493  const CACHED_SOURCEMAPS: string = path.join(process.env.cachePath, SOURCEMAPS_JSON);
494  validateFilePathLength(CACHED_SOURCEMAPS, logger);
495  let cachedJson: Object = {};
496  cachedJson["list"] = moduleList;
497  fs.writeFile(CACHED_MODULELIST_FILE, JSON.stringify(cachedJson, null, 2), 'utf-8',
498    (err) => {
499      if (err) {
500        logger.error(red, `ArkTS:ERROR Failed to write module list.`, reset);
501      }
502    }
503  );
504  fs.writeFile(CACHED_SOURCEMAPS, JSON.stringify(cachedSourceMaps, null, 2), 'utf-8',
505    (err) => {
506      if (err) {
507        logger.error(red, `ArkTS:ERROR Failed to write cache sourceMaps json.`, reset);
508      }
509    }
510  );
511}
512
513function writeSourceMaps(): void {
514  mkdirsSync(projectConfig.buildPath);
515  let sourceMapFilePath: string = path.join(projectConfig.buildPath, SOURCEMAPS);
516  validateFilePathLength(sourceMapFilePath, logger);
517  fs.writeFile(sourceMapFilePath, JSON.stringify(cachedSourceMaps, null, 2), 'utf-8',
518    (err) => {
519      if (err) {
520        logger.error(red, `ArkTS:ERROR Failed to write sourceMaps.`, reset);
521      }
522    }
523  );
524}
525
526function eliminateUnusedFiles(moduleList: Array<string>): void{
527  let cachedModuleList: Array<string> = getCachedModuleList();
528  if (cachedModuleList.length !== 0) {
529    const eliminateFiles: Array<string> = cachedModuleList.filter(m => !moduleList.includes(m));
530    eliminateFiles.forEach((file) => {
531      delete cachedSourceMaps[file];
532    });
533  }
534}
535
536function handleFullModuleFiles(modules, callback): void {
537  const nodeModulesFile: Array<string> = [];
538  modules.forEach(module => {
539    if (module !== undefined && module.resourceResolveData !== undefined) {
540      const filePath: string = module.resourceResolveData.path;
541      let tempFilePath = genTemporaryPath(filePath, projectConfig.projectPath, process.env.cachePath, projectConfig);
542      if (tempFilePath.length === 0) {
543        return;
544      }
545      validateFilePathLength(tempFilePath, logger);
546      let buildFilePath: string = genBuildPath(filePath, projectConfig.projectPath, projectConfig.buildPath,
547        projectConfig);
548      validateFilePathLength(buildFilePath, logger);
549      tempFilePath = toUnixPath(tempFilePath);
550      buildFilePath = toUnixPath(buildFilePath);
551
552      switch (path.extname(filePath)) {
553        case EXTNAME_ETS: {
554          processEtsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
555          break;
556        }
557        case EXTNAME_TS: {
558          processTsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
559          break;
560        }
561        case EXTNAME_JS:
562        case EXTNAME_MJS:
563        case EXTNAME_CJS: {
564          processJsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
565          break;
566        }
567        case EXTNAME_JSON: {
568          processJsonModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
569          break;
570        }
571        default: {
572          logger.error(red, `ArkTS:ERROR Cannot find resolve this file path: ${filePath}`, reset);
573          process.exitCode = FAIL;
574        }
575      }
576    }
577  });
578
579  // for mergeabc source maps
580  if (projectConfig.buildArkMode === 'debug') {
581    const moduleList: Array<string> = Array.from(buildMapFileList);
582    updateCachedSourceMaps();
583    eliminateUnusedFiles(moduleList);
584    updateCachedModuleList(moduleList);
585    writeSourceMaps();
586  }
587
588  if (process.env.panda !== TS2ABC) {
589    const outputABCPath: string = path.join(projectConfig.buildPath, MODULES_ABC);
590    validateFilePathLength(outputABCPath, logger);
591    generateMergedAbc(moduleInfos, entryInfos, outputABCPath);
592    clearGlobalInfo();
593  } else {
594    invokeWorkersModuleToGenAbc(moduleInfos);
595  }
596}
597
598function processEntryToGenAbc(entryInfos: Map<string, EntryInfo>): void {
599  if (entryInfos.size <= 0) {
600    return;
601  }
602  generateNpmEntriesInfo(entryInfos);
603  const npmEntriesInfoPath: string = path.join(process.env.cachePath, NPMENTRIES_TXT);
604  validateFilePathLength(npmEntriesInfoPath, logger);
605  let npmEntriesProtoFileName: string = "npm_entries" + EXTNAME_PROTO_BIN;
606  const npmEntriesProtoFilePath: string = path.join(process.env.cachePath, "protos", "npm_entries", npmEntriesProtoFileName);
607  validateFilePathLength(npmEntriesProtoFilePath, logger);
608  mkdirsSync(path.dirname(npmEntriesProtoFilePath));
609  let js2Abc: string = path.join(arkDir, 'build', 'bin', 'js2abc');
610  if (isWin) {
611    js2Abc = path.join(arkDir, 'build-win', 'bin', 'js2abc.exe');
612  } else if (isMac) {
613    js2Abc = path.join(arkDir, 'build-mac', 'bin', 'js2abc');
614  }
615  validateFilePathLength(js2Abc, logger);
616  const singleCmd: string = `"${js2Abc}" --compile-npm-entries "${npmEntriesInfoPath}" "${npmEntriesProtoFilePath}`;
617  try {
618    childProcess.execSync(singleCmd);
619  } catch (e) {
620    logger.debug(red, `ArkTS:ERROR Failed to generate npm proto file to abc, Error message: ${e}`, reset);
621  }
622}
623
624function writeFileSync(inputString: string, buildPath: string, keyPath: string, jsBundleFile: string): void {
625  let output = path.resolve(buildPath, keyPath);
626  validateFilePathLength(output, logger);
627  let parent: string = path.join(output, '..');
628  if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
629    mkDir(parent);
630  }
631  let cacheOutputPath: string = "";
632  if (process.env.cachePath) {
633    let buildDirArr: string[] = projectConfig.buildPath.split(path.sep);
634    let abilityDir: string = buildDirArr[buildDirArr.length - 1];
635    cacheOutputPath = path.join(process.env.cachePath, TEMPORARY, abilityDir, keyPath);
636  } else {
637    cacheOutputPath = output;
638  }
639  validateFilePathLength(cacheOutputPath, logger);
640  parent = path.join(cacheOutputPath, '..');
641  if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
642    mkDir(parent);
643  }
644  fs.writeFileSync(cacheOutputPath, inputString);
645  if (fs.existsSync(cacheOutputPath)) {
646    const fileSize: number = fs.statSync(cacheOutputPath).size;
647    let sourceFile: string = output.replace(/\.temp\.js$/, "_.js");
648    if (!isDebug && projectConfig.projectRootPath) {
649      sourceFile = toUnixPath(sourceFile.replace(projectConfig.projectRootPath + path.sep, ''));
650    } else {
651      sourceFile = toUnixPath(sourceFile);
652    }
653    output = toUnixPath(output);
654    cacheOutputPath = toUnixPath(cacheOutputPath);
655
656    intermediateJsBundle.push({path: output, size: fileSize, cacheOutputPath: cacheOutputPath, sourceFile: sourceFile});
657  } else {
658    logger.debug(red, `ArkTS:ERROR Failed to convert file ${jsBundleFile} to bin. ${output} is lost`, reset);
659    process.exitCode = FAIL;
660  }
661}
662
663function mkDir(path_: string): void {
664  const parent: string = path.join(path_, '..');
665  if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
666    mkDir(parent);
667  }
668  fs.mkdirSync(path_);
669}
670
671function getSmallestSizeGroup(groupSize: Map<number, number>): any {
672  const groupSizeArray: any = Array.from(groupSize);
673  groupSizeArray.sort(function(g1, g2) {
674    return g1[1] - g2[1]; // sort by size
675  });
676  return groupSizeArray[0][0];
677}
678
679function splitJsBundlesBySize(bundleArray: Array<File>, groupNumber: number): any {
680  const result: any = [];
681  if (bundleArray.length < groupNumber) {
682    for (const value of bundleArray) {
683      result.push([value]);
684    }
685    return result;
686  }
687
688  bundleArray.sort(function(f1: File, f2: File) {
689    return f2.size - f1.size;
690  });
691  const groupFileSize = new Map();
692  for (let i = 0; i < groupNumber; ++i) {
693    result.push([]);
694    groupFileSize.set(i, 0);
695  }
696
697  let index = 0;
698  while (index < bundleArray.length) {
699    const smallestGroup: any = getSmallestSizeGroup(groupFileSize);
700    result[smallestGroup].push(bundleArray[index]);
701    const sizeUpdate: any = groupFileSize.get(smallestGroup) + bundleArray[index].size;
702    groupFileSize.set(smallestGroup, sizeUpdate);
703    index++;
704  }
705  return result;
706}
707
708function invokeWorkersModuleToGenAbc(moduleInfos: Array<ModuleInfo>): void {
709  invokeClusterModuleToAbc();
710}
711
712export function initAbcEnv() : string[] {
713  let args: string[] = [];
714  if (process.env.minPlatformVersion === "8") {
715    process.env.panda = TS2ABC;
716    let js2abc: string = path.join(arkDir, 'build', 'legacy_api8', 'src', 'index.js');
717    if (isWin) {
718      js2abc = path.join(arkDir, 'build-win', 'legacy_api8', 'src', 'index.js');
719    } else if (isMac) {
720      js2abc = path.join(arkDir, 'build-mac', 'legacy_api8', 'src', 'index.js');
721    }
722    validateFilePathLength(js2abc, logger);
723
724    js2abc = '"' + js2abc + '"';
725    args = [
726      '--expose-gc',
727      js2abc
728    ];
729    if (isDebug) {
730      args.push('--debug');
731    }
732  } else if (process.env.panda === TS2ABC) {
733    let js2abc: string = path.join(arkDir, 'build', 'src', 'index.js');
734    if (isWin) {
735      js2abc = path.join(arkDir, 'build-win', 'src', 'index.js');
736    } else if (isMac) {
737      js2abc = path.join(arkDir, 'build-mac', 'src', 'index.js');
738    }
739    validateFilePathLength(js2abc, logger);
740
741    js2abc = '"' + js2abc + '"';
742    args = [
743      '--expose-gc',
744      js2abc
745    ];
746    if (isDebug) {
747      args.push('--debug');
748    }
749  } else if (process.env.panda === ES2ABC  || process.env.panda === 'undefined' || process.env.panda === undefined) {
750    let es2abc: string = path.join(arkDir, 'build', 'bin', 'es2abc');
751    if (isWin) {
752      es2abc = path.join(arkDir, 'build-win', 'bin', 'es2abc.exe');
753    } else if (isMac) {
754      es2abc = path.join(arkDir, 'build-mac', 'bin', 'es2abc');
755    }
756    validateFilePathLength(es2abc, logger);
757
758    args = [
759      '"' + es2abc + '"'
760    ];
761    if (isDebug) {
762      args.push('--debug-info');
763    }
764    if (projectConfig.compileMode === ESMODULE) {
765      args.push('--merge-abc');
766    }
767  }  else {
768    logger.error(red, `ArkTS:ERROR please set panda module`, reset);
769  }
770
771  return args;
772}
773
774function invokeClusterModuleToAbc(): void {
775  if (process.env.watchMode === 'true') {
776    process.exitCode = SUCCESS;
777  }
778  filterIntermediateModuleByHashJson(buildPathInfo, moduleInfos);
779  const abcArgs: string[] = initAbcEnv();
780
781  const splitedModules: any[] = splitModulesByNumber(filterModuleInfos, MAX_WORKER_NUMBER);
782  let cmdPrefix: string = `${nodeJs} ${abcArgs.join(' ')}`;
783  const workerNumber: number = MAX_WORKER_NUMBER < splitedModules.length ? MAX_WORKER_NUMBER : splitedModules.length;
784
785  try {
786    if (process.env.watchMode === 'true') {
787      processWorkersOfPreviewMode(splitedModules, cmdPrefix, workerNumber);
788    } else {
789      processWorkersOfBuildMode(splitedModules, cmdPrefix, workerNumber);
790    }
791  } catch (e) {
792    logger.debug(red, `ArkTS:ERROR failed to generate abc. Error message: ${e}`, reset);
793    process.env.abcCompileSuccess = 'false';
794    if (process.env.watchMode !== 'true') {
795      process.exit(FAIL);
796    }
797  }
798}
799
800function splitModulesByNumber(moduleInfos: Array<ModuleInfo>, workerNumber: number): any[] {
801  const result: any = [];
802  if (moduleInfos.length < workerNumber) {
803    for (const value of moduleInfos) {
804      result.push([value]);
805    }
806    return result;
807  }
808
809  for (let i = 0; i < workerNumber; ++i) {
810    result.push([]);
811  }
812
813  for (let i = 0; i < moduleInfos.length; i++) {
814    const chunk = i % workerNumber;
815    result[chunk].push(moduleInfos[i]);
816  }
817
818  return result;
819}
820
821function invokeWorkersToGenAbc(): void {
822  if (process.env.watchMode === 'true') {
823    process.exitCode = SUCCESS;
824  }
825  let cmdPrefix: string = '';
826
827  const abcArgs: string[] = initAbcEnv();
828  if (process.env.panda === TS2ABC) {
829    cmdPrefix = `${nodeJs} ${abcArgs.join(' ')}`;
830  } else {
831    logger.error(red, `ArkTS:ERROR please set panda module`, reset);
832  }
833
834  filterIntermediateJsBundleByHashJson(buildPathInfo, intermediateJsBundle);
835  const splitedBundles: any[] = splitJsBundlesBySize(fileterIntermediateJsBundle, MAX_WORKER_NUMBER);
836  const workerNumber: number = MAX_WORKER_NUMBER < splitedBundles.length ? MAX_WORKER_NUMBER : splitedBundles.length;
837
838  try {
839    if (process.env.watchMode === 'true') {
840      processWorkersOfPreviewMode(splitedBundles, cmdPrefix, workerNumber);
841    } else {
842      processWorkersOfBuildMode(splitedBundles, cmdPrefix, workerNumber);
843    }
844  } catch (e) {
845    logger.debug(red, `ArkTS:ERROR failed to generate abc. Error message: ${e}`, reset);
846    process.env.abcCompileSuccess = 'false';
847    if (process.env.watchMode !== 'true') {
848      process.exit(FAIL);
849    }
850  }
851}
852
853function filterIntermediateModuleByHashJson(buildPath: string, moduleInfos: Array<ModuleInfo>): void {
854  const tempModuleInfos = Array<ModuleInfo>();
855  moduleInfos.forEach((item) => {
856    const check = tempModuleInfos.every((newItem) => {
857      return item.tempFilePath !== newItem.tempFilePath;
858    });
859    if (check) {
860      tempModuleInfos.push(item);
861    }
862  });
863  moduleInfos = tempModuleInfos;
864
865  for (let i = 0; i < moduleInfos.length; ++i) {
866    filterModuleInfos.push(moduleInfos[i]);
867  }
868  const hashFilePath: string = genHashJsonPath(buildPath);
869  if (hashFilePath.length === 0) {
870    return;
871  }
872  const updateJsonObject: Object = {};
873  let jsonObject: Object = {};
874  let jsonFile: string = '';
875  if (fs.existsSync(hashFilePath)) {
876    jsonFile = fs.readFileSync(hashFilePath).toString();
877    jsonObject = JSON.parse(jsonFile);
878    filterModuleInfos = [];
879    for (let i = 0; i < moduleInfos.length; ++i) {
880      const input: string = moduleInfos[i].tempFilePath;
881      let outputPath: string = genProtoFileName(moduleInfos[i].tempFilePath);
882      if (!fs.existsSync(input)) {
883        logger.debug(red, `ArkTS:ERROR ${input} is lost`, reset);
884        process.exitCode = FAIL;
885        break;
886      }
887      if (fs.existsSync(outputPath)) {
888        const hashInputContentData: string = toHashData(input);
889        const hashAbcContentData: string = toHashData(outputPath);
890        if (jsonObject[input] === hashInputContentData && jsonObject[outputPath] === hashAbcContentData) {
891          updateJsonObject[input] = hashInputContentData;
892          updateJsonObject[outputPath] = hashAbcContentData;
893        } else {
894          filterModuleInfos.push(moduleInfos[i]);
895        }
896      } else {
897        filterModuleInfos.push(moduleInfos[i]);
898      }
899    }
900  }
901
902  moduleHashJsonObject = updateJsonObject;
903}
904
905function writeModuleHashJson(): void {
906  for (let i = 0; i < filterModuleInfos.length; ++i) {
907    const input: string = filterModuleInfos[i].tempFilePath;
908    let outputPath: string = genProtoFileName(filterModuleInfos[i].tempFilePath);;
909    if (!fs.existsSync(input) || !fs.existsSync(outputPath)) {
910      logger.debug(red, `ArkTS:ERROR ${input} is lost`, reset);
911      process.exitCode = FAIL;
912      break;
913    }
914    const hashInputContentData: string = toHashData(input);
915    const hashOutputContentData: string = toHashData(outputPath);
916    moduleHashJsonObject[input] = hashInputContentData;
917    moduleHashJsonObject[outputPath] = hashOutputContentData;
918  }
919  const hashFilePath: string = genHashJsonPath(buildPathInfo);
920  if (hashFilePath.length === 0) {
921    return;
922  }
923  // fix bug of multi trigger
924  fs.writeFileSync(hashFilePath, JSON.stringify(moduleHashJsonObject));
925}
926
927function filterIntermediateJsBundleByHashJson(buildPath: string, inputPaths: File[]): void {
928  inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
929
930  for (let i = 0; i < inputPaths.length; ++i) {
931    fileterIntermediateJsBundle.push(inputPaths[i]);
932  }
933  const hashFilePath: string = genHashJsonPath(buildPath);
934  if (hashFilePath.length === 0) {
935    return;
936  }
937  const updateJsonObject: Object = {};
938  let jsonObject: Object = {};
939  let jsonFile: string = '';
940  if (fs.existsSync(hashFilePath)) {
941    jsonFile = fs.readFileSync(hashFilePath).toString();
942    jsonObject = JSON.parse(jsonFile);
943    fileterIntermediateJsBundle = [];
944    for (let i = 0; i < inputPaths.length; ++i) {
945      const cacheOutputPath: string = inputPaths[i].cacheOutputPath;
946      const cacheAbcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
947      if (!fs.existsSync(cacheOutputPath)) {
948        logger.debug(red, `ArkTS:ERROR ${cacheOutputPath} is lost`, reset);
949        process.exitCode = FAIL;
950        break;
951      }
952      if (fs.existsSync(cacheAbcFilePath)) {
953        const hashInputContentData: string = toHashData(cacheOutputPath);
954        const hashAbcContentData: string = toHashData(cacheAbcFilePath);
955        if (jsonObject[cacheOutputPath] === hashInputContentData && jsonObject[cacheAbcFilePath] === hashAbcContentData) {
956          updateJsonObject[cacheOutputPath] = hashInputContentData;
957          updateJsonObject[cacheAbcFilePath] = hashAbcContentData;
958        } else {
959          fileterIntermediateJsBundle.push(inputPaths[i]);
960        }
961      } else {
962        fileterIntermediateJsBundle.push(inputPaths[i]);
963      }
964    }
965  }
966
967  hashJsonObject = updateJsonObject;
968}
969
970function writeHashJson(): void {
971  for (let i = 0; i < fileterIntermediateJsBundle.length; ++i) {
972    const cacheOutputPath: string = fileterIntermediateJsBundle[i].cacheOutputPath;
973    const cacheAbcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
974    if (!fs.existsSync(cacheOutputPath) || !fs.existsSync(cacheAbcFilePath)) {
975      logger.debug(red, `ArkTS:ERROR ${cacheOutputPath} is lost`, reset);
976      process.exitCode = FAIL;
977      break;
978    }
979    const hashInputContentData: string = toHashData(cacheOutputPath);
980    const hashAbcContentData: string = toHashData(cacheAbcFilePath);
981    hashJsonObject[cacheOutputPath] = hashInputContentData;
982    hashJsonObject[cacheAbcFilePath] = hashAbcContentData;
983  }
984  const hashFilePath: string = genHashJsonPath(buildPathInfo);
985  if (hashFilePath.length === 0) {
986    return;
987  }
988  fs.writeFileSync(hashFilePath, JSON.stringify(hashJsonObject));
989}
990
991function genHashJsonPath(buildPath: string): string {
992  buildPath = toUnixPath(buildPath);
993  if (process.env.cachePath) {
994    if (!fs.existsSync(process.env.cachePath) || !fs.statSync(process.env.cachePath).isDirectory()) {
995      logger.debug(red, `ArkTS:ERROR hash path does not exist`, reset);
996      return '';
997    }
998    let buildDirArr: string[] = projectConfig.buildPath.split(path.sep);
999    let abilityDir: string = buildDirArr[buildDirArr.length - 1];
1000    let hashJsonPath: string = path.join(process.env.cachePath, TEMPORARY, abilityDir, hashFile);
1001    validateFilePathLength(hashJsonPath, logger)
1002    mkdirsSync(path.dirname(hashJsonPath));
1003    return hashJsonPath;
1004  } else if (buildPath.indexOf(ARK) >= 0) {
1005    const dataTmps: string[] = buildPath.split(ARK);
1006    const hashPath: string = path.join(dataTmps[0], ARK);
1007    if (!fs.existsSync(hashPath) || !fs.statSync(hashPath).isDirectory()) {
1008      logger.debug(red, `ArkTS:ERROR hash path does not exist`, reset);
1009      return '';
1010    }
1011    let hashJsonPath: string = path.join(hashPath, hashFile);
1012    validateFilePathLength(hashJsonPath, logger);
1013    return hashJsonPath;
1014  } else {
1015    logger.debug(red, `ArkTS:ERROR not cache exist`, reset);
1016    return '';
1017  }
1018}
1019
1020function checkNodeModules() {
1021  if (process.env.panda === TS2ABC) {
1022    let arkEntryPath: string = path.join(arkDir, 'build');
1023    if (isWin) {
1024      arkEntryPath = path.join(arkDir, 'build-win');
1025    } else if (isMac) {
1026      arkEntryPath = path.join(arkDir, 'build-mac');
1027    }
1028    let nodeModulesPath: string = path.join(arkEntryPath, NODE_MODULES);
1029    validateFilePathLength(nodeModulesPath, logger);
1030    if (!(fs.existsSync(nodeModulesPath) && fs.statSync(nodeModulesPath).isDirectory())) {
1031      logger.error(red, `ERROR: node_modules for ark compiler not found.
1032        Please make sure switch to non-root user before runing "npm install" for safity requirements and try re-run "npm install" under ${arkEntryPath}`, reset);
1033      return false;
1034    }
1035  }
1036
1037  return true;
1038}
1039
1040function copyFileCachePathToBuildPath() {
1041  for (let i = 0; i < intermediateJsBundle.length; ++i) {
1042    const abcFile: string = intermediateJsBundle[i].path.replace(/\.temp\.js$/, ".abc");
1043    const cacheOutputPath: string = intermediateJsBundle[i].cacheOutputPath;
1044    const cacheAbcFilePath: string = intermediateJsBundle[i].cacheOutputPath.replace(/\.temp\.js$/, ".abc");
1045    if (!fs.existsSync(cacheAbcFilePath)) {
1046      logger.debug(red, `ArkTS:ERROR ${cacheAbcFilePath} is lost`, reset);
1047      process.exitCode = FAIL;
1048      break;
1049    }
1050    let parent: string = path.join(abcFile, '..');
1051    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
1052      mkDir(parent);
1053    }
1054    // for preview mode, cache path and old abc file both exist, should copy abc file for updating
1055    if (process.env.cachePath !== undefined) {
1056      fs.copyFileSync(cacheAbcFilePath, abcFile);
1057    }
1058    if (process.env.cachePath === undefined && fs.existsSync(cacheOutputPath)) {
1059      fs.unlinkSync(cacheOutputPath);
1060    }
1061  }
1062}
1063
1064function processExtraAsset() {
1065  if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
1066    writeHashJson();
1067    copyFileCachePathToBuildPath();
1068  } else if (projectConfig.compileMode === ESMODULE) {
1069    processEntryToGenAbc(entryInfos);
1070    writeModuleHashJson();
1071    copyModuleFileCachePathToBuildPath();
1072    mergeProtoToAbc();
1073  }
1074  clearGlobalInfo();
1075}
1076
1077function handleHotReloadChangedFiles() {
1078  if (!fs.existsSync(projectConfig.changedFileList)) {
1079    logger.debug(blue, `ArkTS: Cannot find file: ${projectConfig.changedFileList}, skip hot reload build`, reset);
1080    return;
1081  }
1082
1083  let changedFileListJson: string = fs.readFileSync(projectConfig.changedFileList).toString();
1084  let changedFileList: Array<string> = JSON.parse(changedFileListJson).modifiedFiles;
1085  if (typeof(changedFileList) == "undefined" || changedFileList.length == 0) {
1086    return;
1087  }
1088
1089  let relativeProjectPath = projectConfig.projectPath.slice(projectConfig.projectRootPath.length + path.sep.length);
1090  const nodeModulesFile: Array<string> = [];
1091  let hotReloadSourceMap: Object = {};
1092  moduleInfos = [];
1093
1094  for (let file of changedFileList) {
1095    let filePath: string = path.join(projectConfig.projectPath, file);
1096    validateFilePathLength(filePath, logger);
1097    let tempFilePath: string = genTemporaryPath(filePath, projectConfig.projectPath, process.env.cachePath, projectConfig);
1098    if (tempFilePath.length === 0) {
1099      return;
1100    }
1101    validateFilePathLength(tempFilePath, logger);
1102    let buildFilePath: string = "";
1103    tempFilePath = toUnixPath(tempFilePath);
1104
1105    switch (path.extname(filePath)) {
1106      case EXTNAME_ETS: {
1107        processEtsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined);
1108        break;
1109      }
1110      case EXTNAME_TS: {
1111        processTsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined);
1112        break;
1113      }
1114      case EXTNAME_JS:
1115      case EXTNAME_MJS:
1116      case EXTNAME_CJS: {
1117        processJsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined);
1118        break;
1119      }
1120      case EXTNAME_JSON: {
1121        logger.debug(blue, `ArkTS: json source file: ${filePath} changed, skip hot reload build`, reset);
1122        return;
1123      }
1124      default: {
1125        logger.debug(blue, `ArkTS:ERROR Cannot resolve file path: ${filePath}, stop hot reload build`, reset);
1126        return;
1127      }
1128    }
1129
1130    let sourceMapPath: string = toUnixPath(path.join(relativeProjectPath, file));
1131    validateFilePathLength(sourceMapPath, logger);
1132    hotReloadSourceMap[sourceMapPath] = newSourceMaps[sourceMapPath];
1133  }
1134
1135  if (!fs.existsSync(projectConfig.patchAbcPath)) {
1136    mkdirsSync(projectConfig.patchAbcPath);
1137  }
1138
1139  const outputABCPath: string = path.join(projectConfig.patchAbcPath, MODULES_ABC);
1140  validateFilePathLength(outputABCPath, logger);
1141  generateMergedAbc(moduleInfos, entryInfos, outputABCPath);
1142
1143  // write source maps
1144  let sourceMapFilePath: string = path.join(projectConfig.patchAbcPath, SOURCEMAPS);
1145  validateFilePathLength(sourceMapFilePath, logger);
1146  fs.writeFileSync(sourceMapFilePath,
1147                   JSON.stringify(hotReloadSourceMap, null, 2), 'utf-8');
1148}
1149
1150function handleFinishModules(modules, callback) {
1151  if (projectConfig.hotReload && !isHotReloadFirstBuild) {
1152    handleHotReloadChangedFiles();
1153    return;
1154  }
1155
1156  handleFullModuleFiles(modules, callback);
1157
1158  if (projectConfig.hotReload) {
1159    isHotReloadFirstBuild = false;
1160  }
1161}
1162
1163function copyModuleFileCachePathToBuildPath(): void {
1164  protoFilePath = path.join(path.join(process.env.cachePath, "protos", PROTO_FILESINFO_TXT));
1165  validateFilePathLength(protoFilePath, logger);
1166  mkdirsSync(path.dirname(protoFilePath));
1167  let entriesInfo: string = '';
1168  moduleInfos = removeDuplicateInfo(moduleInfos);
1169  moduleInfos.sort((m1: ModuleInfo, m2: ModuleInfo) => {
1170    return m1.tempFilePath < m2.tempFilePath ? 1 : -1;
1171  });
1172  for (let i = 0; i < moduleInfos.length; ++i) {
1173    let protoTempPath: string = genProtoFileName(moduleInfos[i].tempFilePath);
1174    entriesInfo += `${toUnixPath(protoTempPath)}\n`;
1175  }
1176  if (entryInfos.size > 0) {
1177    let npmEntriesProtoFileName: string = "npm_entries" + EXTNAME_PROTO_BIN;
1178    const npmEntriesProtoFilePath: string = path.join(process.env.cachePath, "protos", "npm_entries", npmEntriesProtoFileName);
1179    entriesInfo += `${toUnixPath(npmEntriesProtoFilePath)}\n`;
1180  }
1181  fs.writeFileSync(protoFilePath, entriesInfo, 'utf-8');
1182}
1183
1184function mergeProtoToAbc(): void {
1185  let mergeAbc: string = path.join(arkDir, 'build', 'bin', 'merge_abc');
1186  if (isWin) {
1187    mergeAbc = path.join(arkDir, 'build-win', 'bin', 'merge_abc.exe');
1188  } else if (isMac) {
1189    mergeAbc = path.join(arkDir, 'build-mac', 'bin', 'merge_abc');
1190  }
1191  mkdirsSync(projectConfig.buildPath);
1192  const singleCmd: string = `"${mergeAbc}" --input "@${protoFilePath}" --outputFilePath "${projectConfig.buildPath}" --output ${MODULES_ABC} --suffix protoBin`;
1193  try {
1194    childProcess.execSync(singleCmd);
1195  } catch (e) {
1196    logger.debug(red, `ArkTS:ERROR Failed to merge proto file to abc. Error message: ${e}`, reset);
1197  }
1198}
1199
1200function generateAbcByEs2AbcOfBundleMode(inputPaths: File[]) {
1201  filterIntermediateJsBundleByHashJson(buildPathInfo, inputPaths);
1202  if (fileterIntermediateJsBundle.length === 0) {
1203    processExtraAsset();
1204    return;
1205  }
1206  let filesInfoPath = generateFileOfBundle(fileterIntermediateJsBundle);
1207  const fileThreads = os.cpus().length < 16 ? os.cpus().length : 16;
1208  let genAbcCmd: string =
1209      `${initAbcEnv().join(' ')} "@${filesInfoPath}" --file-threads "${fileThreads}"`;
1210  logger.debug('gen abc cmd is: ', genAbcCmd);
1211  try {
1212    if (process.env.watchMode === 'true') {
1213      childProcess.execSync(genAbcCmd);
1214      processExtraAsset();
1215    } else {
1216      const child = childProcess.exec(genAbcCmd);
1217      child.on('exit', (code: any) => {
1218        if (code === FAIL) {
1219          logger.debug(red, "ArkTS:ERROR failed to execute es2abc", reset);
1220          process.exit(FAIL);
1221        }
1222        if (process.env.cachePath === undefined) {
1223          unlinkSync(filesInfoPath);
1224        }
1225        processExtraAsset();
1226      });
1227
1228      child.on('error', (err: any) => {
1229        logger.debug(red, err.toString(), reset);
1230        process.exit(FAIL);
1231      });
1232
1233      child.stderr.on('data', (data: any) => {
1234        logger.error(red, data.toString(), reset);
1235      });
1236    }
1237  } catch (e) {
1238    logger.debug(red, `ArkTS:ERROR failed to generate abc with filesInfo ${filesInfoPath}. Error message: ${e} `, reset);
1239    process.env.abcCompileSuccess = 'false';
1240    if (process.env.watchMode !== 'true') {
1241      process.exit(FAIL);
1242    }
1243  } finally {
1244    if (process.env.watchMode === 'true') {
1245      if (process.env.cachePath === undefined) {
1246        unlinkSync(filesInfoPath);
1247      }
1248    }
1249  }
1250}
1251
1252function generateFileOfBundle(inputPaths: File[]): string {
1253  let filesInfoPath: string = buildCachePath(FILESINFO_TXT, projectConfig, logger);
1254  inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
1255
1256  let filesInfo: string = '';
1257  inputPaths.forEach(info => {
1258    const cacheOutputPath: string = info.cacheOutputPath;
1259    const recordName: string = 'null_recordName';
1260    const moduleType: string = 'script';
1261    const sourceFile: string = info.sourceFile;
1262    const abcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, ".abc");
1263    filesInfo += `${cacheOutputPath};${recordName};${moduleType};${sourceFile};${abcFilePath}\n`;
1264  });
1265  fs.writeFileSync(filesInfoPath, filesInfo, 'utf-8');
1266
1267  return filesInfoPath;
1268}
1269
1270function removeDuplicateInfoOfBundleList(inputPaths: File[]) {
1271  const tempInputPaths = Array<File>();
1272  inputPaths.forEach((item) => {
1273    const check = tempInputPaths.every((newItem) => {
1274      return item.path !== newItem.path;
1275    });
1276    if (check) {
1277      tempInputPaths.push(item);
1278    }
1279  });
1280  inputPaths = tempInputPaths;
1281
1282  return inputPaths;
1283}
1284
1285function processWorkersOfPreviewMode(splittedData: any, cmdPrefix: string, workerNumber: number) {
1286  let processEnv: any = Object.assign({}, process.env);
1287  let arkEnvParams: any = {
1288    'splittedData': JSON.stringify(splittedData),
1289    'cmdPrefix': cmdPrefix,
1290    'workerNumber': workerNumber.toString(),
1291  };
1292  if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
1293    arkEnvParams['mode'] = JSBUNDLE;
1294  } else if (projectConfig.compileMode === ESMODULE) {
1295    arkEnvParams['cachePath'] = process.env.cachePath;
1296    arkEnvParams['mode'] = ESMODULE;
1297  }
1298  processEnv.arkEnvParams = JSON.stringify(arkEnvParams);
1299
1300  let genAbcCmd: string = `${nodeJs} "${path.resolve(__dirname, MANAGE_WORKERS_SCRIPT)}"`;
1301  childProcess.execSync(genAbcCmd, {env: processEnv});
1302  processExtraAsset();
1303}
1304
1305function processWorkersOfBuildMode(splittedData: any, cmdPrefix: string, workerNumber: number) {
1306  const useNewApi: boolean = nodeLargeOrEqualTargetVersion(16);
1307
1308  if (useNewApi && cluster.isPrimary || !useNewApi && cluster.isMaster) {
1309    let genAbcScript: string = GEN_ABC_SCRIPT;
1310    if (projectConfig.compileMode === ESMODULE) {
1311      genAbcScript = GEN_MODULE_ABC_SCRIPT;
1312    }
1313    if (useNewApi) {
1314      cluster.setupPrimary({
1315        exec: path.resolve(__dirname, genAbcScript)
1316      });
1317    } else {
1318      cluster.setupMaster({
1319        exec: path.resolve(__dirname, genAbcScript)
1320      });
1321    }
1322
1323    for (let i = 0; i < workerNumber; ++i) {
1324      let workerData: any = {
1325        'inputs': JSON.stringify(splittedData[i]),
1326        'cmd': cmdPrefix
1327      };
1328      if (projectConfig.compileMode === ESMODULE) {
1329        let sn: number = i + 1;
1330        let workerFileName: string = `filesInfo_${sn}.txt`;
1331        workerData['workerFileName'] = workerFileName;
1332        workerData['cachePath'] = process.env.cachePath;
1333      }
1334      cluster.fork(workerData);
1335    }
1336
1337    cluster.on('exit', (worker, code, signal) => {
1338      if (code === FAIL) {
1339        process.exitCode = FAIL;
1340      }
1341      logger.debug(`worker ${worker.process.pid} finished`);
1342    });
1343
1344    process.on('exit', (code) => {
1345      if (process.exitCode !== FAIL && process.env.watchMode !== 'true') {
1346        processExtraAsset();
1347        if (projectConfig.compileMode === ESMODULE &&
1348          (projectConfig.anBuildMode === AOT_FULL || projectConfig.anBuildMode === AOT_PARTIAL)) {
1349          let faultHandler: FaultHandler = (error) => { logger.error(error); process.exit(FAIL); }
1350          let abcArgs: string[] = initAbcEnv();
1351          abcArgs.unshift(nodeJs);
1352          const appAbc: string = path.join(projectConfig.buildPath, MODULES_ABC);
1353          generateAot(arkDir, appAbc, projectConfig, logger, faultHandler);
1354        }
1355      }
1356    });
1357  }
1358}
1359