1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import childProcess from 'child_process'; 17import fs from 'fs'; 18import path from 'path'; 19import cluster from 'cluster'; 20 21import { 22 COMMONJS, 23 ESM, 24 ESMODULE, 25 EXTNAME_CJS, 26 EXTNAME_ETS, 27 EXTNAME_JS, 28 EXTNAME_JSON, 29 EXTNAME_MJS, 30 EXTNAME_PROTO_BIN, 31 EXTNAME_TS, 32 EXTNAME_TXT, 33 FAIL, 34 FILESINFO, 35 FILESINFO_TXT, 36 MAX_WORKER_NUMBER, 37 MODULES_ABC, 38 MODULES_CACHE, 39 NPM_ENTRIES_PROTO_BIN, 40 NPMENTRIES_TXT, 41 OH_MODULES, 42 PACKAGES, 43 PROTO_FILESINFO_TXT, 44 PROTOS, 45 red, 46 reset, 47 SOURCEMAPS, 48 SOURCEMAPS_JSON, 49 WIDGETS_ABC, 50 TS2ABC, 51 ES2ABC 52} from '../common/ark_define'; 53import { 54 needAotCompiler, 55 isMasterOrPrimary 56} from '../utils'; 57import { CommonMode } from '../common/common_mode'; 58import { newSourceMaps } from '../transform'; 59import { 60 changeFileExtension, 61 getEs2abcFileThreadNumber, 62 isCommonJsPluginVirtualFile, 63 isCurrentProjectFiles 64} from '../utils'; 65import { 66 isPackageModulesFile, 67 mkdirsSync, 68 toUnixPath, 69 toHashData, 70 validateFilePathLength 71} from '../../../utils'; 72import { 73 getPackageInfo, 74 getOhmUrlByFilepath, 75 isTs2Abc, 76 isEs2Abc 77} from '../../../ark_utils'; 78import { 79 generateAot, 80 generateBuiltinAbc, 81 FaultHandler 82} from '../../../gen_aot' 83 84export class ModuleInfo { 85 filePath: string; 86 cacheFilePath: string; 87 recordName: string; 88 isCommonJs: boolean; 89 sourceFile: string; 90 packageName: string; 91 92 constructor(filePath: string, cacheFilePath: string, isCommonJs: boolean, recordName: string, sourceFile: string, 93 packageName: string 94 ) { 95 this.filePath = filePath; 96 this.cacheFilePath = cacheFilePath; 97 this.recordName = recordName; 98 this.isCommonJs = isCommonJs; 99 this.sourceFile = sourceFile; 100 this.packageName = packageName; 101 } 102} 103 104export class PackageEntryInfo { 105 pkgEntryPath: string; 106 pkgBuildPath: string; 107 constructor(pkgEntryPath: string, pkgBuildPath: string) { 108 this.pkgEntryPath = pkgEntryPath; 109 this.pkgBuildPath = pkgBuildPath; 110 } 111} 112 113export class ModuleMode extends CommonMode { 114 moduleInfos: Map<String, ModuleInfo>; 115 pkgEntryInfos: Map<String, PackageEntryInfo>; 116 hashJsonObject: any; 117 cacheSourceMapObject: any; 118 filesInfoPath: string; 119 npmEntriesInfoPath: string; 120 moduleAbcPath: string; 121 sourceMapPath: string; 122 cacheFilePath: string; 123 cacheSourceMapPath: string; 124 workerNumber: number; 125 npmEntriesProtoFilePath: string; 126 protoFilePath: string; 127 filterModuleInfos: Map<String, ModuleInfo>; 128 symlinkMap: any; 129 130 constructor(rollupObject: any) { 131 super(rollupObject); 132 this.moduleInfos = new Map<String, ModuleInfo>(); 133 this.pkgEntryInfos = new Map<String, PackageEntryInfo>(); 134 this.hashJsonObject = {}; 135 this.cacheSourceMapObject = {}; 136 this.filesInfoPath = path.join(this.projectConfig.cachePath, FILESINFO_TXT); 137 this.npmEntriesInfoPath = path.join(this.projectConfig.cachePath, NPMENTRIES_TXT); 138 const outPutABC: string = this.projectConfig.widgetCompile ? WIDGETS_ABC : MODULES_ABC; 139 this.moduleAbcPath = path.join(this.projectConfig.aceModuleBuild, outPutABC); 140 this.sourceMapPath = this.arkConfig.isDebug ? path.join(this.projectConfig.aceModuleBuild, SOURCEMAPS) : 141 path.join(this.projectConfig.cachePath, SOURCEMAPS); 142 this.cacheFilePath = path.join(this.projectConfig.cachePath, MODULES_CACHE); 143 this.cacheSourceMapPath = path.join(this.projectConfig.cachePath, SOURCEMAPS_JSON); 144 this.workerNumber = MAX_WORKER_NUMBER; 145 this.npmEntriesProtoFilePath = path.join(this.projectConfig.cachePath, PROTOS, NPM_ENTRIES_PROTO_BIN); 146 this.protoFilePath = path.join(this.projectConfig.cachePath, PROTOS, PROTO_FILESINFO_TXT); 147 this.hashJsonObject = {}; 148 this.filterModuleInfos = new Map<String, ModuleInfo>(); 149 this.symlinkMap = rollupObject.share.symlinkMap; 150 } 151 152 prepareForCompilation(rollupObject: any): void { 153 this.collectModuleFileList(rollupObject, rollupObject.getModuleIds()); 154 this.removeCacheInfo(rollupObject); 155 } 156 157 collectModuleFileList(module: any, fileList: IterableIterator<string>) { 158 let moduleInfos: Map<String, ModuleInfo> = new Map<String, ModuleInfo>(); 159 let pkgEntryInfos: Map<String, PackageEntryInfo> = new Map<String, PackageEntryInfo>(); 160 for (const moduleId of fileList) { 161 if (isCommonJsPluginVirtualFile(moduleId) || !isCurrentProjectFiles(moduleId, this.projectConfig)) { 162 continue; 163 } 164 const moduleInfo: any = module.getModuleInfo(moduleId); 165 if (moduleInfo['meta']['isNodeEntryFile']) { 166 this.getPackageEntryInfo(moduleId, moduleInfo['meta'], pkgEntryInfos); 167 } 168 169 this.processModuleInfos(moduleId, moduleInfos, moduleInfo['meta']); 170 } 171 this.moduleInfos = moduleInfos; 172 this.pkgEntryInfos = pkgEntryInfos; 173 } 174 175 private getPackageEntryInfo(filePath: string, metaInfo: any, pkgEntryInfos: Map<String, PackageEntryInfo>) { 176 if (metaInfo['isLocalDependency']) { 177 const hostModulesInfo: any = metaInfo['hostModulesInfo']; 178 const pkgBuildPath: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, metaInfo['moduleName']); 179 hostModulesInfo.forEach(hostModuleInfo => { 180 const hostDependencyName: string = hostModuleInfo['hostDependencyName']; 181 const hostModuleName: string = hostModuleInfo['hostModuleName']; 182 const pkgEntryPath: string = toUnixPath(path.join(`${PACKAGES}@${hostModuleName}`, hostDependencyName)); 183 if (!pkgEntryInfos.has(pkgEntryPath)) { 184 pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath)); 185 } 186 }); 187 return; 188 } 189 190 if (!metaInfo['pkgPath']) { 191 this.logger.debug("Failed to get 'pkgPath' from metaInfo. File: ", filePath); 192 return; 193 } 194 const pkgPath: string = metaInfo['pkgPath']; 195 let originPkgEntryPath: string = toUnixPath(filePath.replace(pkgPath, '')); 196 if (originPkgEntryPath.startsWith('/')) { 197 originPkgEntryPath = originPkgEntryPath.slice(1, originPkgEntryPath.length); 198 } 199 const pkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(pkgPath)); 200 let pkgBuildPath: string = path.join(pkgEntryPath, originPkgEntryPath); 201 pkgBuildPath = toUnixPath(pkgBuildPath.substring(0, pkgBuildPath.lastIndexOf('.'))); 202 if (!pkgEntryInfos.has(pkgEntryPath)) { 203 pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath)); 204 } 205 // create symlink path to actual path mapping in ohpm 206 if (this.projectConfig.packageDir == OH_MODULES && this.symlinkMap) { 207 const symlinkEntries: any = Object.entries(this.symlinkMap); 208 for (const [actualPath, symlinkPaths] of symlinkEntries) { 209 if (actualPath === pkgPath) { 210 (<string[]>symlinkPaths).forEach((symlink: string) => { 211 const symlinkPkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(symlink)); 212 if (!pkgEntryInfos.has(symlinkPkgEntryPath)) { 213 pkgEntryInfos.set(symlinkPkgEntryPath, new PackageEntryInfo(symlinkPkgEntryPath, pkgEntryPath)); 214 } 215 }); 216 break; 217 } 218 } 219 } 220 } 221 222 private processModuleInfos(moduleId: string, moduleInfos: Map<String, ModuleInfo>, metaInfo?: any) { 223 switch (path.extname(moduleId)) { 224 case EXTNAME_ETS: { 225 const extName: string = this.projectConfig.processTs ? EXTNAME_TS : EXTNAME_JS; 226 this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos); 227 break; 228 } 229 case EXTNAME_TS: { 230 const extName: string = this.projectConfig.processTs ? '' : EXTNAME_JS; 231 this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos); 232 break; 233 } 234 case EXTNAME_JS: 235 case EXTNAME_MJS: 236 case EXTNAME_CJS: { 237 const extName: string = (moduleId.endsWith(EXTNAME_MJS) || moduleId.endsWith(EXTNAME_CJS)) ? EXTNAME_JS : ''; 238 const isCommonJS: boolean = metaInfo && metaInfo['commonjs'] && metaInfo['commonjs']['isCommonJS']; 239 this.addModuleInfoItem(moduleId, isCommonJS, extName, metaInfo, moduleInfos); 240 break; 241 } 242 case EXTNAME_JSON: { 243 this.addModuleInfoItem(moduleId, false, '', metaInfo, moduleInfos); 244 break; 245 } 246 default: 247 break; 248 } 249 } 250 251 private addModuleInfoItem(filePath: string, isCommonJs: boolean, extName: string, metaInfo: any, moduleInfos: any) { 252 let namespace: string = metaInfo['moduleName']; 253 let recordName: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, namespace); 254 let sourceFile: string = filePath.replace(this.projectConfig.projectRootPath + path.sep, ''); 255 let cacheFilePath: string = 256 this.genFileCachePath(filePath, this.projectConfig.projectRootPath, this.projectConfig.cachePath); 257 let packageName: string = ''; 258 if (isPackageModulesFile(filePath, this.projectConfig)) { 259 packageName = this.getPkgModulesFilePkgName(metaInfo['pkgPath']); 260 } else { 261 packageName = 262 metaInfo['isLocalDependency'] ? namespace : getPackageInfo(this.projectConfig.aceModuleJsonPath)[1]; 263 } 264 265 if (extName.length !== 0) { 266 cacheFilePath = changeFileExtension(cacheFilePath, extName); 267 } 268 269 cacheFilePath = toUnixPath(cacheFilePath); 270 recordName = toUnixPath(recordName); 271 sourceFile = toUnixPath(sourceFile); 272 packageName = toUnixPath(packageName); 273 274 moduleInfos.set(filePath, new ModuleInfo(filePath, cacheFilePath, isCommonJs, recordName, sourceFile, packageName)); 275 } 276 277 updateCachedSourceMaps(): void { 278 if (!fs.existsSync(this.cacheSourceMapPath)) { 279 this.cacheSourceMapObject = newSourceMaps; 280 return; 281 } 282 283 this.cacheSourceMapObject = JSON.parse(fs.readFileSync(this.cacheSourceMapPath).toString()); 284 285 // remove unused source files's sourceMap 286 let unusedFiles = []; 287 let compileFileList: Set<string> = new Set(); 288 this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleId: string) => { 289 compileFileList.add(toUnixPath(moduleId)); 290 }) 291 292 Object.keys(this.cacheSourceMapObject).forEach(key => { 293 const sourceFileAbsolutePath: string = toUnixPath(path.join(this.projectConfig.projectRootPath, key)); 294 if (!compileFileList.has(sourceFileAbsolutePath)) { 295 unusedFiles.push(key); 296 } 297 }); 298 unusedFiles.forEach(file => { 299 delete this.cacheSourceMapObject[file]; 300 }) 301 302 // update sourceMap 303 Object.keys(newSourceMaps).forEach(key => { 304 this.cacheSourceMapObject[key] = newSourceMaps[key]; 305 }); 306 } 307 308 buildModuleSourceMapInfo() { 309 if (this.projectConfig.widgetCompile) { 310 return; 311 } 312 313 this.updateCachedSourceMaps(); 314 fs.writeFile(this.sourceMapPath, JSON.stringify(this.cacheSourceMapObject, null, 2), 'utf-8', (err) => { 315 if (err) { 316 this.throwArkTsCompilerError('ArkTS:ERROR failed to write sourceMaps'); 317 } 318 fs.copyFileSync(this.sourceMapPath, this.cacheSourceMapPath); 319 }); 320 } 321 322 generateEs2AbcCmd() { 323 const fileThreads = getEs2abcFileThreadNumber(); 324 this.cmdArgs.push(`"@${this.filesInfoPath}"`); 325 this.cmdArgs.push('--npm-module-entry-list'); 326 this.cmdArgs.push(`"${this.npmEntriesInfoPath}"`); 327 this.cmdArgs.push('--output'); 328 this.cmdArgs.push(`"${this.moduleAbcPath}"`); 329 this.cmdArgs.push('--file-threads'); 330 this.cmdArgs.push(`"${fileThreads}"`); 331 this.cmdArgs.push('--merge-abc'); 332 } 333 334 addCacheFileArgs() { 335 this.cmdArgs.push('--cache-file'); 336 this.cmdArgs.push(`"@${this.cacheFilePath}"`); 337 } 338 339 private generateCompileFilesInfo() { 340 let filesInfo: string = ''; 341 this.moduleInfos.forEach((info) => { 342 const moduleType: string = info.isCommonJs ? COMMONJS : ESM; 343 filesInfo += `${info.cacheFilePath};${info.recordName};${moduleType};${info.sourceFile};${info.packageName}\n`; 344 }); 345 fs.writeFileSync(this.filesInfoPath, filesInfo, 'utf-8'); 346 } 347 348 private generateNpmEntriesInfo() { 349 let entriesInfo: string = ''; 350 for (const value of this.pkgEntryInfos.values()) { 351 entriesInfo += `${value.pkgEntryPath}:${value.pkgBuildPath}\n`; 352 } 353 fs.writeFileSync(this.npmEntriesInfoPath, entriesInfo, 'utf-8'); 354 } 355 356 private generateAbcCacheFilesInfo(): void { 357 let abcCacheFilesInfo: string = ''; 358 359 // generate source file cache 360 this.moduleInfos.forEach((info) => { 361 let abcCacheFilePath: string = changeFileExtension(info.cacheFilePath, EXTNAME_PROTO_BIN); 362 abcCacheFilesInfo += `${info.cacheFilePath};${abcCacheFilePath}\n`; 363 }); 364 365 // generate npm entries cache 366 let npmEntriesCacheFilePath: string = changeFileExtension(this.npmEntriesInfoPath, EXTNAME_PROTO_BIN); 367 abcCacheFilesInfo += `${this.npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`; 368 369 fs.writeFileSync(this.cacheFilePath, abcCacheFilesInfo, 'utf-8'); 370 } 371 372 private genDescriptionsForMergedEs2abc() { 373 this.generateCompileFilesInfo(); 374 this.generateNpmEntriesInfo(); 375 this.generateAbcCacheFilesInfo(); 376 } 377 378 generateMergedAbcOfEs2Abc() { 379 // collect data error from subprocess 380 let errMsg: string = ''; 381 this.genDescriptionsForMergedEs2abc(); 382 const genAbcCmd: string = this.cmdArgs.join(' '); 383 try { 384 const child = this.triggerAsync(() => { 385 return childProcess.exec(genAbcCmd, { windowsHide: true }); 386 }); 387 child.on('exit', (code: any) => { 388 if (code === FAIL) { 389 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc'); 390 } 391 this.triggerEndSignal(); 392 }); 393 394 child.on('error', (err: any) => { 395 this.throwArkTsCompilerError(err.toString()); 396 }); 397 398 child.stderr.on('data', (data: any) => { 399 errMsg += data.toString(); 400 }); 401 402 child.stderr.on('end', (data: any) => { 403 if (errMsg !== undefined && errMsg.length > 0) { 404 this.logger.error(red, errMsg, reset); 405 } 406 }); 407 } catch (e) { 408 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc. Error message: ' + e.toString()); 409 } 410 } 411 412 filterModulesByHashJson() { 413 if (this.hashJsonFilePath.length === 0 || !fs.existsSync(this.hashJsonFilePath)) { 414 for (const key of this.moduleInfos.keys()) { 415 this.filterModuleInfos.set(key, this.moduleInfos.get(key)); 416 } 417 return; 418 } 419 420 let updatedJsonObject: any = {}; 421 let jsonObject: any = {}; 422 let jsonFile: string = ''; 423 424 if (fs.existsSync(this.hashJsonFilePath)) { 425 jsonFile = fs.readFileSync(this.hashJsonFilePath).toString(); 426 jsonObject = JSON.parse(jsonFile); 427 this.filterModuleInfos = new Map<string, ModuleInfo>(); 428 for (const [key, value] of this.moduleInfos) { 429 const cacheFilePath: string = value.cacheFilePath; 430 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 431 if (!fs.existsSync(cacheFilePath)) { 432 this.throwArkTsCompilerError(`ArkTS:ERROR ${cacheFilePath} is lost`); 433 } 434 if (fs.existsSync(cacheProtoFilePath)) { 435 const hashCacheFileContentData: any = toHashData(cacheFilePath); 436 const hashProtoFileContentData: any = toHashData(cacheProtoFilePath); 437 if (jsonObject[cacheFilePath] === hashCacheFileContentData && 438 jsonObject[cacheProtoFilePath] === hashProtoFileContentData) { 439 updatedJsonObject[cacheFilePath] = cacheFilePath; 440 updatedJsonObject[cacheProtoFilePath] = cacheProtoFilePath; 441 continue; 442 } 443 } 444 this.filterModuleInfos.set(key, value); 445 } 446 } 447 448 this.hashJsonObject = updatedJsonObject; 449 } 450 451 getSplittedModulesByNumber() { 452 const result: any = []; 453 if (this.filterModuleInfos.size < this.workerNumber) { 454 for (const value of this.filterModuleInfos.values()) { 455 result.push([value]); 456 } 457 return result; 458 } 459 460 for (let i = 0; i < this.workerNumber; ++i) { 461 result.push([]); 462 } 463 464 let pos: number = 0; 465 for (const value of this.filterModuleInfos.values()) { 466 const chunk = pos % this.workerNumber; 467 result[chunk].push(value); 468 pos++; 469 } 470 471 return result; 472 } 473 474 invokeTs2AbcWorkersToGenProto(splittedModules) { 475 let ts2abcCmdArgs: string[] = this.cmdArgs.slice(0); 476 ts2abcCmdArgs.push('--output-proto'); 477 ts2abcCmdArgs.push('--merge-abc'); 478 ts2abcCmdArgs.push('--input-file'); 479 if (isMasterOrPrimary()) { 480 this.setupCluster(cluster); 481 this.workerNumber = splittedModules.length; 482 for (let i = 0; i < this.workerNumber; ++i) { 483 const sn: number = i + 1; 484 const workerFileName: string = `${FILESINFO}_${sn}${EXTNAME_TXT}`; 485 const workerData: any = { 486 inputs: JSON.stringify(splittedModules[i]), 487 cmd: ts2abcCmdArgs.join(' '), 488 workerFileName: workerFileName, 489 mode: ESMODULE, 490 cachePath: this.projectConfig.cachePath 491 }; 492 this.triggerAsync(() => { 493 const worker: any = cluster.fork(workerData); 494 worker.on('message', (errorMsg) => { 495 this.logger.error(red, errorMsg.data.toString(), reset); 496 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc'); 497 }); 498 }); 499 } 500 } 501 } 502 503 processTs2abcWorkersToGenAbc() { 504 this.generateNpmEntriesInfo(); 505 let workerCount: number = 0; 506 if (isMasterOrPrimary()) { 507 cluster.on('exit', (worker, code, signal) => { 508 if (code === FAIL) { 509 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc'); 510 } 511 workerCount++; 512 if (workerCount === this.workerNumber) { 513 this.generateNpmEntryToGenProto(); 514 this.generateProtoFilesInfo(); 515 this.mergeProtoToAbc(); 516 this.processAotIfNeeded(); 517 this.afterCompilationProcess(); 518 } 519 this.triggerEndSignal(); 520 }); 521 if (this.workerNumber == 0) { 522 // process aot for no source file changed. 523 this.processAotIfNeeded(); 524 } 525 } 526 } 527 528 private processAotIfNeeded(): void { 529 if (!needAotCompiler(this.projectConfig)) { 530 return; 531 } 532 let faultHandler: FaultHandler = ((error: string) => { this.throwArkTsCompilerError(error); }) 533 const builtinAbcPath: string = generateBuiltinAbc(this.arkConfig.arkRootPath, this.cmdArgs, 534 this.projectConfig.cachePath, this.logger, faultHandler); 535 generateAot(this.arkConfig.arkRootPath, builtinAbcPath, this.projectConfig, this.logger, faultHandler); 536 } 537 538 private genFileCachePath(filePath: string, projectRootPath: string, cachePath: string): string { 539 const sufStr: string = toUnixPath(filePath).replace(toUnixPath(projectRootPath), ''); 540 const output: string = path.join(cachePath, sufStr); 541 return output; 542 } 543 544 private getPkgModulesFilePkgName(pkgPath: string) { 545 pkgPath = toUnixPath(pkgPath); 546 const packageDir: string = this.projectConfig.packageDir; 547 const projectRootPath = toUnixPath(this.projectConfig.projectRootPath); 548 const projectPkgModulesPath: string = toUnixPath(path.join(projectRootPath, packageDir)); 549 let pkgName: string = ''; 550 if (pkgPath.includes(projectPkgModulesPath)) { 551 pkgName = path.join(PACKAGES, pkgPath.replace(projectPkgModulesPath, '')); 552 } else { 553 for (const key in this.projectConfig.modulePathMap) { 554 const value: string = this.projectConfig.modulePathMap[key]; 555 const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir)); 556 if (pkgPath.indexOf(fakeModulePkgModulesPath) !== -1) { 557 const tempFilePath: string = pkgPath.replace(projectRootPath, ''); 558 pkgName = path.join(`${PACKAGES}@${key}`, 559 tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1)); 560 break; 561 } 562 } 563 } 564 565 return pkgName.replace(new RegExp(packageDir, 'g'), PACKAGES); 566 } 567 568 private generateProtoFilesInfo() { 569 validateFilePathLength(this.protoFilePath, this.logger); 570 mkdirsSync(path.dirname(this.protoFilePath)); 571 let protoFilesInfo: string = ''; 572 const sortModuleInfos: any = new Map([...this.moduleInfos].sort()); 573 for (const value of sortModuleInfos.values()) { 574 const cacheProtoPath: string = changeFileExtension(value.cacheFilePath, EXTNAME_PROTO_BIN); 575 protoFilesInfo += `${toUnixPath(cacheProtoPath)}\n`; 576 } 577 if (this.pkgEntryInfos.size > 0) { 578 protoFilesInfo += `${toUnixPath(this.npmEntriesProtoFilePath)}\n`; 579 } 580 fs.writeFileSync(this.protoFilePath, protoFilesInfo, 'utf-8'); 581 } 582 583 private mergeProtoToAbc() { 584 mkdirsSync(this.projectConfig.aceModuleBuild); 585 const cmd: any = `"${this.arkConfig.mergeAbcPath}" --input "@${this.protoFilePath}" --outputFilePath "${ 586 this.projectConfig.aceModuleBuild}" --output ${MODULES_ABC} --suffix protoBin`; 587 try { 588 childProcess.execSync(cmd, { windowsHide: true }); 589 } catch (e) { 590 this.throwArkTsCompilerError(`ArkTS:ERROR failed to merge proto file to abc, error message:` + e.toString()); 591 } 592 } 593 594 private afterCompilationProcess() { 595 this.writeHashJson(); 596 } 597 598 private writeHashJson() { 599 if (this.hashJsonFilePath.length === 0) { 600 return; 601 } 602 603 for (const value of this.filterModuleInfos.values()) { 604 const cacheFilePath: string = value.cacheFilePath; 605 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 606 if (!fs.existsSync(cacheFilePath) || !fs.existsSync(cacheProtoFilePath)) { 607 this.throwArkTsCompilerError( 608 `ArkTS:ERROR ${cacheFilePath} or ${cacheProtoFilePath} is lost` 609 ); 610 } 611 const hashCacheFileContentData: any = toHashData(cacheFilePath); 612 const hashCacheProtoContentData: any = toHashData(cacheProtoFilePath); 613 this.hashJsonObject[cacheFilePath] = hashCacheFileContentData; 614 this.hashJsonObject[cacheProtoFilePath] = hashCacheProtoContentData; 615 } 616 617 fs.writeFileSync(this.hashJsonFilePath, JSON.stringify(this.hashJsonObject)); 618 } 619 620 private generateNpmEntryToGenProto() { 621 if (this.pkgEntryInfos.size <= 0) { 622 return; 623 } 624 mkdirsSync(path.dirname(this.npmEntriesProtoFilePath)); 625 const cmd: string = `"${this.arkConfig.js2abcPath}" --compile-npm-entries "${ 626 this.npmEntriesInfoPath}" "${this.npmEntriesProtoFilePath}"`; 627 try { 628 childProcess.execSync(cmd, { windowsHide: true }); 629 } catch (e) { 630 this.throwArkTsCompilerError(`ArkTS:ERROR failed to generate npm proto file to abc. Error message: ` + e.toString()); 631 } 632 } 633 634 private removeCompilationCache(): void { 635 if (isEs2Abc(this.projectConfig)) { 636 this.removeEs2abcCompilationCache(); 637 } else if (isTs2Abc(this.projectConfig)) { 638 this.removeTs2abcCompilationCache(); 639 } else { 640 this.throwArkTsCompilerError(`Invalid projectConfig.pandaMode for module build, should be either 641 "${TS2ABC}" or "${ES2ABC}"`); 642 } 643 } 644 645 private removeEs2abcCompilationCache(): void { 646 if (fs.existsSync(this.cacheFilePath)) { 647 const data: string = fs.readFileSync(this.cacheFilePath, 'utf-8'); 648 const lines: any = data.split(/\r?\n/); 649 lines.forEach(line => { 650 const [, abcCacheFilePath]: any = line.split(';'); 651 if (fs.existsSync(abcCacheFilePath)) { 652 fs.unlinkSync(abcCacheFilePath); 653 } 654 }); 655 fs.unlinkSync(this.cacheFilePath); 656 } 657 } 658 659 private removeTs2abcCompilationCache(): void { 660 if (fs.existsSync(this.hashJsonFilePath)) { 661 fs.unlinkSync(this.hashJsonFilePath); 662 } 663 if (fs.existsSync(this.protoFilePath)) { 664 const data: string = fs.readFileSync(this.protoFilePath, 'utf-8'); 665 const lines: any = data.split(/\r?\n/); 666 lines.forEach(line => { 667 const protoFilePath: string = line; 668 if (fs.existsSync(protoFilePath)) { 669 fs.unlinkSync(protoFilePath); 670 } 671 }); 672 fs.unlinkSync(this.protoFilePath); 673 } 674 } 675} 676