• 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
16const fs = require('fs');
17const path = require('path');
18const cluster = require('cluster');
19const process = require('process');
20const crypto = require('crypto');
21const events = require('events');
22const os = require('os');
23const childProcess = require('child_process');
24
25const forward = '(global.___mainEntry___ = function (globalObjects) {' + '\n' +
26              '  var define = globalObjects.define;' + '\n' +
27              '  var require = globalObjects.require;' + '\n' +
28              '  var bootstrap = globalObjects.bootstrap;' + '\n' +
29              '  var register = globalObjects.register;' + '\n' +
30              '  var render = globalObjects.render;' + '\n' +
31              '  var $app_define$ = globalObjects.$app_define$;' + '\n' +
32              '  var $app_bootstrap$ = globalObjects.$app_bootstrap$;' + '\n' +
33              '  var $app_require$ = globalObjects.$app_require$;' + '\n' +
34              '  var history = globalObjects.history;' + '\n' +
35              '  var Image = globalObjects.Image;' + '\n' +
36              '  var OffscreenCanvas = globalObjects.OffscreenCanvas;' + '\n' +
37              '  (function(global) {' + '\n' +
38              '    "use strict";' + '\n';
39const last = '\n' + '})(this.__appProto__);' + '\n' + '})';
40const genAbcScript = 'gen-abc.js';
41let output;
42let isWin = false;
43let isMac = false;
44let isDebug = false;
45let arkDir;
46let nodeJs;
47let intermediateJsBundle = [];
48let workerFile = null;
49let fileterIntermediateJsBundle = [];
50let hashJsonObject = {};
51let buildPathInfo = "";
52const SUCCESS = 0;
53const FAIL = 1;
54const red = '\u001b[31m';
55const reset = '\u001b[39m';
56const blue = '\u001b[34m';
57const hashFile = 'gen_hash.json';
58const ARK = '/ark/';
59const NODE_MODULES = 'node_modules';
60const TEMPORARY = 'temporary';
61const TS2ABC = 'ts2abc';
62const ES2ABC = 'es2abc';
63const WINDOWS = 'Windows_NT';
64const LINUX = 'Linux';
65const MAC = 'Darwin';
66const FILESINFO_TXT = 'filesInfo.txt';
67const manageBunldeWorkersScript = 'manage-bundle-workers.js';
68const PREBUILDINFO_JSON = 'preBuildInfo.json';
69
70class GenAbcPlugin {
71  constructor(output_, arkDir_, nodeJs_, workerFile_, isDebug_) {
72    output = output_;
73    arkDir = arkDir_;
74    nodeJs = nodeJs_;
75    isDebug = isDebug_;
76    workerFile = workerFile_;
77  }
78  apply(compiler) {
79    if (fs.existsSync(path.resolve(arkDir, 'build-win'))) {
80      isWin = true;
81    } else if (fs.existsSync(path.resolve(arkDir, 'build-mac'))) {
82      isMac = true;
83    } else if (!fs.existsSync(path.resolve(arkDir, 'build'))) {
84      console.error(red, 'ERROR find build fail', reset);
85      process.exitCode = FAIL;
86      return;
87    }
88
89    if (!checkNodeModules()) {
90      process.exitCode = FAIL;
91      return;
92    }
93
94    // for preview mode max listeners
95    events.EventEmitter.defaultMaxListeners = 100;
96
97    compiler.hooks.emit.tap('GenAbcPlugin', (compilation) => {
98      const assets = compilation.assets;
99      const keys = Object.keys(assets);
100      buildPathInfo = output;
101      keys.forEach(key => {
102        // choice *.js
103        if (output && path.extname(key) === '.js') {
104          let newContent = assets[key].source();
105          if (checkWorksFile(key, workerFile) && key !== 'commons.js' && key !== 'vendors.js') {
106            newContent = forward + newContent + last;
107          }
108          if (key === 'commons.js' || key === 'vendors.js' || !checkWorksFile(key, workerFile)) {
109            newContent = `\n\n\n\n\n\n\n\n\n\n\n\n\n\n` + newContent;
110          }
111          const keyPath = key.replace(/\.js$/, ".temp.js");
112          writeFileSync(newContent, output, keyPath, key, true);
113        } else if (output && path.extname(key) === '.json' &&
114          process.env.DEVICE_LEVEL === 'card' && process.env.configOutput && !checkI18n(key)) {
115          writeFileSync(assets[key].source(), output, key, key, false);
116        }
117      })
118    });
119    compiler.hooks.afterEmit.tap('GenAbcPluginMultiThread', () => {
120      if (intermediateJsBundle.length === 0) {
121        return;
122      }
123      buildPathInfo = output;
124      clearWebpackCacheByBuildInfo();
125      setPrebuildInfo();
126      processMultiThreadEntry();
127    });
128  }
129}
130
131function processMultiThreadEntry() {
132  if (isTs2Abc() || process.env.minPlatformVersion === "8") {
133      invokeWorkerToGenAbc();
134  } else if (isEs2Abc()) {
135    generateAbcByEs2AbcOfBundleMode(intermediateJsBundle);
136  } else {
137    console.debug(red, `ERROR please set panda module`, reset);
138  }
139}
140
141function checkI18n(key) {
142  const outI18nPath = path.resolve(process.env.configOutput, key);
143  const projectI18nPath = outI18nPath.replace(output, process.env.projectPath);
144  if (projectI18nPath.indexOf(
145    path.resolve(__dirname, process.env.projectPath, 'i18n')) > -1) {
146    return true;
147  }
148  return false;
149}
150
151function checkWorksFile(assetPath, workerFile) {
152  if (workerFile === null) {
153    if (assetPath.search("./workers/") !== 0) {
154      return true;
155    } else {
156      return false;
157    }
158  } else {
159    for (const key in workerFile) {
160      let keyExt = key + '.js';
161      if (keyExt === assetPath) {
162        return false;
163      }
164    }
165  }
166
167  return true;
168}
169
170function writeFileSync(inputString, buildPath, keyPath, jsBundleFile, isToBin) {
171    let output = path.resolve(buildPath, keyPath);
172    validateFilePathLength(output);
173    let parent = path.join(output, '..');
174    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
175        mkDir(parent);
176    }
177    if (!isToBin) {
178      fs.writeFileSync(output, inputString);
179      return;
180    }
181    let cacheOutputPath = "";
182    if (process.env.cachePath) {
183      let buildDirArr = buildPathInfo.split(path.sep);
184      let abilityDir = buildDirArr[buildDirArr.length - 1];
185      cacheOutputPath = path.join(process.env.cachePath, TEMPORARY, abilityDir, keyPath);
186    } else {
187      cacheOutputPath = output;
188    }
189    validateFilePathLength(cacheOutputPath);
190    parent = path.join(cacheOutputPath, '..');
191    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
192      mkDir(parent);
193    }
194    fs.writeFileSync(cacheOutputPath, inputString);
195    if (fs.existsSync(cacheOutputPath)) {
196      let fileSize = fs.statSync(cacheOutputPath).size;
197      output = toUnixPath(output);
198      cacheOutputPath = toUnixPath(cacheOutputPath);
199      intermediateJsBundle.push({path: output, size: fileSize, cacheOutputPath: cacheOutputPath});
200    } else {
201      console.debug(red, `ERROR Failed to convert file ${jsBundleFile} to bin. ${output} is lost`, reset);
202      process.exitCode = FAIL;
203    }
204}
205
206function mkDir(path_) {
207    const parent = path.join(path_, '..');
208    if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
209        mkDir(parent);
210    }
211    fs.mkdirSync(path_);
212}
213
214function getSmallestSizeGroup(groupSize) {
215  let groupSizeArray = Array.from(groupSize);
216  groupSizeArray.sort(function(g1, g2) {
217    return g1[1] - g2[1]; // sort by size
218  });
219  return groupSizeArray[0][0];
220}
221
222function splitJsBundlesBySize(bundleArray, groupNumber) {
223  let result = [];
224  if (bundleArray.length < groupNumber) {
225    for (let value of bundleArray) {
226      result.push([value]);
227    }
228    return result;
229  }
230
231  bundleArray.sort(function(f1, f2) {
232    return f2.size - f1.size;
233  });
234  let groupFileSize = new Map();
235  for (let i = 0; i < groupNumber; ++i) {
236    result.push([]);
237    groupFileSize.set(i, 0);
238  }
239
240  let index = 0;
241  while(index < bundleArray.length) {
242    let smallestGroup = getSmallestSizeGroup(groupFileSize);
243    result[smallestGroup].push(bundleArray[index]);
244    let sizeUpdate = groupFileSize.get(smallestGroup) + bundleArray[index].size;
245    groupFileSize.set(smallestGroup, sizeUpdate);
246    index++;
247  }
248  return result;
249}
250
251function invokeWorkerToGenAbc() {
252  if (process.env.isPreview === "true") {
253    process.exitCode = SUCCESS;
254  }
255  let maxWorkerNumber = isEs2Abc() ? os.cpus().length : 3;
256  const abcArgs = initAbcEnv();
257  let cmdPrefix = initCmdPrefix(abcArgs);
258
259  filterIntermediateJsBundleByHashJson(buildPathInfo, intermediateJsBundle);
260  const splitedBundles = splitJsBundlesBySize(fileterIntermediateJsBundle, maxWorkerNumber);
261  const workerNumber = maxWorkerNumber < splitedBundles.length ? maxWorkerNumber : splitedBundles.length;
262
263  try  {
264    if (process.env.isPreview === 'true') {
265      processWorkersOfPreviewMode(splitedBundles, cmdPrefix, workerNumber);
266    } else {
267      processWorkersOfBuildMode(splitedBundles, cmdPrefix, workerNumber);
268    }
269  } catch (e) {
270    console.debug(red, `ERROR failed to generate abc. Error message: ${e} `, reset);
271    process.env.abcCompileSuccess = 'false';
272    if (process.env.isPreview !== 'true') {
273      process.exit(FAIL);
274    }
275  }
276}
277
278function clearGlobalInfo() {
279  if (process.env.isPreview !== "true") {
280    intermediateJsBundle = [];
281  }
282  fileterIntermediateJsBundle = [];
283  hashJsonObject = {};
284}
285
286function filterIntermediateJsBundleByHashJson(buildPath, inputPaths) {
287  inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
288
289  for (let i = 0; i < inputPaths.length; ++i) {
290    fileterIntermediateJsBundle.push(inputPaths[i]);
291  }
292  const hashFilePath = genHashJsonPath(buildPath);
293  if (hashFilePath.length == 0) {
294    return;
295  }
296  let updateJsonObject = {};
297  let jsonObject = {};
298  let jsonFile = "";
299  if (fs.existsSync(hashFilePath)) {
300    jsonFile = fs.readFileSync(hashFilePath).toString();
301    jsonObject = JSON.parse(jsonFile);
302    fileterIntermediateJsBundle = [];
303    for (let i = 0; i < inputPaths.length; ++i) {
304      const cacheOutputPath = inputPaths[i].cacheOutputPath;
305      const cacheAbcFilePath = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
306      if (!fs.existsSync(cacheOutputPath)) {
307        console.debug(red, `ERROR ${cacheOutputPath} is lost`, reset);
308        process.exitCode = FAIL;
309        break;
310      }
311
312      if (fs.existsSync(cacheAbcFilePath)) {
313        const hashInputContentData = toHashData(cacheOutputPath);
314        const hashAbcContentData = toHashData(cacheAbcFilePath);
315        if (jsonObject[cacheOutputPath] === hashInputContentData && jsonObject[cacheAbcFilePath] === hashAbcContentData) {
316          updateJsonObject[cacheOutputPath] = hashInputContentData;
317          updateJsonObject[cacheAbcFilePath] = hashAbcContentData;
318        } else {
319          fileterIntermediateJsBundle.push(inputPaths[i]);
320        }
321      } else {
322        fileterIntermediateJsBundle.push(inputPaths[i]);
323      }
324    }
325  }
326
327  hashJsonObject = updateJsonObject;
328}
329
330function writeHashJson() {
331  for (let i = 0; i < fileterIntermediateJsBundle.length; ++i) {
332    const cacheOutputPath = fileterIntermediateJsBundle[i].cacheOutputPath;
333    const cacheAbcFilePath = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
334    if (!fs.existsSync(cacheOutputPath) || !fs.existsSync(cacheAbcFilePath)) {
335      console.debug(red, `ERROR ${cacheOutputPath} is lost`, reset);
336      process.exitCode = FAIL;
337      break;
338    }
339    const hashInputContentData = toHashData(cacheOutputPath);
340    const hashAbcContentData = toHashData(cacheAbcFilePath);
341    hashJsonObject[cacheOutputPath] = hashInputContentData;
342    hashJsonObject[cacheAbcFilePath] = hashAbcContentData;
343  }
344  const hashFilePath = genHashJsonPath(buildPathInfo);
345  if (hashFilePath.length == 0) {
346    return;
347  }
348  fs.writeFileSync(hashFilePath, JSON.stringify(hashJsonObject));
349}
350
351function genHashJsonPath(buildPath) {
352  buildPath = toUnixPath(buildPath);
353  if (process.env.cachePath) {
354    if (!fs.existsSync(process.env.cachePath) || !fs.statSync(process.env.cachePath).isDirectory()) {
355      return '';
356    }
357    let buildDirArr = buildPathInfo.split(path.sep);
358    let abilityDir = buildDirArr[buildDirArr.length - 1];
359    let hashJsonPath = path.join(process.env.cachePath, TEMPORARY, abilityDir, hashFile);
360    validateFilePathLength(hashJsonPath);
361    let parent = path.join(hashJsonPath, '..');
362    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
363      mkDir(parent);
364    }
365    return hashJsonPath;
366  } else if (buildPath.indexOf(ARK) >= 0) {
367    const dataTmps = buildPath.split(ARK);
368    const hashPath = path.join(dataTmps[0], ARK);
369    if (!fs.existsSync(hashPath) || !fs.statSync(hashPath).isDirectory()) {
370      return '';
371    }
372    let hashJsonPath = path.join(hashPath, hashFile);
373    validateFilePathLength(hashJsonPath);
374    return hashJsonPath;
375  } else {
376    return '';
377  }
378}
379
380function toUnixPath(data) {
381  if (/^win/.test(require('os').platform())) {
382    const fileTmps = data.split(path.sep);
383    const newData = path.posix.join(...fileTmps);
384    return newData;
385  }
386  return data;
387}
388
389function toHashData(path) {
390  const content = fs.readFileSync(path);
391  const hash = crypto.createHash('sha256');
392  hash.update(content);
393  return hash.digest('hex');
394}
395
396module.exports = {
397  GenAbcPlugin: GenAbcPlugin,
398  checkWorksFile: checkWorksFile
399}
400
401function copyFileCachePathToBuildPath() {
402  for (let i = 0; i < intermediateJsBundle.length; ++i) {
403    const abcFile = intermediateJsBundle[i].path.replace(/\.temp\.js$/, ".abc");
404    const cacheOutputPath = intermediateJsBundle[i].cacheOutputPath;
405    const cacheAbcFilePath = intermediateJsBundle[i].cacheOutputPath.replace(/\.temp\.js$/, ".abc");
406    if (!fs.existsSync(cacheAbcFilePath)) {
407      console.debug(red, `ERROR ${cacheAbcFilePath} is lost`, reset);
408      process.exitCode = FAIL;
409      break;
410    }
411    let parent = path.join(abcFile, '..');
412    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
413      mkDir(parent);
414    }
415    // for preview mode, cache path and old abc file both exist, should copy abc file for updating
416    if (process.env.cachePath !== undefined) {
417      fs.copyFileSync(cacheAbcFilePath, abcFile);
418    }
419    if (process.env.cachePath === undefined && fs.existsSync(cacheOutputPath)) {
420      fs.unlinkSync(cacheOutputPath);
421    }
422  }
423}
424
425function processExtraAssetForBundle() {
426  writeHashJson();
427  copyFileCachePathToBuildPath();
428  clearGlobalInfo();
429}
430
431function checkNodeModules() {
432  if (process.env.panda === TS2ABC) {
433    let arkEntryPath = path.join(arkDir, 'build');
434    if (isWin) {
435      arkEntryPath = path.join(arkDir, 'build-win');
436    } else if (isMac) {
437      arkEntryPath = path.join(arkDir, 'build-mac');
438    }
439    let nodeModulesPath = path.join(arkEntryPath, NODE_MODULES);
440    validateFilePathLength(nodeModulesPath);
441    if (!(fs.existsSync(nodeModulesPath) && fs.statSync(nodeModulesPath).isDirectory())) {
442      console.error(red, `ERROR: node_modules for ark compiler not found.
443        Please make sure switch to non-root user before runing "npm install" for safity requirements and try re-run "npm install" under ${arkEntryPath}`, reset);
444      return false;
445    }
446  }
447
448  return true;
449}
450
451function initAbcEnv() {
452  let args = [];
453  if (process.env.minPlatformVersion === "8") {
454    process.env.panda = TS2ABC;
455    let js2abc = path.join(arkDir, 'build', 'legacy_api8', 'src', 'index.js');
456    if (isWin) {
457      js2abc = path.join(arkDir, 'build-win', 'legacy_api8', 'src', 'index.js');
458    } else if (isMac) {
459      js2abc = path.join(arkDir, 'build-mac', 'legacy_api8', 'src', 'index.js');
460    }
461    validateFilePathLength(js2abc);
462
463    js2abc = '"' + js2abc + '"';
464    args = [
465      '--expose-gc',
466      js2abc
467    ];
468    if (isDebug) {
469      args.push('--debug');
470    }
471  } else if (process.env.panda === TS2ABC) {
472    let js2abc = path.join(arkDir, 'build', 'src', 'index.js');
473    if (isWin) {
474      js2abc = path.join(arkDir, 'build-win', 'src', 'index.js');
475    } else if (isMac) {
476      js2abc = path.join(arkDir, 'build-mac', 'src', 'index.js');
477    }
478    validateFilePathLength(js2abc);
479
480    js2abc = '"' + js2abc + '"';
481    args = [
482      '--expose-gc',
483      js2abc
484    ];
485    if (isDebug) {
486      args.push('--debug');
487    }
488  } else if (process.env.panda === ES2ABC  || process.env.panda === 'undefined' || process.env.panda === undefined) {
489    let es2abc = path.join(arkDir, 'build', 'bin', 'es2abc');
490    if (isWin) {
491      es2abc = path.join(arkDir, 'build-win', 'bin', 'es2abc.exe');
492    } else if (isMac) {
493      es2abc = path.join(arkDir, 'build-mac', 'bin', 'es2abc');
494    }
495    validateFilePathLength(es2abc);
496
497    args = [
498      '"' + es2abc + '"'
499    ];
500    if (isDebug) {
501      args.push('--debug-info');
502    }
503  }  else {
504    console.debug(red, `ERROR: please set panda module`, reset);
505  }
506
507  return args;
508}
509
510export function isWindows() {
511  return os.type() === WINDOWS;
512}
513
514export function isLinux() {
515  return os.type() === LINUX;
516}
517
518export function isMacOs() {
519  return os.type() === MAC;
520}
521
522export function maxFilePathLength() {
523  if (isWindows()) {
524    return 32766;
525  } else if (isLinux()) {
526    return 4095;
527  } else if (isMacOs()) {
528    return 1016;
529  } else {
530    return -1;
531  }
532}
533
534export function validateFilePathLength(filePath) {
535  if (maxFilePathLength() < 0) {
536    console.error("Unknown OS platform");
537    process.exitCode = FAIL;
538    return false;
539  } else if (filePath.length > 0 && filePath.length <= maxFilePathLength()) {
540    return true;
541  } else if (filePath.length > maxFilePathLength()) {
542    console.error(`The length of ${filePath} exceeds the limitation of current platform, which is ` +
543    `${maxFilePathLength()}. Please try moving the project folder to avoid deeply nested file path and try again`);
544    process.exitCode = FAIL;
545    return false;
546  } else {
547    console.error("Validate file path failed");
548    process.exitCode = FAIL;
549    return false;
550  }
551}
552
553export function isEs2Abc() {
554  return process.env.panda === ES2ABC  || process.env.panda === 'undefined' || process.env.panda === undefined;
555}
556
557export function isTs2Abc() {
558  return process.env.panda === TS2ABC;
559}
560
561function generateAbcByEs2AbcOfBundleMode(inputPaths) {
562  filterIntermediateJsBundleByHashJson(buildPathInfo, inputPaths);
563  if (fileterIntermediateJsBundle.length === 0) {
564    processExtraAssetForBundle()
565    return;
566  }
567  let filesInfoPath = generateFileOfBundle(fileterIntermediateJsBundle);
568  const fileThreads = os.cpus().length < 16 ? os.cpus().length : 16;
569  let genAbcCmd =
570      `${initAbcEnv().join(' ')} "@${filesInfoPath}" --file-threads "${fileThreads}"`;
571
572  console.debug('gen abc cmd is: ', genAbcCmd);
573  try {
574    if (process.env.isPreview === 'true') {
575      childProcess.execSync(genAbcCmd);
576      processExtraAssetForBundle();
577    } else {
578      const child = childProcess.exec(genAbcCmd);
579      child.on('exit', (code) => {
580        if (code === FAIL) {
581          console.debug(red, "ERROR failed to execute es2abc", reset);
582          process.exit(FAIL);
583        }
584        if (process.env.cachePath === undefined) {
585          unlinkSync(filesInfoPath);
586        }
587        processExtraAssetForBundle();
588      });
589
590      child.on('error', (err) => {
591        console.debug(red, err.toString(), reset);
592        process.exit(FAIL);
593      });
594
595      child.stderr.on('data', (data) => {
596        console.debug(red, data.toString(), reset);
597      });
598    }
599  } catch (e) {
600    console.debug(red, `ERROR failed to generate abc with filesInfo ${filesInfoPath}. Error message: ${e} `, reset);
601    process.env.abcCompileSuccess = 'false';
602    if (process.env.isPreview !== 'true') {
603      process.exit(FAIL);
604    }
605  } finally {
606    if (process.env.isPreview === 'true') {
607      if (process.env.cachePath === undefined) {
608        unlinkSync(filesInfoPath);
609      }
610    }
611  }
612}
613
614function generateFileOfBundle(inputPaths) {
615  let filesInfoPath = buildCachePath(FILESINFO_TXT);
616  inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
617
618  let filesInfo = '';
619  inputPaths.forEach(info => {
620    const cacheOutputPath = info.cacheOutputPath;
621    const recordName = 'null_recordName';
622    const moduleType = 'script';
623    const sourceFile = info.path.replace(/\.temp\.js$/, "_.js");
624    const abcFilePath = cacheOutputPath.replace(/\.temp\.js$/, ".abc");
625    filesInfo += `${cacheOutputPath};${recordName};${moduleType};${sourceFile};${abcFilePath}\n`;
626  });
627  fs.writeFileSync(filesInfoPath, filesInfo, 'utf-8');
628
629  return filesInfoPath;
630}
631
632function removeDuplicateInfoOfBundleList(inputPaths) {
633  let tempInputPaths = [];
634  inputPaths.forEach((item) => {
635    const check = tempInputPaths.every((newItem) => {
636      return item.path !== newItem.path;
637    });
638    if (check) {
639      tempInputPaths.push(item);
640    }
641  });
642  inputPaths = tempInputPaths;
643
644  return inputPaths;
645}
646
647function buildCachePath(tailName) {
648  let pathName = process.env.cachePath !== undefined ?
649      path.join(process.env.cachePath, tailName) : path.join(buildPathInfo, tailName);
650  validateFilePathLength(pathName);
651
652  return pathName;
653}
654
655function unlinkSync(filePath) {
656  if (fs.existsSync(filePath)) {
657    fs.unlinkSync(filePath);
658  }
659}
660
661function initCmdPrefix(abcArgs) {
662  let cmdPrefix = "";
663  if (process.env.panda === TS2ABC) {
664    cmdPrefix = `${nodeJs} ${abcArgs.join(' ')}`;
665  } else if (process.env.panda === ES2ABC  || process.env.panda === 'undefined' || process.env.panda === undefined) {
666    cmdPrefix = `${abcArgs.join(' ')}`;
667  } else {
668    console.debug(red, `ERROR please set panda module`, reset);
669  }
670
671  return cmdPrefix;
672}
673
674function processWorkersOfPreviewMode(splittedData, cmdPrefix, workerNumber) {
675  let processEnv = Object.assign({}, process.env);
676  let arkEnvParams = {
677    'splittedData': JSON.stringify(splittedData),
678    'cmdPrefix': cmdPrefix,
679    'workerNumber': workerNumber.toString()
680  };
681  processEnv.arkEnvParams = JSON.stringify(arkEnvParams);
682
683  let genAbcCmd = `${nodeJs} "${path.resolve(__dirname, manageBunldeWorkersScript)}"`;
684  childProcess.execSync(genAbcCmd, {env: processEnv});
685  processExtraAssetForBundle();
686}
687
688function processWorkersOfBuildMode(splittedData, cmdPrefix, workerNumber) {
689  const clusterNewApiVersion = 16;
690  const currentNodeVersion = parseInt(process.version.split(".")[0]);
691  const useNewApi = currentNodeVersion >= clusterNewApiVersion;
692
693  if (useNewApi && cluster.isPrimary || !useNewApi && cluster.isMaster) {
694    if (useNewApi) {
695      cluster.setupPrimary({
696        exec: path.resolve(__dirname, genAbcScript)
697      });
698    } else {
699      cluster.setupMaster({
700        exec: path.resolve(__dirname, genAbcScript)
701      });
702    }
703
704    for (let i = 0; i < workerNumber; ++i) {
705      let workerData = {
706        'inputs': JSON.stringify(splittedData[i]),
707        'cmd': cmdPrefix
708      };
709      cluster.fork(workerData);
710    }
711
712    cluster.on('exit', (worker, code, signal) => {
713      if (code === FAIL) {
714        process.exitCode = FAIL;
715      }
716      console.debug(`worker ${worker.process.pid} finished`);
717    });
718
719    process.on('exit', (code) => {
720      if (process.exitCode !== FAIL && process.env.isPreview !== 'true') {
721        processExtraAssetForBundle();
722      }
723    });
724  }
725}
726
727function clearWebpackCacheByBuildInfo() {
728  if (!process.env.cachePath) {
729    return;
730  }
731
732  // clear&update cache dir when build info is different from last time
733  const cachePrebuildInfoPath = path.join(process.env.cachePath, PREBUILDINFO_JSON);
734  if (fs.existsSync(cachePrebuildInfoPath)) {
735    let cachedJson = undefined;
736    try {
737      cachedJson = JSON.parse(fs.readFileSync(cachePrebuildInfoPath).toString());
738    } catch {
739      removeHashJsonFile();
740      return;
741    }
742    // api version is 8 or 9
743    if (cachedJson && cachedJson.minAPIVersion &&
744      cachedJson.minAPIVersion.toString() !== process.env.minPlatformVersion) {
745      removeHashJsonFile();
746    }
747  }
748}
749
750function setPrebuildInfo() {
751  if (process.env.cachePath && process.env.minPlatformVersion) {
752    let cachedJson = {};
753    const cachePrebuildInfoPath = path.join(process.env.cachePath, PREBUILDINFO_JSON);
754    validateFilePathLength(cachePrebuildInfoPath);
755    cachedJson.minAPIVersion = process.env.minPlatformVersion;
756    fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8',
757      (err) => {
758        if (err) {
759          logger.error(red, `ArkTS:ERROR Failed to write bundle build info.`, reset);
760        }
761      }
762    );
763  }
764}
765
766function removeHashJsonFile() {
767  const hashFilePath = genHashJsonPath(buildPathInfo);
768  if (hashFilePath.length !== 0 && fs.existsSync(hashFilePath)) {
769    fs.unlinkSync(hashFilePath);
770  }
771}
772