1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import childProcess from 'child_process'; 17import fs from 'fs'; 18import path from 'path'; 19import cluster from 'cluster'; 20 21import { 22 COMMONJS, 23 COMPILE_CONTEXT_INFO_JSON, 24 ESM, 25 ESMODULE, 26 EXTNAME_CJS, 27 EXTNAME_ETS, 28 EXTNAME_JS, 29 EXTNAME_JSON, 30 EXTNAME_MJS, 31 EXTNAME_PROTO_BIN, 32 EXTNAME_TS, 33 EXTNAME_TXT, 34 FAIL, 35 SUCCESS, 36 FILESINFO, 37 FILESINFO_TXT, 38 MAX_WORKER_NUMBER, 39 MODULES_ABC, 40 MODULES_CACHE, 41 NPM_ENTRIES_PROTO_BIN, 42 NPMENTRIES_TXT, 43 OH_MODULES, 44 PACKAGES, 45 PROTO_FILESINFO_TXT, 46 PROTOS, 47 red, 48 reset, 49 SOURCEMAPS, 50 SOURCEMAPS_JSON, 51 WIDGETS_ABC, 52 TS2ABC, 53 ES2ABC, 54 ETS, 55 TS, 56 JS 57} from '../common/ark_define'; 58import { 59 needAotCompiler, 60 isMasterOrPrimary, 61 isAotMode, 62 isDebug 63} from '../utils'; 64import { CommonMode } from '../common/common_mode'; 65import { 66 handleObfuscatedFilePath, 67 enableObfuscateFileName, 68 enableObfuscatedFilePathConfig 69} from '../common/ob_config_resolver'; 70import { 71 changeFileExtension, 72 getEs2abcFileThreadNumber, 73 isCommonJsPluginVirtualFile, 74 isCurrentProjectFiles, 75 shouldETSOrTSFileTransformToJS 76} from '../utils'; 77import { 78 isPackageModulesFile, 79 mkdirsSync, 80 toUnixPath, 81 toHashData, 82 validateFilePathLength 83} from '../../../utils'; 84import { 85 getPackageInfo, 86 getNormalizedOhmUrlByFilepath, 87 getOhmUrlByFilepath, 88 getOhmUrlByExternalPackage, 89 isTs2Abc, 90 isEs2Abc, 91 createAndStartEvent, 92 stopEvent, 93 transformOhmurlToPkgName, 94 transformOhmurlToRecordName 95} from '../../../ark_utils'; 96import { 97 generateAot, 98 FaultHandler 99} from '../../../gen_aot'; 100import { 101 NATIVE_MODULE 102} from '../../../pre_define'; 103import { 104 sharedModuleSet 105} from '../check_shared_module'; 106import { SourceMapGenerator } from '../generate_sourcemap'; 107import { MemoryMonitor } from '../../meomry_monitor/rollup-plugin-memory-monitor'; 108import { MemoryDefine } from '../../meomry_monitor/memory_define'; 109import { 110 ArkTSInternalErrorDescription, 111 ArkTSErrorDescription, 112 ErrorCode 113} from '../error_code'; 114import { 115 LogData, 116 LogDataFactory 117} from '../logger'; 118 119export class ModuleInfo { 120 filePath: string; 121 cacheFilePath: string; 122 recordName: string; 123 isCommonJs: boolean; 124 sourceFile: string; 125 packageName: string; 126 originSourceLang: string; 127 128 constructor(filePath: string, cacheFilePath: string, isCommonJs: boolean, recordName: string, sourceFile: string, 129 packageName: string, originSourceLang: string 130 ) { 131 this.filePath = filePath; 132 this.cacheFilePath = cacheFilePath; 133 this.recordName = recordName; 134 this.isCommonJs = isCommonJs; 135 this.sourceFile = sourceFile; 136 this.packageName = packageName; 137 this.originSourceLang = originSourceLang 138 } 139} 140 141export class PackageEntryInfo { 142 pkgEntryPath: string; 143 pkgBuildPath: string; 144 constructor(pkgEntryPath: string, pkgBuildPath: string) { 145 this.pkgEntryPath = pkgEntryPath; 146 this.pkgBuildPath = pkgBuildPath; 147 } 148} 149 150export class ModuleMode extends CommonMode { 151 moduleInfos: Map<String, ModuleInfo>; 152 pkgEntryInfos: Map<String, PackageEntryInfo>; 153 hashJsonObject: Object; 154 filesInfoPath: string; 155 npmEntriesInfoPath: string; 156 moduleAbcPath: string; 157 sourceMapPath: string; 158 cacheFilePath: string; 159 cacheSourceMapPath: string; 160 workerNumber: number; 161 npmEntriesProtoFilePath: string; 162 protoFilePath: string; 163 filterModuleInfos: Map<String, ModuleInfo>; 164 symlinkMap: Object; 165 useNormalizedOHMUrl: boolean; 166 compileContextInfoPath: string; 167 abcPaths: string[] = []; 168 byteCodeHar: boolean; 169 170 constructor(rollupObject: Object) { 171 super(rollupObject); 172 this.moduleInfos = new Map<String, ModuleInfo>(); 173 this.pkgEntryInfos = new Map<String, PackageEntryInfo>(); 174 this.hashJsonObject = {}; 175 this.filesInfoPath = path.join(this.projectConfig.cachePath, FILESINFO_TXT); 176 this.npmEntriesInfoPath = path.join(this.projectConfig.cachePath, NPMENTRIES_TXT); 177 const outPutABC: string = this.projectConfig.widgetCompile ? WIDGETS_ABC : MODULES_ABC; 178 this.moduleAbcPath = path.join(this.projectConfig.aceModuleBuild, outPutABC); 179 this.sourceMapPath = this.arkConfig.isDebug ? path.join(this.projectConfig.aceModuleBuild, SOURCEMAPS) : 180 path.join(this.projectConfig.cachePath, SOURCEMAPS); 181 this.cacheFilePath = path.join(this.projectConfig.cachePath, MODULES_CACHE); 182 this.cacheSourceMapPath = path.join(this.projectConfig.cachePath, SOURCEMAPS_JSON); 183 this.workerNumber = MAX_WORKER_NUMBER; 184 this.npmEntriesProtoFilePath = path.join(this.projectConfig.cachePath, PROTOS, NPM_ENTRIES_PROTO_BIN); 185 this.protoFilePath = path.join(this.projectConfig.cachePath, PROTOS, PROTO_FILESINFO_TXT); 186 this.hashJsonObject = {}; 187 this.filterModuleInfos = new Map<String, ModuleInfo>(); 188 this.symlinkMap = rollupObject.share.symlinkMap; 189 this.useNormalizedOHMUrl = this.isUsingNormalizedOHMUrl(); 190 if (Object.prototype.hasOwnProperty.call(this.projectConfig, 'byteCodeHarInfo')) { 191 let byteCodeHarInfo = this.projectConfig.byteCodeHarInfo; 192 for (const packageName in byteCodeHarInfo) { 193 const abcPath = toUnixPath(byteCodeHarInfo[packageName].abcPath); 194 this.abcPaths.push(abcPath); 195 } 196 } 197 this.byteCodeHar = !!this.projectConfig.byteCodeHar; 198 if (this.useNormalizedOHMUrl) { 199 this.compileContextInfoPath = this.generateCompileContextInfo(rollupObject); 200 } 201 } 202 203 private generateCompileContextInfo(rollupObject: Object): string { 204 let compileContextInfoPath: string = path.join(this.projectConfig.cachePath, COMPILE_CONTEXT_INFO_JSON); 205 let compileContextInfo: Object = {}; 206 let hspPkgNames: Array<string> = []; 207 for (const hspAliasName in this.projectConfig.hspNameOhmMap) { 208 let hspPkgName: string = hspAliasName; 209 if (this.projectConfig.dependencyAliasMap.has(hspAliasName)) { 210 hspPkgName = this.projectConfig.dependencyAliasMap.get(hspAliasName); 211 } 212 hspPkgNames.push(toUnixPath(hspPkgName)); 213 } 214 compileContextInfo.hspPkgNames = hspPkgNames; 215 let compileEntries: Set<string> = new Set(); 216 let entryObj: Object = this.projectConfig.entryObj; 217 if (!!this.projectConfig.widgetCompile) { 218 entryObj = this.projectConfig.cardEntryObj; 219 } 220 for (const key in entryObj) { 221 let moduleId: string = entryObj[key]; 222 let moduleInfo: Object = rollupObject.getModuleInfo(moduleId); 223 if (!moduleInfo) { 224 const errInfo: LogData = LogDataFactory.newInstance( 225 ErrorCode.ETS2BUNDLE_INTERNAL_MODULE_INFO_NOT_FOUND, 226 ArkTSInternalErrorDescription, 227 'Failed to find module info. ' + 228 `Failed to find module info with '${moduleId}' from the context information.` 229 ); 230 this.logger.printErrorAndExit(errInfo); 231 } 232 let metaInfo: Object = moduleInfo.meta; 233 if (!metaInfo) { 234 const errInfo: LogData = LogDataFactory.newInstance( 235 ErrorCode.ETS2BUNDLE_INTERNAL_META_INFO_NOT_FOUND, 236 ArkTSInternalErrorDescription, 237 'Failed to find meta info. ' + 238 `Failed to find meta info with '${moduleId}' from the module info.` 239 ); 240 this.logger.printErrorAndExit(errInfo); 241 } 242 const pkgParams = { 243 pkgName: metaInfo.pkgName, 244 pkgPath: metaInfo.pkgPath, 245 isRecordName: true 246 }; 247 let recordName: string = getNormalizedOhmUrlByFilepath(moduleId, this.projectConfig, this.logger, pkgParams, 248 undefined); 249 compileEntries.add(recordName); 250 } 251 this.collectDeclarationFilesEntry(compileEntries, hspPkgNames); 252 compileContextInfo.compileEntries = Array.from(compileEntries); 253 if (this.projectConfig.updateVersionInfo) { 254 compileContextInfo.updateVersionInfo = this.projectConfig.updateVersionInfo; 255 } else if (this.projectConfig.pkgContextInfo) { 256 compileContextInfo.pkgContextInfo = this.projectConfig.pkgContextInfo; 257 } 258 // The bundleType is 'shared' in cross-app hsp. 259 if (this.projectConfig.bundleType === 'shared') { 260 compileContextInfo.needModifyRecord = true; 261 compileContextInfo.bundleName = this.projectConfig.bundleName; 262 } 263 fs.writeFileSync(compileContextInfoPath, JSON.stringify(compileContextInfo), 'utf-8'); 264 return compileContextInfoPath; 265 } 266 267 private collectDeclarationFilesEntry(compileEntries: Set<string>, hspPkgNames: Array<string>): void { 268 if (this.projectConfig.arkRouterMap) { 269 // Collect bytecode har's declaration files entries in router map, use 270 // by es2abc for dependency resolution. 271 this.collectRouterMapEntries(compileEntries, hspPkgNames); 272 } 273 if (this.projectConfig.declarationEntry) { 274 // Collect bytecode har's declaration files entries include dynamic import and workers, use 275 // by es2abc for dependency resolution. 276 this.projectConfig.declarationEntry.forEach((ohmurl) => { 277 let pkgName: string = transformOhmurlToPkgName(ohmurl); 278 if (!hspPkgNames.includes(pkgName)) { 279 let recordName: string = transformOhmurlToRecordName(ohmurl); 280 compileEntries.add(recordName); 281 } 282 }); 283 } 284 } 285 286 private collectRouterMapEntries(compileEntries: Set<string>, hspPkgNames: Array<string>): void { 287 this.projectConfig.arkRouterMap.forEach((router) => { 288 if (router.ohmurl) { 289 let pkgName: string = transformOhmurlToPkgName(router.ohmurl); 290 if (!hspPkgNames.includes(pkgName)) { 291 let recordName: string = transformOhmurlToRecordName(router.ohmurl); 292 compileEntries.add(recordName); 293 } 294 } 295 }); 296 } 297 298 prepareForCompilation(rollupObject: Object, parentEvent: Object): void { 299 const eventPrepareForCompilation = createAndStartEvent(parentEvent, 'preparation for compilation'); 300 this.collectModuleFileList(rollupObject, rollupObject.getModuleIds()); 301 this.removeCacheInfo(rollupObject); 302 stopEvent(eventPrepareForCompilation); 303 } 304 305 collectModuleFileList(module: Object, fileList: IterableIterator<string>): void { 306 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.PKG_ENTRY_INFOS_MODULE_INFOS); 307 let moduleInfos: Map<String, ModuleInfo> = new Map<String, ModuleInfo>(); 308 let pkgEntryInfos: Map<String, PackageEntryInfo> = new Map<String, PackageEntryInfo>(); 309 for (const moduleId of fileList) { 310 if (isCommonJsPluginVirtualFile(moduleId) || !isCurrentProjectFiles(moduleId, this.projectConfig)) { 311 continue; 312 } 313 const moduleInfo: Object = module.getModuleInfo(moduleId); 314 if (moduleInfo.meta.isNodeEntryFile && !this.useNormalizedOHMUrl) { 315 this.getPackageEntryInfo(moduleId, moduleInfo.meta, pkgEntryInfos); 316 } 317 318 this.processModuleInfos(moduleId, moduleInfos, moduleInfo.meta); 319 } 320 if (!this.useNormalizedOHMUrl) { 321 this.getDynamicImportEntryInfo(pkgEntryInfos); 322 } 323 this.getNativeModuleEntryInfo(pkgEntryInfos); 324 this.moduleInfos = moduleInfos; 325 this.pkgEntryInfos = pkgEntryInfos; 326 MemoryMonitor.stopRecordStage(recordInfo); 327 } 328 329 private isUsingNormalizedOHMUrl(): boolean { 330 return !!this.projectConfig.useNormalizedOHMUrl; 331 } 332 333 private updatePkgEntryInfos(pkgEntryInfos: Map<String, PackageEntryInfo>, key: String, value: PackageEntryInfo): void { 334 if (!pkgEntryInfos.has(key)) { 335 pkgEntryInfos.set(key, new PackageEntryInfo(key, value)); 336 } 337 } 338 339 private getDynamicImportEntryInfo(pkgEntryInfos: Map<String, PackageEntryInfo>): void { 340 if (this.projectConfig.dynamicImportLibInfo) { 341 const REG_LIB_SO: RegExp = /lib(.+)\.so/; 342 for (const [pkgName, pkgInfo] of Object.entries(this.projectConfig.dynamicImportLibInfo)) { 343 if (REG_LIB_SO.test(pkgName)) { 344 let ohmurl: string = pkgName.replace(REG_LIB_SO, (_, libsoKey) => { 345 return `@app.${this.projectConfig.bundleName}/${this.projectConfig.moduleName}/${libsoKey}`; 346 }); 347 this.updatePkgEntryInfos(pkgEntryInfos, pkgName, ohmurl); 348 continue; 349 } 350 let hspOhmurl: string | undefined = getOhmUrlByExternalPackage(pkgName, this.projectConfig, this.logger, 351 this.useNormalizedOHMUrl); 352 if (hspOhmurl !== undefined) { 353 hspOhmurl = hspOhmurl.replace(/^@(\w+):(.*)/, '@$1.$2'); 354 this.updatePkgEntryInfos(pkgEntryInfos, pkgName, hspOhmurl); 355 continue; 356 } 357 const entryFile: string = pkgInfo.entryFilePath; 358 this.getPackageEntryInfo(entryFile, pkgInfo, pkgEntryInfos); 359 } 360 } 361 } 362 363 private getNativeModuleEntryInfo(pkgEntryInfos: Map<String, PackageEntryInfo>): void { 364 for (const item of NATIVE_MODULE) { 365 let key = '@' + item; 366 this.updatePkgEntryInfos(pkgEntryInfos, key, '@native.' + item); 367 } 368 } 369 370 private getPackageEntryInfo(filePath: string, metaInfo: Object, pkgEntryInfos: Map<String, PackageEntryInfo>): void { 371 if (metaInfo.isLocalDependency) { 372 const hostModulesInfo: Object = metaInfo.hostModulesInfo; 373 const pkgBuildPath: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, metaInfo.moduleName); 374 hostModulesInfo.forEach(hostModuleInfo => { 375 const hostDependencyName: string = hostModuleInfo.hostDependencyName; 376 const hostModuleName: string = hostModuleInfo.hostModuleName; 377 const pkgEntryPath: string = toUnixPath(path.join(`${PACKAGES}@${hostModuleName}`, hostDependencyName)); 378 if (!pkgEntryInfos.has(pkgEntryPath)) { 379 pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath)); 380 } 381 this.updatePkgEntryInfos(pkgEntryInfos, hostDependencyName, `@bundle.${pkgBuildPath}`); 382 }); 383 return; 384 } 385 386 if (!metaInfo.pkgPath) { 387 const errInfo: LogData = LogDataFactory.newInstance( 388 ErrorCode.ETS2BUNDLE_INTERNAL_UNABLE_TO_GET_MODULE_INFO_META_PKG_PATH, 389 ArkTSInternalErrorDescription, 390 `Failed to get ModuleInfo properties 'meta.pkgPath', moduleId: ${filePath}` 391 ); 392 this.logger.printErrorAndExit(errInfo); 393 } 394 const pkgPath: string = metaInfo.pkgPath; 395 let originPkgEntryPath: string = toUnixPath(filePath.replace(pkgPath, '')); 396 if (originPkgEntryPath.startsWith('/')) { 397 originPkgEntryPath = originPkgEntryPath.slice(1, originPkgEntryPath.length); 398 } 399 const pkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(pkgPath)); 400 let pkgBuildPath: string = path.join(pkgEntryPath, originPkgEntryPath); 401 pkgBuildPath = toUnixPath(pkgBuildPath.substring(0, pkgBuildPath.lastIndexOf('.'))); 402 if (!pkgEntryInfos.has(pkgEntryPath)) { 403 pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath)); 404 } 405 // create symlink path to actual path mapping in ohpm 406 if (this.projectConfig.packageDir == OH_MODULES && this.symlinkMap) { 407 const symlinkEntries: Object = Object.entries(this.symlinkMap); 408 for (const [actualPath, symlinkPaths] of symlinkEntries) { 409 if (actualPath === pkgPath) { 410 (<string[]>symlinkPaths).forEach((symlink: string) => { 411 const symlinkPkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(symlink)); 412 if (!pkgEntryInfos.has(symlinkPkgEntryPath)) { 413 pkgEntryInfos.set(symlinkPkgEntryPath, new PackageEntryInfo(symlinkPkgEntryPath, pkgEntryPath)); 414 } 415 }); 416 break; 417 } 418 } 419 } 420 } 421 422 private processModuleInfos(moduleId: string, moduleInfos: Map<String, ModuleInfo>, metaInfo?: Object): void { 423 switch (path.extname(moduleId)) { 424 case EXTNAME_ETS: { 425 const extName: string = shouldETSOrTSFileTransformToJS(moduleId, this.projectConfig, metaInfo) ? EXTNAME_JS : EXTNAME_TS; 426 this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos, ETS); 427 break; 428 } 429 case EXTNAME_TS: { 430 const extName: string = shouldETSOrTSFileTransformToJS(moduleId, this.projectConfig, metaInfo) ? EXTNAME_JS : ''; 431 this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos, TS); 432 break; 433 } 434 case EXTNAME_JS: 435 case EXTNAME_MJS: 436 case EXTNAME_CJS: { 437 const extName: string = (moduleId.endsWith(EXTNAME_MJS) || moduleId.endsWith(EXTNAME_CJS)) ? EXTNAME_JS : ''; 438 const isCommonJS: boolean = metaInfo && metaInfo.commonjs && metaInfo.commonjs.isCommonJS; 439 this.addModuleInfoItem(moduleId, isCommonJS, extName, metaInfo, moduleInfos, JS); 440 break; 441 } 442 case EXTNAME_JSON: { 443 this.addModuleInfoItem(moduleId, false, '', metaInfo, moduleInfos); 444 break; 445 } 446 default: 447 break; 448 } 449 } 450 451 private handleFileNameObfuscationInModuleInfo(sourceMapGenerator: SourceMapGenerator, isPackageModules: boolean, originalFilePath: string, filePath: string, 452 sourceFile: string) { 453 if (!enableObfuscateFileName(isPackageModules, this.projectConfig)) { 454 if (sourceMapGenerator.isNewSourceMaps()) { 455 sourceFile = sourceMapGenerator.genKey(originalFilePath); 456 } 457 return {filePath: filePath, sourceFile: sourceFile}; 458 } 459 460 // if release mode, enable obfuscation, enable filename obfuscation -> call mangleFilePath() 461 filePath = handleObfuscatedFilePath(originalFilePath, isPackageModules, this.projectConfig); 462 sourceFile = filePath.replace(toUnixPath(this.projectConfig.projectRootPath) + '/', ''); 463 464 if (sourceMapGenerator.isNewSourceMaps()) { 465 sourceFile = sourceMapGenerator.genKey(originalFilePath); // If the file name is obfuscated, meta info cannot be found. 466 if (!sourceMapGenerator.sourceMapKeyMappingForObf.get(sourceFile)) { 467 sourceMapGenerator.saveKeyMappingForObfFileName(originalFilePath); 468 } 469 // If the file name is obfuscated, the sourceFile needs to be updated. 470 sourceFile = sourceMapGenerator.sourceMapKeyMappingForObf.get(sourceFile); 471 } 472 return {filePath: filePath, sourceFile: sourceFile}; 473 } 474 475 private addModuleInfoItem(originalFilePath: string, isCommonJs: boolean, extName: string, 476 metaInfo: Object, moduleInfos: Map<String, ModuleInfo>, originSourceLang: string = ""): void { 477 const sourceMapGenerator: SourceMapGenerator = SourceMapGenerator.getInstance(); 478 const isPackageModules = isPackageModulesFile(originalFilePath, this.projectConfig); 479 let filePath: string = originalFilePath; 480 let sourceFile: string = filePath.replace(this.projectConfig.projectRootPath + path.sep, ''); 481 const isObfuscateEnabled: boolean = enableObfuscatedFilePathConfig(isPackageModules, this.projectConfig); 482 if (isObfuscateEnabled) { 483 const filePathAndSourceFile = this.handleFileNameObfuscationInModuleInfo(sourceMapGenerator, isPackageModules, originalFilePath, filePath, sourceFile); 484 filePath = filePathAndSourceFile.filePath; 485 sourceFile = filePathAndSourceFile.sourceFile; 486 } else { 487 if (sourceMapGenerator.isNewSourceMaps()) { 488 sourceFile = sourceMapGenerator.genKey(originalFilePath); 489 } 490 } 491 492 let moduleName: string = metaInfo.moduleName; 493 let recordName: string = ''; 494 let cacheFilePath: string = 495 this.genFileCachePath(filePath, this.projectConfig.projectRootPath, this.projectConfig.cachePath, metaInfo); 496 let packageName: string = ''; 497 498 if (this.useNormalizedOHMUrl) { 499 packageName = metaInfo.pkgName; 500 const pkgParams = { 501 pkgName: packageName, 502 pkgPath: metaInfo.pkgPath, 503 isRecordName: true 504 }; 505 recordName = getNormalizedOhmUrlByFilepath(filePath, this.projectConfig, this.logger, pkgParams, undefined); 506 } else { 507 recordName = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, moduleName); 508 if (isPackageModules) { 509 packageName = this.getPkgModulesFilePkgName(metaInfo.pkgPath); 510 } else { 511 packageName = 512 metaInfo.isLocalDependency ? moduleName : getPackageInfo(this.projectConfig.aceModuleJsonPath)[1]; 513 } 514 } 515 516 if (extName.length !== 0) { 517 cacheFilePath = changeFileExtension(cacheFilePath, extName); 518 } 519 520 cacheFilePath = toUnixPath(cacheFilePath); 521 recordName = toUnixPath(recordName); 522 packageName = toUnixPath(packageName); 523 if (!sourceMapGenerator.isNewSourceMaps()) { 524 sourceFile = cacheFilePath.replace(toUnixPath(this.projectConfig.projectRootPath) + '/', ''); 525 } 526 filePath = toUnixPath(filePath); 527 528 moduleInfos.set(filePath, new ModuleInfo(filePath, cacheFilePath, isCommonJs, recordName, sourceFile, packageName, 529 originSourceLang)); 530 } 531 532 generateEs2AbcCmd() { 533 const fileThreads = getEs2abcFileThreadNumber(); 534 this.cmdArgs.push(`"@${this.filesInfoPath}"`); 535 if (!this.byteCodeHar) { 536 this.cmdArgs.push('--npm-module-entry-list'); 537 this.cmdArgs.push(`"${this.npmEntriesInfoPath}"`); 538 } 539 this.cmdArgs.push('--output'); 540 this.cmdArgs.push(`"${this.moduleAbcPath}"`); 541 this.cmdArgs.push('--file-threads'); 542 this.cmdArgs.push(`"${fileThreads}"`); 543 this.cmdArgs.push('--merge-abc'); 544 this.cmdArgs.push(`"--target-api-version=${this.projectConfig.compatibleSdkVersion}"`); 545 if (this.projectConfig.compatibleSdkVersionStage) { 546 this.cmdArgs.push(`"--target-api-sub-version=${this.projectConfig.compatibleSdkVersionStage}"`); 547 } 548 if (this.arkConfig.isBranchElimination) { 549 this.cmdArgs.push('--branch-elimination'); 550 } 551 if (this.projectConfig.transformLib) { 552 this.cmdArgs.push(`--transform-lib`); 553 this.cmdArgs.push(`"${this.projectConfig.transformLib}"`); 554 } 555 if (this.compileContextInfoPath !== undefined) { 556 this.cmdArgs.push(`--compile-context-info`); 557 this.cmdArgs.push(`"${this.compileContextInfoPath}"`); 558 } 559 if (this.abcPaths.length > 0 && !this.byteCodeHar) { 560 this.cmdArgs.push('--enable-abc-input'); 561 this.cmdArgs.push('--remove-redundant-file'); 562 } 563 if (!this.arkConfig.optTryCatchFunc) { 564 this.cmdArgs.push('--opt-try-catch-func=false'); 565 } 566 if (this.projectConfig.allowEtsAnnotations) { 567 this.cmdArgs.push('--enable-annotations'); 568 } 569 } 570 571 addCacheFileArgs() { 572 this.cmdArgs.push('--cache-file'); 573 this.cmdArgs.push(`"@${this.cacheFilePath}"`); 574 } 575 576 private generateCompileFilesInfo(includeByteCodeHarInfo: boolean): void { 577 let filesInfo: string = ''; 578 this.moduleInfos.forEach((info) => { 579 const moduleType: string = info.isCommonJs ? COMMONJS : ESM; 580 const isSharedModule: boolean = sharedModuleSet.has(info.filePath); 581 filesInfo += `${info.cacheFilePath};${info.recordName};${moduleType};${info.sourceFile};${info.packageName};` + 582 `${isSharedModule};${info.originSourceLang}\n`; 583 }); 584 if (includeByteCodeHarInfo) { 585 Object.entries(this.projectConfig.byteCodeHarInfo).forEach(([pkgName, abcInfo]) => { 586 // es2abc parses file path and pkgName according to the file extension .abc. 587 // Accurate version replacement requires 'pkgName' to es2abc. 588 const abcPath: string = toUnixPath(abcInfo.abcPath); 589 filesInfo += `${abcPath};;;;${pkgName};\n`; 590 }); 591 } 592 593 fs.writeFileSync(this.filesInfoPath, filesInfo, 'utf-8'); 594 } 595 596 private generateNpmEntriesInfo() { 597 let entriesInfo: string = ''; 598 for (const value of this.pkgEntryInfos.values()) { 599 entriesInfo += `${value.pkgEntryPath}:${value.pkgBuildPath}\n`; 600 } 601 fs.writeFileSync(this.npmEntriesInfoPath, entriesInfo, 'utf-8'); 602 } 603 604 private generateAbcCacheFilesInfo(): void { 605 let abcCacheFilesInfo: string = ''; 606 607 // generate source file cache 608 this.moduleInfos.forEach((info) => { 609 let abcCacheFilePath: string = changeFileExtension(info.cacheFilePath, EXTNAME_PROTO_BIN); 610 abcCacheFilesInfo += `${info.cacheFilePath};${abcCacheFilePath}\n`; 611 }); 612 613 // generate npm entries cache 614 let npmEntriesCacheFilePath: string = changeFileExtension(this.npmEntriesInfoPath, EXTNAME_PROTO_BIN); 615 abcCacheFilesInfo += `${this.npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`; 616 if (this.projectConfig.cacheBytecodeHar) { 617 this.abcPaths.forEach((abcPath) => { 618 let abcCacheFilePath: string = this.genAbcCacheFilePath(abcPath); 619 mkdirsSync(path.dirname(abcCacheFilePath)); 620 abcCacheFilesInfo += `${abcPath};${abcCacheFilePath}\n`; 621 }); 622 } 623 fs.writeFileSync(this.cacheFilePath, abcCacheFilesInfo, 'utf-8'); 624 } 625 626 // Generate cache file path for bytecode har 627 private genAbcCacheFilePath(abcPath: string): string { 628 /** 629 * The projectTopDir is the path of the main project, the projectRootPath is the project path to which it belongs, 630 * and the path of bytecode har is within the main project. Therefore, the projectTopDir is used to intercept the 631 * relative path of bytecodehar. 632 */ 633 let relativeAbcPath: string = abcPath.replace(toUnixPath(this.projectConfig.projectTopDir), ''); 634 let tempPath: string = path.join(this.projectConfig.cachePath, relativeAbcPath); 635 return changeFileExtension(tempPath, EXTNAME_PROTO_BIN); 636 } 637 638 genDescriptionsForMergedEs2abc(includeByteCodeHarInfo: boolean): void { 639 this.generateCompileFilesInfo(includeByteCodeHarInfo); 640 if (!this.byteCodeHar) { 641 this.generateNpmEntriesInfo(); 642 } 643 this.generateAbcCacheFilesInfo(); 644 } 645 646 generateMergedAbcOfEs2Abc(parentEvent: Object): void { 647 // collect data error from subprocess 648 let logDataList: Object[] = []; 649 let errMsg: string = ''; 650 const eventGenDescriptionsForMergedEs2abc = createAndStartEvent(parentEvent, 'generate descriptions for merged es2abc'); 651 stopEvent(eventGenDescriptionsForMergedEs2abc); 652 const genAbcCmd: string = this.cmdArgs.join(' '); 653 try { 654 let eventGenAbc: Object; 655 const child = this.triggerAsync(() => { 656 eventGenAbc = createAndStartEvent(parentEvent, 'generate merged abc by es2abc (async)', true); 657 return childProcess.exec(genAbcCmd, { windowsHide: true }); 658 }); 659 child.on('close', (code: any) => { 660 if (code === SUCCESS) { 661 stopEvent(eventGenAbc, true); 662 this.triggerEndSignal(); 663 this.processAotIfNeeded(); 664 return; 665 } 666 for (const logData of logDataList) { 667 this.logger.printError(logData); 668 } 669 if (errMsg !== '') { 670 this.logger.error(`Error Message: ${errMsg}`); 671 } 672 const errInfo: LogData = LogDataFactory.newInstance( 673 ErrorCode.ETS2BUNDLE_EXTERNAL_ES2ABC_EXECUTION_FAILED, 674 ArkTSErrorDescription, 675 'Failed to execute es2abc.', 676 '', 677 ["Please refer to es2abc's error codes."] 678 ); 679 this.logger.printErrorAndExit(errInfo); 680 }); 681 682 child.on('error', (err: any) => { 683 stopEvent(eventGenAbc, true); 684 const errInfo: LogData = LogDataFactory.newInstance( 685 ErrorCode.ETS2BUNDLE_INTERNAL_ES2ABC_SUBPROCESS_START_FAILED, 686 ArkTSInternalErrorDescription, 687 `Failed to initialize or launch the es2abc process. ${err.toString()}` 688 ); 689 this.logger.printErrorAndExit(errInfo); 690 }); 691 692 child.stderr.on('data', (data: any) => { 693 const logData = LogDataFactory.newInstanceFromEs2AbcError(data.toString()); 694 if (logData) { 695 logDataList.push(logData); 696 } else { 697 errMsg += data.toString(); 698 } 699 }); 700 701 } catch (e) { 702 const errInfo: LogData = LogDataFactory.newInstance( 703 ErrorCode.ETS2BUNDLE_INTERNAL_EXECUTE_ES2ABC_WITH_ASYNC_HANDLER_FAILED, 704 ArkTSInternalErrorDescription, 705 `Failed to execute es2abc with async handler. ${e.toString()}` 706 ); 707 this.logger.printErrorAndExit(errInfo); 708 } 709 } 710 711 filterModulesByHashJson() { 712 if (this.hashJsonFilePath.length === 0 || !fs.existsSync(this.hashJsonFilePath)) { 713 for (const key of this.moduleInfos.keys()) { 714 this.filterModuleInfos.set(key, this.moduleInfos.get(key)); 715 } 716 return; 717 } 718 719 let updatedJsonObject: Object = {}; 720 let jsonObject: Object = {}; 721 let jsonFile: string = ''; 722 723 if (fs.existsSync(this.hashJsonFilePath)) { 724 jsonFile = fs.readFileSync(this.hashJsonFilePath).toString(); 725 jsonObject = JSON.parse(jsonFile); 726 this.filterModuleInfos = new Map<string, ModuleInfo>(); 727 for (const [key, value] of this.moduleInfos) { 728 const cacheFilePath: string = value.cacheFilePath; 729 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 730 if (!fs.existsSync(cacheFilePath)) { 731 this.logger.throwArkTsCompilerError( 732 `ArkTS:INTERNAL ERROR: Failed to get module cache abc from ${cacheFilePath} in incremental build.` + 733 'Please try to rebuild the project.'); 734 } 735 if (fs.existsSync(cacheProtoFilePath)) { 736 const hashCacheFileContentData: string = toHashData(cacheFilePath); 737 const hashProtoFileContentData: string = toHashData(cacheProtoFilePath); 738 if (jsonObject[cacheFilePath] === hashCacheFileContentData && 739 jsonObject[cacheProtoFilePath] === hashProtoFileContentData) { 740 updatedJsonObject[cacheFilePath] = cacheFilePath; 741 updatedJsonObject[cacheProtoFilePath] = cacheProtoFilePath; 742 continue; 743 } 744 } 745 this.filterModuleInfos.set(key, value); 746 } 747 } 748 749 this.hashJsonObject = updatedJsonObject; 750 } 751 752 getSplittedModulesByNumber() { 753 const result: any = []; 754 if (this.filterModuleInfos.size < this.workerNumber) { 755 for (const value of this.filterModuleInfos.values()) { 756 result.push([value]); 757 } 758 return result; 759 } 760 761 for (let i = 0; i < this.workerNumber; ++i) { 762 result.push([]); 763 } 764 765 let pos: number = 0; 766 for (const value of this.filterModuleInfos.values()) { 767 const chunk = pos % this.workerNumber; 768 result[chunk].push(value); 769 pos++; 770 } 771 772 return result; 773 } 774 775 invokeTs2AbcWorkersToGenProto(splittedModules) { 776 let ts2abcCmdArgs: string[] = this.cmdArgs.slice(0); 777 ts2abcCmdArgs.push('--output-proto'); 778 ts2abcCmdArgs.push('--merge-abc'); 779 ts2abcCmdArgs.push('--input-file'); 780 if (isMasterOrPrimary()) { 781 this.setupCluster(cluster); 782 this.workerNumber = splittedModules.length; 783 for (let i = 0; i < this.workerNumber; ++i) { 784 const sn: number = i + 1; 785 const workerFileName: string = `${FILESINFO}_${sn}${EXTNAME_TXT}`; 786 const workerData: Object = { 787 inputs: JSON.stringify(splittedModules[i]), 788 cmd: ts2abcCmdArgs.join(' '), 789 workerFileName: workerFileName, 790 mode: ESMODULE, 791 cachePath: this.projectConfig.cachePath 792 }; 793 this.triggerAsync(() => { 794 const worker: Object = cluster.fork(workerData); 795 worker.on('message', (errorMsg) => { 796 this.logger.error(red, errorMsg.data.toString(), reset); 797 this.logger.throwArkTsCompilerError('ArkTS:ERROR Failed to execute ts2abc.'); 798 }); 799 }); 800 } 801 } 802 } 803 804 processTs2abcWorkersToGenAbc() { 805 this.generateNpmEntriesInfo(); 806 let workerCount: number = 0; 807 if (isMasterOrPrimary()) { 808 cluster.on('exit', (worker, code, signal) => { 809 if (code === FAIL) { 810 this.logger.throwArkTsCompilerError('ArkTS:ERROR Failed to execute ts2abc'); 811 } 812 workerCount++; 813 if (workerCount === this.workerNumber) { 814 this.generateNpmEntryToGenProto(); 815 this.generateProtoFilesInfo(); 816 this.mergeProtoToAbc(); 817 this.processAotIfNeeded(); 818 this.afterCompilationProcess(); 819 } 820 this.triggerEndSignal(); 821 }); 822 if (this.workerNumber === 0) { 823 // process aot for no source file changed. 824 this.processAotIfNeeded(); 825 } 826 } 827 } 828 829 private processAotIfNeeded(): void { 830 if (!needAotCompiler(this.projectConfig)) { 831 return; 832 } 833 let faultHandler: FaultHandler = ((error: string) => { this.logger.throwArkTsCompilerError(error); }); 834 generateAot(this.arkConfig.arkRootPath, this.moduleAbcPath, this.projectConfig, this.logger, faultHandler); 835 } 836 837 private genFileCachePath(filePath: string, projectRootPath: string, cachePath: string, metaInfo: Object): string { 838 filePath = toUnixPath(filePath); 839 let sufStr: string = ''; 840 if (metaInfo) { 841 if (metaInfo.isLocalDependency) { 842 sufStr = filePath.replace(toUnixPath(metaInfo.belongModulePath), metaInfo.moduleName); 843 } else { 844 sufStr = filePath.replace(toUnixPath(metaInfo.belongProjectPath), ''); 845 } 846 } else { 847 sufStr = filePath.replace(toUnixPath(projectRootPath), ''); 848 } 849 const output: string = path.join(cachePath, sufStr); 850 return output; 851 } 852 853 private getPkgModulesFilePkgName(pkgPath: string) { 854 pkgPath = toUnixPath(pkgPath); 855 const packageDir: string = this.projectConfig.packageDir; 856 const projectRootPath = toUnixPath(this.projectConfig.projectRootPath); 857 const projectPkgModulesPath: string = toUnixPath(path.join(projectRootPath, packageDir)); 858 let pkgName: string = ''; 859 if (pkgPath.includes(projectPkgModulesPath)) { 860 pkgName = path.join(PACKAGES, pkgPath.replace(projectPkgModulesPath, '')); 861 } else { 862 for (const key in this.projectConfig.modulePathMap) { 863 const value: string = this.projectConfig.modulePathMap[key]; 864 const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir)); 865 if (pkgPath.indexOf(fakeModulePkgModulesPath) !== -1) { 866 const tempFilePath: string = pkgPath.replace(projectRootPath, ''); 867 pkgName = path.join(`${PACKAGES}@${key}`, 868 tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1)); 869 break; 870 } 871 } 872 } 873 874 return pkgName.replace(new RegExp(packageDir, 'g'), PACKAGES); 875 } 876 877 private generateProtoFilesInfo() { 878 validateFilePathLength(this.protoFilePath, this.logger); 879 mkdirsSync(path.dirname(this.protoFilePath)); 880 let protoFilesInfo: string = ''; 881 const sortModuleInfos: Object = new Map([...this.moduleInfos].sort()); 882 for (const value of sortModuleInfos.values()) { 883 const cacheProtoPath: string = changeFileExtension(value.cacheFilePath, EXTNAME_PROTO_BIN); 884 protoFilesInfo += `${toUnixPath(cacheProtoPath)}\n`; 885 } 886 if (this.pkgEntryInfos.size > 0) { 887 protoFilesInfo += `${toUnixPath(this.npmEntriesProtoFilePath)}\n`; 888 } 889 fs.writeFileSync(this.protoFilePath, protoFilesInfo, 'utf-8'); 890 } 891 892 private mergeProtoToAbc() { 893 mkdirsSync(this.projectConfig.aceModuleBuild); 894 const cmd: string = `"${this.arkConfig.mergeAbcPath}" --input "@${this.protoFilePath}" --outputFilePath "${ 895 this.projectConfig.aceModuleBuild}" --output ${MODULES_ABC} --suffix protoBin`; 896 try { 897 childProcess.execSync(cmd, { windowsHide: true }); 898 } catch (e) { 899 this.logger.throwArkTsCompilerError('ArkTS:INTERNAL ERROR: Failed to merge proto file to abc.\n' + 900 'Error message:' + e.toString()); 901 } 902 } 903 904 private afterCompilationProcess() { 905 this.writeHashJson(); 906 } 907 908 private writeHashJson() { 909 if (this.hashJsonFilePath.length === 0) { 910 return; 911 } 912 913 for (const value of this.filterModuleInfos.values()) { 914 const cacheFilePath: string = value.cacheFilePath; 915 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 916 if (!fs.existsSync(cacheFilePath) || !fs.existsSync(cacheProtoFilePath)) { 917 this.logger.throwArkTsCompilerError( 918 `ArkTS:ERROR ${cacheFilePath} or ${cacheProtoFilePath} is lost` 919 ); 920 } 921 const hashCacheFileContentData: string = toHashData(cacheFilePath); 922 const hashCacheProtoContentData: string = toHashData(cacheProtoFilePath); 923 this.hashJsonObject[cacheFilePath] = hashCacheFileContentData; 924 this.hashJsonObject[cacheProtoFilePath] = hashCacheProtoContentData; 925 } 926 927 fs.writeFileSync(this.hashJsonFilePath, JSON.stringify(this.hashJsonObject)); 928 } 929 930 private generateNpmEntryToGenProto() { 931 if (this.pkgEntryInfos.size <= 0) { 932 return; 933 } 934 mkdirsSync(path.dirname(this.npmEntriesProtoFilePath)); 935 const cmd: string = `"${this.arkConfig.js2abcPath}" --compile-npm-entries "${ 936 this.npmEntriesInfoPath}" "${this.npmEntriesProtoFilePath}"`; 937 try { 938 childProcess.execSync(cmd, { windowsHide: true }); 939 } catch (e) { 940 this.logger.throwArkTsCompilerError('ArkTS:ERROR failed to generate npm proto file to abc. Error message: ' + e.toString()); 941 } 942 } 943 944 private removeCompilationCache(): void { 945 if (isEs2Abc(this.projectConfig)) { 946 this.removeEs2abcCompilationCache(); 947 } else if (isTs2Abc(this.projectConfig)) { 948 this.removeTs2abcCompilationCache(); 949 } else { 950 const errInfo: LogData = LogDataFactory.newInstance( 951 ErrorCode.ETS2BUNDLE_INTERNAL_INVALID_COMPILE_MODE, 952 ArkTSInternalErrorDescription, 953 'Invalid compilation mode. ' + 954 `ProjectConfig.pandaMode should be either ${TS2ABC} or ${ES2ABC}.` 955 ); 956 this.logger.printErrorAndExit(errInfo); 957 } 958 } 959 960 private removeEs2abcCompilationCache(): void { 961 if (fs.existsSync(this.cacheFilePath)) { 962 const data: string = fs.readFileSync(this.cacheFilePath, 'utf-8'); 963 const lines: string[] = data.split(/\r?\n/); 964 lines.forEach(line => { 965 const [, abcCacheFilePath]: string[] = line.split(';'); 966 if (fs.existsSync(abcCacheFilePath)) { 967 fs.unlinkSync(abcCacheFilePath); 968 } 969 }); 970 fs.unlinkSync(this.cacheFilePath); 971 } 972 } 973 974 private removeTs2abcCompilationCache(): void { 975 if (fs.existsSync(this.hashJsonFilePath)) { 976 fs.unlinkSync(this.hashJsonFilePath); 977 } 978 if (fs.existsSync(this.protoFilePath)) { 979 const data: string = fs.readFileSync(this.protoFilePath, 'utf-8'); 980 const lines: string[] = data.split(/\r?\n/); 981 lines.forEach(line => { 982 const protoFilePath: string = line; 983 if (fs.existsSync(protoFilePath)) { 984 fs.unlinkSync(protoFilePath); 985 } 986 }); 987 fs.unlinkSync(this.protoFilePath); 988 } 989 } 990} 991