* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as fs from 'fs';
import * as path from 'path';
import cluster from 'cluster';
import process from 'process';
import os from 'os';
import events from 'events';
import Compiler from 'webpack/lib/Compiler';
import { logger } from './compile_info';
import * as childProcess from 'child_process';
import {
toUnixPath,
toHashData,
mkdirsSync,
nodeLargeOrEqualTargetVersion,
removeDir,
validateFilePathLength,
unlinkSync,
isPackageModulesFile,
genTemporaryPath
} from './utils';
import {
buildCachePath,
genAbcFileName,
genBuildPath,
genMergeProtoFileName,
genProtoFileName,
getOhmUrlByFilepath,
getPackageInfo,
isEs2Abc,
isTs2Abc,
newSourceMaps,
removeDuplicateInfo
} from './ark_utils';
import { projectConfig } from '../main';
import {
ESMODULE,
JSBUNDLE,
NODE_MODULES,
ES2ABC,
EXTNAME_D_ETS,
EXTNAME_D_TS,
EXTNAME_ETS,
EXTNAME_JS,
EXTNAME_TS,
EXTNAME_MJS,
EXTNAME_CJS,
EXTNAME_JSON,
EXTNAME_JS_MAP,
FAIL,
MODULELIST_JSON,
MODULES_ABC,
PREBUILDINFO_JSON,
SUCCESS,
SOURCEMAPS_JSON,
SOURCEMAPS,
TEMPORARY,
TS2ABC,
PROTO_FILESINFO_TXT,
NPMENTRIES_TXT,
EXTNAME_PROTO_BIN,
FILESINFO_TXT,
MANAGE_WORKERS_SCRIPT,
MAX_WORKER_NUMBER,
GEN_ABC_SCRIPT,
GEN_MODULE_ABC_SCRIPT,
AOT_FULL,
AOT_PARTIAL,
PACKAGES
} from './pre_define';
import {
generateMergedAbc,
generateNpmEntriesInfo
} from './gen_merged_abc';
import {
generateAot,
FaultHandler
} from './gen_aot';
let output: string;
let isWin: boolean = false;
let isMac: boolean = false;
let isDebug: boolean = false;
let arkDir: string;
let nodeJs: string;
interface File {
path: string,
size: number,
cacheOutputPath: string,
sourceFile: string
}
let intermediateJsBundle: Array<File> = [];
let fileterIntermediateJsBundle: Array<File> = [];
let moduleInfos: Array<ModuleInfo> = [];
let filterModuleInfos: Array<ModuleInfo> = [];
let commonJsModuleInfos: Array<ModuleInfo> = [];
let ESMModuleInfos: Array<ModuleInfo> = [];
let entryInfos: Map<string, EntryInfo> = new Map<string, EntryInfo>();
let hashJsonObject = {};
let moduleHashJsonObject = {};
let buildPathInfo: string = '';
let buildMapFileList: Set<string> = new Set<string>();
let isHotReloadFirstBuild: boolean = true;
let protoFilePath: string = '';
const red: string = '\u001b[31m';
const reset: string = '\u001b[39m';
const blue = '\u001b[34m';
const hashFile: string = 'gen_hash.json';
const ARK: string = '/ark/';
export class ModuleInfo {
filePath: string;
tempFilePath: string;
buildFilePath: string;
abcFilePath: string;
isCommonJs: boolean;
recordName: string;
sourceFile: string;
packageName: string;
constructor(filePath: string, tempFilePath: string, buildFilePath: string,
abcFilePath: string, packageName: string, isCommonJs: boolean) {
this.filePath = filePath;
this.tempFilePath = tempFilePath;
this.buildFilePath = buildFilePath;
this.abcFilePath = abcFilePath;
this.packageName = packageName;
this.isCommonJs = isCommonJs;
this.recordName = getOhmUrlByFilepath(filePath, projectConfig, logger);
this.sourceFile = filePath.replace(projectConfig.projectRootPath + path.sep, '');
}
}
export class EntryInfo {
npmInfo: string;
buildPath: string;
entry: string;
constructor(npmInfo: string, buildPath: string, entry: string) {
this.npmInfo = npmInfo;
this.buildPath = buildPath;
this.entry = entry;
}
}
export class GenAbcPlugin {
constructor(output_, arkDir_, nodeJs_, isDebug_) {
output = output_;
arkDir = arkDir_;
nodeJs = nodeJs_;
isDebug = isDebug_;
}
apply(compiler: Compiler) {
if (fs.existsSync(path.resolve(arkDir, 'build-win'))) {
isWin = true;
} else {
if (fs.existsSync(path.resolve(arkDir, 'build-mac'))) {
isMac = true;
} else {
if (!fs.existsSync(path.resolve(arkDir, 'build'))) {
logger.error(red, 'ArkTS:ERROR find build fail', reset);
process.exitCode = FAIL;
return;
}
}
}
if (!checkNodeModules()) {
process.exitCode = FAIL;
return;
}
if (projectConfig.compileMode === ESMODULE) {
if (projectConfig.cachePath && !projectConfig.xtsMode) {
let cachedJson: Object = {};
const cachePrebuildInfoPath: string = path.join(projectConfig.cachePath, PREBUILDINFO_JSON);
validateFilePathLength(cachePrebuildInfoPath, logger);
cachedJson.buildMode = projectConfig.buildArkMode;
cachedJson.bundleName = projectConfig.bundleName;
cachedJson.moduleName = projectConfig.moduleName;
fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8',
(err) => {
if (err) {
logger.error(red, `ArkTS:ERROR Failed to write module build info.`, reset);
}
}
);
}
removeDir(output);
removeDir(projectConfig.nodeModulesPath);
}
if (projectConfig.compileMode === JSBUNDLE && process.env.minPlatformVersion) {
if (projectConfig.cachePath && !projectConfig.xtsMode) {
let cachedJson: Object = {};
const cachePrebuildInfoPath: string = path.join(projectConfig.cachePath, PREBUILDINFO_JSON);
validateFilePathLength(cachePrebuildInfoPath, logger);
cachedJson.minAPIVersion = process.env.minPlatformVersion;
fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8',
(err) => {
if (err) {
logger.error(red, `ArkTS:ERROR Failed to write bundle build info.`, reset);
}
}
);
}
}
events.EventEmitter.defaultMaxListeners = 100;
compiler.hooks.compilation.tap('GenAbcPlugin', (compilation) => {
if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
return;
}
buildPathInfo = output;
compilation.hooks.finishModules.tap('finishModules', handleFinishModules.bind(this));
});
compiler.hooks.compilation.tap('GenAbcPlugin', (compilation) => {
compilation.hooks.processAssets.tap('processAssets', (assets) => {
if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
return;
}
let newAssets: Object = {};
Object.keys(compilation.assets).forEach(key => {
if (path.extname(key) !== EXTNAME_JS && path.extname(key) !== EXTNAME_JS_MAP) {
newAssets[key] = assets[key];
}
});
compilation.assets = newAssets;
});
});
compiler.hooks.emit.tap('GenAbcPlugin', (compilation) => {
if (projectConfig.compileMode === ESMODULE) {
return;
}
Object.keys(compilation.assets).forEach(key => {
if (output && path.extname(key) === EXTNAME_JS) {
const newContent: string = compilation.assets[key].source();
const keyPath: string = key.replace(/\.js$/, '.temp.js');
writeFileSync(newContent, output, keyPath, key);
}
});
});
compiler.hooks.afterEmit.tap('GenAbcPluginMultiThread', () => {
if (projectConfig.compileMode === ESMODULE) {
return;
}
if (intermediateJsBundle.length === 0) {
return;
}
buildPathInfo = output;
if (isTs2Abc(projectConfig) || process.env.minPlatformVersion === '8') {
invokeWorkersToGenAbc();
} else if (isEs2Abc(projectConfig)) {
generateAbcByEs2AbcOfBundleMode(intermediateJsBundle);
} else {
logger.error(red, `ArkTS:ERROR please set panda module`, reset);
}
});
}
}
function clearGlobalInfo() {
if (process.env.watchMode !== 'true') {
intermediateJsBundle = [];
moduleInfos = [];
entryInfos = new Map<string, EntryInfo>();
}
fileterIntermediateJsBundle = [];
filterModuleInfos = [];
commonJsModuleInfos = [];
ESMModuleInfos = [];
hashJsonObject = {};
moduleHashJsonObject = {};
buildMapFileList = new Set<string>();
}
function getEntryInfo(filePath: string, resourceResolveData: Object): string {
if (!resourceResolveData.descriptionFilePath) {
return '';
}
let isEntry: boolean = false;
let mainFileds: Set<string> = getEntryCandidatesFromPackageJson(resourceResolveData);
for (let value of mainFileds.values()) {
if (toUnixPath(filePath) === value) {
isEntry = true;
break;
}
}
const packageJsonPath: string = resourceResolveData.descriptionFilePath;
let npmInfoPath: string = path.resolve(packageJsonPath, '..');
let entry: string = toUnixPath(filePath.replace(npmInfoPath, ''));
if (entry.startsWith('/')) {
entry = entry.slice(1, entry.length);
}
const fakeEntryPath: string = path.resolve(npmInfoPath, 'fake.js');
const tempFakeEntryPath: string = genTemporaryPath(fakeEntryPath, projectConfig.projectPath, process.env.cachePath,
projectConfig, undefined);
const buildFakeEntryPath: string = genBuildPath(fakeEntryPath, projectConfig.projectPath, projectConfig.buildPath,
projectConfig);
npmInfoPath = toUnixPath(path.resolve(tempFakeEntryPath, '..'));
const buildNpmInfoPath: string = toUnixPath(path.resolve(buildFakeEntryPath, '..'));
if (!entryInfos.has(npmInfoPath) && isEntry) {
const entryInfo: EntryInfo = new EntryInfo(npmInfoPath, buildNpmInfoPath, entry);
entryInfos.set(npmInfoPath, entryInfo);
}
return buildNpmInfoPath;
}
function getEntryCandidatesFromPackageJson(resourceResolveData: Object): Set<string> {
let descriptionFileData: Object = resourceResolveData.descriptionFileData;
let packagePath: string = path.resolve(resourceResolveData.descriptionFilePath, '..');
let mainFileds: Set<string> = new Set<string>();
if (descriptionFileData.browser) {
if (typeof descriptionFileData.browser === 'string') {
mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.browser)));
} else {
Object.keys(descriptionFileData.browser).forEach(key => {
if (typeof key === 'string' && typeof descriptionFileData.browser[key] === 'string') {
mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.browser[key])));
}
});
}
}
if (descriptionFileData.module) {
mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.module)));
}
if (descriptionFileData.main) {
mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.main)));
}
if (mainFileds.size === 0) {
mainFileds.add(toUnixPath(path.join(packagePath, 'index.js')));
mainFileds.add(toUnixPath(path.join(packagePath, 'index.ets')));
mainFileds.add(toUnixPath(path.join(packagePath, 'index.ts')));
}
return mainFileds;
}
function processNodeModulesFile(filePath: string, tempFilePath: string, buildFilePath: string,
abcFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
let npmPkgPath: string = getEntryInfo(filePath, module.resourceResolveData);
const buildNpmPkgPath: string = npmPkgPath.replace(toUnixPath(projectConfig.nodeModulesPath), '');
const npmPkgName: string = toUnixPath(path.join(PACKAGES, buildNpmPkgPath)).replace(new RegExp(NODE_MODULES, 'g'), PACKAGES);
const descriptionFileData: Object = module.resourceResolveData.descriptionFileData;
if (descriptionFileData && descriptionFileData.type && descriptionFileData.type === 'module') {
const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, false);
moduleInfos.push(tempModuleInfo);
nodeModulesFile.push(tempFilePath);
} else if (filePath.endsWith(EXTNAME_MJS)) {
const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, false);
moduleInfos.push(tempModuleInfo);
nodeModulesFile.push(tempFilePath);
} else {
const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, true);
moduleInfos.push(tempModuleInfo);
nodeModulesFile.push(tempFilePath);
}
if (!filePath.endsWith(EXTNAME_JSON)) {
buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
}
}
function processEtsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
if (filePath.endsWith(EXTNAME_D_ETS)) {
return;
}
if (projectConfig.processTs === true) {
tempFilePath = tempFilePath.replace(/\.ets$/, EXTNAME_TS);
buildFilePath = buildFilePath.replace(/\.ets$/, EXTNAME_TS);
} else {
tempFilePath = tempFilePath.replace(/\.ets$/, EXTNAME_JS);
buildFilePath = buildFilePath.replace(/\.ets$/, EXTNAME_JS);
}
const abcFilePath: string = genAbcFileName(tempFilePath);
if (isPackageModulesFile(filePath, projectConfig)) {
processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
} else {
const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
moduleInfos.push(tempModuleInfo);
}
buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
}
function processTsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
if (filePath.endsWith(EXTNAME_D_TS)) {
return;
}
if (projectConfig.processTs === false) {
tempFilePath = tempFilePath.replace(/\.ts$/, EXTNAME_JS);
buildFilePath = buildFilePath.replace(/\.ts$/, EXTNAME_JS);
}
const abcFilePath: string = genAbcFileName(tempFilePath);
if (isPackageModulesFile(filePath, projectConfig)) {
processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
} else {
const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
moduleInfos.push(tempModuleInfo);
}
buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
}
function processJsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
const parent: string = path.join(tempFilePath, '..');
if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
mkDir(parent);
}
if (filePath.endsWith(EXTNAME_MJS) || filePath.endsWith(EXTNAME_CJS)) {
fs.copyFileSync(filePath, tempFilePath);
}
const abcFilePath: string = genAbcFileName(tempFilePath);
if (isPackageModulesFile(filePath, projectConfig)) {
processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
} else {
const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
moduleInfos.push(tempModuleInfo);
}
buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, '')));
}
function processJsonModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void {
const abcFilePath: string = 'NA';
if (isPackageModulesFile(filePath, projectConfig)) {
processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module);
} else {
const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1];
const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false);
moduleInfos.push(tempModuleInfo);
}
}
let cachedSourceMaps: Object;
function updateCachedSourceMaps(): void {
const CACHED_SOURCEMAPS: string = path.join(process.env.cachePath, SOURCEMAPS_JSON);
validateFilePathLength(CACHED_SOURCEMAPS, logger);
if (!fs.existsSync(CACHED_SOURCEMAPS)) {
cachedSourceMaps = {};
} else {
cachedSourceMaps = JSON.parse(fs.readFileSync(CACHED_SOURCEMAPS).toString());
}
Object.keys(newSourceMaps).forEach(key => {
cachedSourceMaps[key] = newSourceMaps[key];
});
}
function getCachedModuleList(): Array<string> {
const CACHED_MODULELIST_FILE: string = path.join(process.env.cachePath, MODULELIST_JSON);
validateFilePathLength(CACHED_MODULELIST_FILE, logger);
if (!fs.existsSync(CACHED_MODULELIST_FILE)) {
return [];
}
const data: Object = JSON.parse(fs.readFileSync(CACHED_MODULELIST_FILE).toString());
const moduleList: Array<string> = data.list;
return moduleList;
}
function updateCachedModuleList(moduleList: Array<string>): void {
const CACHED_MODULELIST_FILE: string = path.join(process.env.cachePath, MODULELIST_JSON);
validateFilePathLength(CACHED_MODULELIST_FILE, logger);
const CACHED_SOURCEMAPS: string = path.join(process.env.cachePath, SOURCEMAPS_JSON);
validateFilePathLength(CACHED_SOURCEMAPS, logger);
let cachedJson: Object = {};
cachedJson.list = moduleList;
fs.writeFile(CACHED_MODULELIST_FILE, JSON.stringify(cachedJson, null, 2), 'utf-8',
(err) => {
if (err) {
logger.error(red, `ArkTS:ERROR Failed to write module list.`, reset);
}
}
);
fs.writeFile(CACHED_SOURCEMAPS, JSON.stringify(cachedSourceMaps, null, 2), 'utf-8',
(err) => {
if (err) {
logger.error(red, `ArkTS:ERROR Failed to write cache sourceMaps json.`, reset);
}
}
);
}
function writeSourceMaps(): void {
mkdirsSync(projectConfig.buildPath);
let sourceMapFilePath: string = path.join(projectConfig.buildPath, SOURCEMAPS);
validateFilePathLength(sourceMapFilePath, logger);
fs.writeFile(sourceMapFilePath, JSON.stringify(cachedSourceMaps, null, 2), 'utf-8',
(err) => {
if (err) {
logger.error(red, `ArkTS:ERROR Failed to write sourceMaps.`, reset);
}
}
);
}
function eliminateUnusedFiles(moduleList: Array<string>): void {
let cachedModuleList: Array<string> = getCachedModuleList();
if (cachedModuleList.length !== 0) {
let newCacheSourceMaps: Object = {};
cachedModuleList.forEach((file) => {
if (moduleList.includes(file)) {
newCacheSourceMaps[file] = cachedSourceMaps[file];
}
});
cachedSourceMaps = newCacheSourceMaps;
}
}
function handleFullModuleFiles(modules, callback): void {
const nodeModulesFile: Array<string> = [];
modules.forEach(module => {
if (module !== undefined && module.resourceResolveData !== undefined) {
const filePath: string = module.resourceResolveData.path;
let tempFilePath = genTemporaryPath(filePath, projectConfig.projectPath, process.env.cachePath,
projectConfig, undefined);
if (tempFilePath.length === 0) {
return;
}
validateFilePathLength(tempFilePath, logger);
let buildFilePath: string = genBuildPath(filePath, projectConfig.projectPath, projectConfig.buildPath,
projectConfig);
validateFilePathLength(buildFilePath, logger);
tempFilePath = toUnixPath(tempFilePath);
buildFilePath = toUnixPath(buildFilePath);
switch (path.extname(filePath)) {
case EXTNAME_ETS: {
processEtsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
break;
}
case EXTNAME_TS: {
processTsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
break;
}
case EXTNAME_JS:
case EXTNAME_MJS:
case EXTNAME_CJS: {
processJsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
break;
}
case EXTNAME_JSON: {
processJsonModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module);
break;
}
default: {
logger.error(red, `ArkTS:ERROR Cannot find resolve this file path: ${filePath}`, reset);
process.exitCode = FAIL;
}
}
}
});
if (projectConfig.buildArkMode === 'debug') {
const moduleList: Array<string> = Array.from(buildMapFileList);
updateCachedSourceMaps();
eliminateUnusedFiles(moduleList);
updateCachedModuleList(moduleList);
writeSourceMaps();
}
if (process.env.panda !== TS2ABC) {
const outputABCPath: string = path.join(projectConfig.buildPath, MODULES_ABC);
validateFilePathLength(outputABCPath, logger);
generateMergedAbc(moduleInfos, entryInfos, outputABCPath);
clearGlobalInfo();
} else {
invokeWorkersModuleToGenAbc(moduleInfos);
}
}
function processEntryToGenAbc(entryInfos: Map<string, EntryInfo>): void {
if (entryInfos.size <= 0) {
return;
}
generateNpmEntriesInfo(entryInfos);
const npmEntriesInfoPath: string = path.join(process.env.cachePath, NPMENTRIES_TXT);
validateFilePathLength(npmEntriesInfoPath, logger);
let npmEntriesProtoFileName: string = 'npm_entries' + EXTNAME_PROTO_BIN;
const npmEntriesProtoFilePath: string = path.join(process.env.cachePath, 'protos', 'npm_entries', npmEntriesProtoFileName);
validateFilePathLength(npmEntriesProtoFilePath, logger);
mkdirsSync(path.dirname(npmEntriesProtoFilePath));
let js2Abc: string = path.join(arkDir, 'build', 'bin', 'js2abc');
if (isWin) {
js2Abc = path.join(arkDir, 'build-win', 'bin', 'js2abc.exe');
} else if (isMac) {
js2Abc = path.join(arkDir, 'build-mac', 'bin', 'js2abc');
}
validateFilePathLength(js2Abc, logger);
const singleCmd: string = `"${js2Abc}" --compile-npm-entries "${npmEntriesInfoPath}" "${npmEntriesProtoFilePath}`;
try {
childProcess.execSync(singleCmd);
} catch (e) {
logger.debug(red, `ArkTS:ERROR Failed to generate npm proto file to abc, Error message: ${e}`, reset);
}
}
function writeFileSync(inputString: string, buildPath: string, keyPath: string, jsBundleFile: string): void {
let output = path.resolve(buildPath, keyPath);
validateFilePathLength(output, logger);
let parent: string = path.join(output, '..');
if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
mkDir(parent);
}
let cacheOutputPath: string = '';
if (process.env.cachePath) {
let buildDirArr: string[] = projectConfig.buildPath.split(path.sep);
let abilityDir: string = buildDirArr[buildDirArr.length - 1];
cacheOutputPath = path.join(process.env.cachePath, TEMPORARY, abilityDir, keyPath);
} else {
cacheOutputPath = output;
}
validateFilePathLength(cacheOutputPath, logger);
parent = path.join(cacheOutputPath, '..');
if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
mkDir(parent);
}
fs.writeFileSync(cacheOutputPath, inputString);
if (fs.existsSync(cacheOutputPath)) {
const fileSize: number = fs.statSync(cacheOutputPath).size;
let sourceFile: string = output.replace(/\.temp\.js$/, '_.js');
if (projectConfig.projectRootPath) {
sourceFile = toUnixPath(sourceFile.replace(projectConfig.projectRootPath + path.sep, ''));
} else {
sourceFile = toUnixPath(sourceFile);
}
output = toUnixPath(output);
cacheOutputPath = toUnixPath(cacheOutputPath);
intermediateJsBundle.push({path: output, size: fileSize, cacheOutputPath: cacheOutputPath, sourceFile: sourceFile});
} else {
logger.debug(red, `ArkTS:ERROR Failed to convert file ${jsBundleFile} to bin. ${output} is lost`, reset);
process.exitCode = FAIL;
}
}
function mkDir(path_: string): void {
const parent: string = path.join(path_, '..');
if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
mkDir(parent);
}
fs.mkdirSync(path_);
}
function getSmallestSizeGroup(groupSize: Map<number, number>): any {
const groupSizeArray: any = Array.from(groupSize);
groupSizeArray.sort(function(g1, g2) {
return g1[1] - g2[1];
});
return groupSizeArray[0][0];
}
function splitJsBundlesBySize(bundleArray: Array<File>, groupNumber: number): any {
const result: any = [];
if (bundleArray.length < groupNumber) {
for (const value of bundleArray) {
result.push([value]);
}
return result;
}
bundleArray.sort(function(f1: File, f2: File) {
return f2.size - f1.size;
});
const groupFileSize = new Map();
for (let i = 0; i < groupNumber; ++i) {
result.push([]);
groupFileSize.set(i, 0);
}
let index = 0;
while (index < bundleArray.length) {
const smallestGroup: any = getSmallestSizeGroup(groupFileSize);
result[smallestGroup].push(bundleArray[index]);
const sizeUpdate: any = groupFileSize.get(smallestGroup) + bundleArray[index].size;
groupFileSize.set(smallestGroup, sizeUpdate);
index++;
}
return result;
}
function invokeWorkersModuleToGenAbc(moduleInfos: Array<ModuleInfo>): void {
invokeClusterModuleToAbc();
}
export function initAbcEnv() : string[] {
let args: string[] = [];
if (process.env.minPlatformVersion === '8') {
process.env.panda = TS2ABC;
let js2abc: string = path.join(arkDir, 'build', 'legacy_api8', 'src', 'index.js');
if (isWin) {
js2abc = path.join(arkDir, 'build-win', 'legacy_api8', 'src', 'index.js');
} else if (isMac) {
js2abc = path.join(arkDir, 'build-mac', 'legacy_api8', 'src', 'index.js');
}
validateFilePathLength(js2abc, logger);
js2abc = '"' + js2abc + '"';
args = [
'--expose-gc',
js2abc
];
if (isDebug) {
args.push('--debug');
}
} else if (process.env.panda === TS2ABC) {
let js2abc: string = path.join(arkDir, 'build', 'src', 'index.js');
if (isWin) {
js2abc = path.join(arkDir, 'build-win', 'src', 'index.js');
} else if (isMac) {
js2abc = path.join(arkDir, 'build-mac', 'src', 'index.js');
}
validateFilePathLength(js2abc, logger);
js2abc = '"' + js2abc + '"';
args = [
'--expose-gc',
js2abc
];
if (isDebug) {
args.push('--debug');
}
} else if (process.env.panda === ES2ABC || process.env.panda === 'undefined' || process.env.panda === undefined) {
let es2abc: string = path.join(arkDir, 'build', 'bin', 'es2abc');
if (isWin) {
es2abc = path.join(arkDir, 'build-win', 'bin', 'es2abc.exe');
} else if (isMac) {
es2abc = path.join(arkDir, 'build-mac', 'bin', 'es2abc');
}
validateFilePathLength(es2abc, logger);
args = [
'"' + es2abc + '"'
];
if (isDebug) {
args.push('--debug-info');
}
if (projectConfig.compileMode === ESMODULE) {
args.push('--merge-abc');
}
} else {
logger.error(red, `ArkTS:ERROR please set panda module`, reset);
}
return args;
}
function invokeClusterModuleToAbc(): void {
if (process.env.watchMode === 'true') {
process.exitCode = SUCCESS;
}
filterIntermediateModuleByHashJson(buildPathInfo, moduleInfos);
const abcArgs: string[] = initAbcEnv();
const splitedModules: any[] = splitModulesByNumber(filterModuleInfos, MAX_WORKER_NUMBER);
let cmdPrefix: string = `${nodeJs} ${abcArgs.join(' ')}`;
const workerNumber: number = MAX_WORKER_NUMBER < splitedModules.length ? MAX_WORKER_NUMBER : splitedModules.length;
try {
if (process.env.watchMode === 'true') {
processWorkersOfPreviewMode(splitedModules, cmdPrefix, workerNumber);
} else {
processWorkersOfBuildMode(splitedModules, cmdPrefix, workerNumber);
}
} catch (e) {
logger.debug(red, `ArkTS:ERROR failed to generate abc. Error message: ${e}`, reset);
process.env.abcCompileSuccess = 'false';
if (process.env.watchMode !== 'true') {
process.exit(FAIL);
}
}
}
function splitModulesByNumber(moduleInfos: Array<ModuleInfo>, workerNumber: number): any[] {
const result: any = [];
if (moduleInfos.length < workerNumber) {
for (const value of moduleInfos) {
result.push([value]);
}
return result;
}
for (let i = 0; i < workerNumber; ++i) {
result.push([]);
}
for (let i = 0; i < moduleInfos.length; i++) {
const chunk = i % workerNumber;
result[chunk].push(moduleInfos[i]);
}
return result;
}
function invokeWorkersToGenAbc(): void {
if (process.env.watchMode === 'true') {
process.exitCode = SUCCESS;
}
let cmdPrefix: string = '';
const abcArgs: string[] = initAbcEnv();
if (process.env.panda === TS2ABC) {
cmdPrefix = `${nodeJs} ${abcArgs.join(' ')}`;
} else {
logger.error(red, `ArkTS:ERROR please set panda module`, reset);
}
filterIntermediateJsBundleByHashJson(buildPathInfo, intermediateJsBundle);
const splitedBundles: any[] = splitJsBundlesBySize(fileterIntermediateJsBundle, MAX_WORKER_NUMBER);
const workerNumber: number = MAX_WORKER_NUMBER < splitedBundles.length ? MAX_WORKER_NUMBER : splitedBundles.length;
try {
if (process.env.watchMode === 'true') {
processWorkersOfPreviewMode(splitedBundles, cmdPrefix, workerNumber);
} else {
processWorkersOfBuildMode(splitedBundles, cmdPrefix, workerNumber);
}
} catch (e) {
logger.debug(red, `ArkTS:ERROR failed to generate abc. Error message: ${e}`, reset);
process.env.abcCompileSuccess = 'false';
if (process.env.watchMode !== 'true') {
process.exit(FAIL);
}
}
}
function filterIntermediateModuleByHashJson(buildPath: string, moduleInfos: Array<ModuleInfo>): void {
const tempModuleInfos = Array<ModuleInfo>();
moduleInfos.forEach((item) => {
const check = tempModuleInfos.every((newItem) => {
return item.tempFilePath !== newItem.tempFilePath;
});
if (check) {
tempModuleInfos.push(item);
}
});
moduleInfos = tempModuleInfos;
for (let i = 0; i < moduleInfos.length; ++i) {
filterModuleInfos.push(moduleInfos[i]);
}
const hashFilePath: string = genHashJsonPath(buildPath);
if (hashFilePath.length === 0) {
return;
}
const updateJsonObject: Object = {};
let jsonObject: Object = {};
let jsonFile: string = '';
if (fs.existsSync(hashFilePath)) {
jsonFile = fs.readFileSync(hashFilePath).toString();
jsonObject = JSON.parse(jsonFile);
filterModuleInfos = [];
for (let i = 0; i < moduleInfos.length; ++i) {
const input: string = moduleInfos[i].tempFilePath;
let outputPath: string = genProtoFileName(moduleInfos[i].tempFilePath);
if (!fs.existsSync(input)) {
logger.debug(red, `ArkTS:ERROR ${input} is lost`, reset);
process.exitCode = FAIL;
break;
}
if (fs.existsSync(outputPath)) {
const hashInputContentData: string = toHashData(input);
const hashAbcContentData: string = toHashData(outputPath);
if (jsonObject[input] === hashInputContentData && jsonObject[outputPath] === hashAbcContentData) {
updateJsonObject[input] = hashInputContentData;
updateJsonObject[outputPath] = hashAbcContentData;
} else {
filterModuleInfos.push(moduleInfos[i]);
}
} else {
filterModuleInfos.push(moduleInfos[i]);
}
}
}
moduleHashJsonObject = updateJsonObject;
}
function writeModuleHashJson(): void {
for (let i = 0; i < filterModuleInfos.length; ++i) {
const input: string = filterModuleInfos[i].tempFilePath;
let outputPath: string = genProtoFileName(filterModuleInfos[i].tempFilePath);
if (!fs.existsSync(input) || !fs.existsSync(outputPath)) {
logger.debug(red, `ArkTS:ERROR ${input} is lost`, reset);
process.exitCode = FAIL;
break;
}
const hashInputContentData: string = toHashData(input);
const hashOutputContentData: string = toHashData(outputPath);
moduleHashJsonObject[input] = hashInputContentData;
moduleHashJsonObject[outputPath] = hashOutputContentData;
}
const hashFilePath: string = genHashJsonPath(buildPathInfo);
if (hashFilePath.length === 0) {
return;
}
fs.writeFileSync(hashFilePath, JSON.stringify(moduleHashJsonObject));
}
function filterIntermediateJsBundleByHashJson(buildPath: string, inputPaths: File[]): void {
inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
for (let i = 0; i < inputPaths.length; ++i) {
fileterIntermediateJsBundle.push(inputPaths[i]);
}
const hashFilePath: string = genHashJsonPath(buildPath);
if (hashFilePath.length === 0) {
return;
}
const updateJsonObject: Object = {};
let jsonObject: Object = {};
let jsonFile: string = '';
if (fs.existsSync(hashFilePath)) {
jsonFile = fs.readFileSync(hashFilePath).toString();
jsonObject = JSON.parse(jsonFile);
fileterIntermediateJsBundle = [];
for (let i = 0; i < inputPaths.length; ++i) {
const cacheOutputPath: string = inputPaths[i].cacheOutputPath;
const cacheAbcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
if (!fs.existsSync(cacheOutputPath)) {
logger.debug(red, `ArkTS:ERROR ${cacheOutputPath} is lost`, reset);
process.exitCode = FAIL;
break;
}
if (fs.existsSync(cacheAbcFilePath)) {
const hashInputContentData: string = toHashData(cacheOutputPath);
const hashAbcContentData: string = toHashData(cacheAbcFilePath);
if (jsonObject[cacheOutputPath] === hashInputContentData && jsonObject[cacheAbcFilePath] === hashAbcContentData) {
updateJsonObject[cacheOutputPath] = hashInputContentData;
updateJsonObject[cacheAbcFilePath] = hashAbcContentData;
} else {
fileterIntermediateJsBundle.push(inputPaths[i]);
}
} else {
fileterIntermediateJsBundle.push(inputPaths[i]);
}
}
}
hashJsonObject = updateJsonObject;
}
function writeHashJson(): void {
for (let i = 0; i < fileterIntermediateJsBundle.length; ++i) {
const cacheOutputPath: string = fileterIntermediateJsBundle[i].cacheOutputPath;
const cacheAbcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
if (!fs.existsSync(cacheOutputPath) || !fs.existsSync(cacheAbcFilePath)) {
logger.debug(red, `ArkTS:ERROR ${cacheOutputPath} is lost`, reset);
process.exitCode = FAIL;
break;
}
const hashInputContentData: string = toHashData(cacheOutputPath);
const hashAbcContentData: string = toHashData(cacheAbcFilePath);
hashJsonObject[cacheOutputPath] = hashInputContentData;
hashJsonObject[cacheAbcFilePath] = hashAbcContentData;
}
const hashFilePath: string = genHashJsonPath(buildPathInfo);
if (hashFilePath.length === 0) {
return;
}
fs.writeFileSync(hashFilePath, JSON.stringify(hashJsonObject));
}
function genHashJsonPath(buildPath: string): string {
buildPath = toUnixPath(buildPath);
if (process.env.cachePath) {
if (!fs.existsSync(process.env.cachePath) || !fs.statSync(process.env.cachePath).isDirectory()) {
logger.debug(red, `ArkTS:ERROR hash path does not exist`, reset);
return '';
}
let buildDirArr: string[] = projectConfig.buildPath.split(path.sep);
let abilityDir: string = buildDirArr[buildDirArr.length - 1];
let hashJsonPath: string = path.join(process.env.cachePath, TEMPORARY, abilityDir, hashFile);
validateFilePathLength(hashJsonPath, logger);
mkdirsSync(path.dirname(hashJsonPath));
return hashJsonPath;
} else if (buildPath.indexOf(ARK) >= 0) {
const dataTmps: string[] = buildPath.split(ARK);
const hashPath: string = path.join(dataTmps[0], ARK);
if (!fs.existsSync(hashPath) || !fs.statSync(hashPath).isDirectory()) {
logger.debug(red, `ArkTS:ERROR hash path does not exist`, reset);
return '';
}
let hashJsonPath: string = path.join(hashPath, hashFile);
validateFilePathLength(hashJsonPath, logger);
return hashJsonPath;
} else {
logger.debug(red, `ArkTS:ERROR not cache exist`, reset);
return '';
}
}
function checkNodeModules() {
if (process.env.panda === TS2ABC) {
let arkEntryPath: string = path.join(arkDir, 'build');
if (isWin) {
arkEntryPath = path.join(arkDir, 'build-win');
} else if (isMac) {
arkEntryPath = path.join(arkDir, 'build-mac');
}
let nodeModulesPath: string = path.join(arkEntryPath, NODE_MODULES);
validateFilePathLength(nodeModulesPath, logger);
if (!(fs.existsSync(nodeModulesPath) && fs.statSync(nodeModulesPath).isDirectory())) {
logger.error(red, `ERROR: node_modules for ark compiler not found.
Please make sure switch to non-root user before runing "npm install" for safity requirements and try re-run "npm install" under ${arkEntryPath}`, reset);
return false;
}
}
return true;
}
function copyFileCachePathToBuildPath() {
for (let i = 0; i < intermediateJsBundle.length; ++i) {
const abcFile: string = intermediateJsBundle[i].path.replace(/\.temp\.js$/, '.abc');
const cacheOutputPath: string = intermediateJsBundle[i].cacheOutputPath;
const cacheAbcFilePath: string = intermediateJsBundle[i].cacheOutputPath.replace(/\.temp\.js$/, '.abc');
if (!fs.existsSync(cacheAbcFilePath)) {
logger.debug(red, `ArkTS:ERROR ${cacheAbcFilePath} is lost`, reset);
process.exitCode = FAIL;
break;
}
let parent: string = path.join(abcFile, '..');
if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
mkDir(parent);
}
if (process.env.cachePath !== undefined) {
fs.copyFileSync(cacheAbcFilePath, abcFile);
}
if (process.env.cachePath === undefined && fs.existsSync(cacheOutputPath)) {
fs.unlinkSync(cacheOutputPath);
}
}
}
function processExtraAsset() {
if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
writeHashJson();
copyFileCachePathToBuildPath();
} else if (projectConfig.compileMode === ESMODULE) {
processEntryToGenAbc(entryInfos);
writeModuleHashJson();
copyModuleFileCachePathToBuildPath();
mergeProtoToAbc();
}
clearGlobalInfo();
}
function handleHotReloadChangedFiles() {
if (!fs.existsSync(projectConfig.changedFileList)) {
logger.debug(blue, `ArkTS: Cannot find file: ${projectConfig.changedFileList}, skip hot reload build`, reset);
return;
}
let changedFileListJson: string = fs.readFileSync(projectConfig.changedFileList).toString();
let changedFileList: Array<string> = JSON.parse(changedFileListJson).modifiedFiles;
if (typeof(changedFileList) === 'undefined' || changedFileList.length === 0) {
return;
}
let relativeProjectPath = projectConfig.projectPath.slice(projectConfig.projectRootPath.length + path.sep.length);
const nodeModulesFile: Array<string> = [];
let hotReloadSourceMap: Object = {};
moduleInfos = [];
for (let file of changedFileList) {
let filePath: string = path.join(projectConfig.projectPath, file);
validateFilePathLength(filePath, logger);
let tempFilePath: string = genTemporaryPath(filePath, projectConfig.projectPath, process.env.cachePath,
projectConfig, undefined);
if (tempFilePath.length === 0) {
return;
}
validateFilePathLength(tempFilePath, logger);
let buildFilePath: string = '';
tempFilePath = toUnixPath(tempFilePath);
switch (path.extname(filePath)) {
case EXTNAME_ETS: {
processEtsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined);
break;
}
case EXTNAME_TS: {
processTsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined);
break;
}
case EXTNAME_JS:
case EXTNAME_MJS:
case EXTNAME_CJS: {
processJsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined);
break;
}
case EXTNAME_JSON: {
logger.debug(blue, `ArkTS: json source file: ${filePath} changed, skip hot reload build`, reset);
return;
}
default: {
logger.debug(blue, `ArkTS:ERROR Cannot resolve file path: ${filePath}, stop hot reload build`, reset);
return;
}
}
let sourceMapPath: string = toUnixPath(path.join(relativeProjectPath, file));
validateFilePathLength(sourceMapPath, logger);
hotReloadSourceMap[sourceMapPath] = newSourceMaps[sourceMapPath];
}
if (!fs.existsSync(projectConfig.patchAbcPath)) {
mkdirsSync(projectConfig.patchAbcPath);
}
const outputABCPath: string = path.join(projectConfig.patchAbcPath, MODULES_ABC);
validateFilePathLength(outputABCPath, logger);
generateMergedAbc(moduleInfos, entryInfos, outputABCPath);
let sourceMapFilePath: string = path.join(projectConfig.patchAbcPath, SOURCEMAPS);
validateFilePathLength(sourceMapFilePath, logger);
fs.writeFileSync(sourceMapFilePath,
JSON.stringify(hotReloadSourceMap, null, 2), 'utf-8');
}
function handleFinishModules(modules, callback) {
if (projectConfig.hotReload && !isHotReloadFirstBuild) {
handleHotReloadChangedFiles();
return;
}
handleFullModuleFiles(modules, callback);
if (projectConfig.hotReload) {
isHotReloadFirstBuild = false;
}
}
function copyModuleFileCachePathToBuildPath(): void {
protoFilePath = path.join(path.join(process.env.cachePath, 'protos', PROTO_FILESINFO_TXT));
validateFilePathLength(protoFilePath, logger);
mkdirsSync(path.dirname(protoFilePath));
let entriesInfo: string = '';
moduleInfos = removeDuplicateInfo(moduleInfos);
moduleInfos.sort((m1: ModuleInfo, m2: ModuleInfo) => {
return m1.tempFilePath < m2.tempFilePath ? 1 : -1;
});
for (let i = 0; i < moduleInfos.length; ++i) {
let protoTempPath: string = genProtoFileName(moduleInfos[i].tempFilePath);
entriesInfo += `${toUnixPath(protoTempPath)}\n`;
}
if (entryInfos.size > 0) {
let npmEntriesProtoFileName: string = 'npm_entries' + EXTNAME_PROTO_BIN;
const npmEntriesProtoFilePath: string = path.join(process.env.cachePath, 'protos', 'npm_entries', npmEntriesProtoFileName);
entriesInfo += `${toUnixPath(npmEntriesProtoFilePath)}\n`;
}
fs.writeFileSync(protoFilePath, entriesInfo, 'utf-8');
}
function mergeProtoToAbc(): void {
let mergeAbc: string = path.join(arkDir, 'build', 'bin', 'merge_abc');
if (isWin) {
mergeAbc = path.join(arkDir, 'build-win', 'bin', 'merge_abc.exe');
} else if (isMac) {
mergeAbc = path.join(arkDir, 'build-mac', 'bin', 'merge_abc');
}
mkdirsSync(projectConfig.buildPath);
const singleCmd: string = `"${mergeAbc}" --input "@${protoFilePath}" --outputFilePath "${projectConfig.buildPath}" --output ${MODULES_ABC} --suffix protoBin`;
try {
childProcess.execSync(singleCmd);
} catch (e) {
logger.debug(red, `ArkTS:ERROR Failed to merge proto file to abc. Error message: ${e}`, reset);
}
}
function generateAbcByEs2AbcOfBundleMode(inputPaths: File[]) {
filterIntermediateJsBundleByHashJson(buildPathInfo, inputPaths);
if (fileterIntermediateJsBundle.length === 0) {
processExtraAsset();
return;
}
let filesInfoPath = generateFileOfBundle(fileterIntermediateJsBundle);
const fileThreads = os.cpus().length < 16 ? os.cpus().length : 16;
let genAbcCmd: string =
`${initAbcEnv().join(' ')} "@${filesInfoPath}" --file-threads "${fileThreads}"`;
logger.debug('gen abc cmd is: ', genAbcCmd);
try {
if (process.env.watchMode === 'true') {
childProcess.execSync(genAbcCmd);
processExtraAsset();
} else {
const child = childProcess.exec(genAbcCmd);
child.on('exit', (code: any) => {
if (code === FAIL) {
logger.debug(red, 'ArkTS:ERROR failed to execute es2abc', reset);
process.exit(FAIL);
}
if (process.env.cachePath === undefined) {
unlinkSync(filesInfoPath);
}
processExtraAsset();
});
child.on('error', (err: any) => {
logger.debug(red, err.toString(), reset);
process.exit(FAIL);
});
child.stderr.on('data', (data: any) => {
logger.error(red, data.toString(), reset);
});
}
} catch (e) {
logger.debug(red, `ArkTS:ERROR failed to generate abc with filesInfo ${filesInfoPath}. Error message: ${e} `, reset);
process.env.abcCompileSuccess = 'false';
if (process.env.watchMode !== 'true') {
process.exit(FAIL);
}
} finally {
if (process.env.watchMode === 'true') {
if (process.env.cachePath === undefined) {
unlinkSync(filesInfoPath);
}
}
}
}
function generateFileOfBundle(inputPaths: File[]): string {
let filesInfoPath: string = buildCachePath(FILESINFO_TXT, projectConfig, logger);
inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
let filesInfo: string = '';
inputPaths.forEach(info => {
const cacheOutputPath: string = info.cacheOutputPath;
const recordName: string = 'null_recordName';
const moduleType: string = 'script';
const sourceFile: string = info.sourceFile;
const abcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
filesInfo += `${cacheOutputPath};${recordName};${moduleType};${sourceFile};${abcFilePath}\n`;
});
fs.writeFileSync(filesInfoPath, filesInfo, 'utf-8');
return filesInfoPath;
}
function removeDuplicateInfoOfBundleList(inputPaths: File[]) {
const tempInputPaths = Array<File>();
inputPaths.forEach((item) => {
const check = tempInputPaths.every((newItem) => {
return item.path !== newItem.path;
});
if (check) {
tempInputPaths.push(item);
}
});
inputPaths = tempInputPaths;
return inputPaths;
}
function processWorkersOfPreviewMode(splittedData: any, cmdPrefix: string, workerNumber: number) {
let processEnv: any = Object.assign({}, process.env);
let arkEnvParams: any = {
'splittedData': JSON.stringify(splittedData),
'cmdPrefix': cmdPrefix,
'workerNumber': workerNumber.toString(),
};
if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) {
arkEnvParams.mode = JSBUNDLE;
} else if (projectConfig.compileMode === ESMODULE) {
arkEnvParams.cachePath = process.env.cachePath;
arkEnvParams.mode = ESMODULE;
}
processEnv.arkEnvParams = JSON.stringify(arkEnvParams);
let genAbcCmd: string = `${nodeJs} "${path.resolve(__dirname, MANAGE_WORKERS_SCRIPT)}"`;
childProcess.execSync(genAbcCmd, {env: processEnv});
processExtraAsset();
}
function processWorkersOfBuildMode(splittedData: any, cmdPrefix: string, workerNumber: number) {
const useNewApi: boolean = nodeLargeOrEqualTargetVersion(16);
if (useNewApi && cluster.isPrimary || !useNewApi && cluster.isMaster) {
let genAbcScript: string = GEN_ABC_SCRIPT;
if (projectConfig.compileMode === ESMODULE) {
genAbcScript = GEN_MODULE_ABC_SCRIPT;
}
if (useNewApi) {
cluster.setupPrimary({
exec: path.resolve(__dirname, genAbcScript)
});
} else {
cluster.setupMaster({
exec: path.resolve(__dirname, genAbcScript)
});
}
for (let i = 0; i < workerNumber; ++i) {
let workerData: any = {
'inputs': JSON.stringify(splittedData[i]),
'cmd': cmdPrefix
};
if (projectConfig.compileMode === ESMODULE) {
let sn: number = i + 1;
let workerFileName: string = `filesInfo_${sn}.txt`;
workerData.workerFileName = workerFileName;
workerData.cachePath = process.env.cachePath;
}
cluster.fork(workerData);
}
cluster.on('exit', (worker, code, signal) => {
if (code === FAIL) {
process.exitCode = FAIL;
}
logger.debug(`worker ${worker.process.pid} finished`);
});
process.on('exit', (code) => {
if (process.exitCode !== FAIL && process.env.watchMode !== 'true') {
processExtraAsset();
if (projectConfig.compileMode === ESMODULE &&
(projectConfig.anBuildMode === AOT_FULL || projectConfig.anBuildMode === AOT_PARTIAL)) {
let faultHandler: FaultHandler = (error) => { logger.error(error); process.exit(FAIL); };
let abcArgs: string[] = initAbcEnv();
abcArgs.unshift(nodeJs);
const appAbc: string = path.join(projectConfig.buildPath, MODULES_ABC);
generateAot(arkDir, appAbc, projectConfig, logger, faultHandler);
}
}
});
}
}