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 this.triggerAsync(() => { 315 fs.writeFile(this.sourceMapPath, JSON.stringify(this.cacheSourceMapObject, null, 2), 'utf-8', (err) => { 316 if (err) { 317 this.throwArkTsCompilerError('ArkTS:ERROR failed to write sourceMaps'); 318 } 319 fs.copyFileSync(this.sourceMapPath, this.cacheSourceMapPath); 320 this.triggerEndSignal(); 321 }); 322 }); 323 } 324 325 generateEs2AbcCmd() { 326 const fileThreads = getEs2abcFileThreadNumber(); 327 this.cmdArgs.push(`"@${this.filesInfoPath}"`); 328 this.cmdArgs.push('--npm-module-entry-list'); 329 this.cmdArgs.push(`"${this.npmEntriesInfoPath}"`); 330 this.cmdArgs.push('--output'); 331 this.cmdArgs.push(`"${this.moduleAbcPath}"`); 332 this.cmdArgs.push('--file-threads'); 333 this.cmdArgs.push(`"${fileThreads}"`); 334 this.cmdArgs.push('--merge-abc'); 335 } 336 337 addCacheFileArgs() { 338 this.cmdArgs.push('--cache-file'); 339 this.cmdArgs.push(`"@${this.cacheFilePath}"`); 340 } 341 342 private generateCompileFilesInfo() { 343 let filesInfo: string = ''; 344 this.moduleInfos.forEach((info) => { 345 const moduleType: string = info.isCommonJs ? COMMONJS : ESM; 346 filesInfo += `${info.cacheFilePath};${info.recordName};${moduleType};${info.sourceFile};${info.packageName}\n`; 347 }); 348 fs.writeFileSync(this.filesInfoPath, filesInfo, 'utf-8'); 349 } 350 351 private generateNpmEntriesInfo() { 352 let entriesInfo: string = ''; 353 for (const value of this.pkgEntryInfos.values()) { 354 entriesInfo += `${value.pkgEntryPath}:${value.pkgBuildPath}\n`; 355 } 356 fs.writeFileSync(this.npmEntriesInfoPath, entriesInfo, 'utf-8'); 357 } 358 359 private generateAbcCacheFilesInfo(): void { 360 let abcCacheFilesInfo: string = ''; 361 362 // generate source file cache 363 this.moduleInfos.forEach((info) => { 364 let abcCacheFilePath: string = changeFileExtension(info.cacheFilePath, EXTNAME_PROTO_BIN); 365 abcCacheFilesInfo += `${info.cacheFilePath};${abcCacheFilePath}\n`; 366 }); 367 368 // generate npm entries cache 369 let npmEntriesCacheFilePath: string = changeFileExtension(this.npmEntriesInfoPath, EXTNAME_PROTO_BIN); 370 abcCacheFilesInfo += `${this.npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`; 371 372 fs.writeFileSync(this.cacheFilePath, abcCacheFilesInfo, 'utf-8'); 373 } 374 375 private genDescriptionsForMergedEs2abc() { 376 this.generateCompileFilesInfo(); 377 this.generateNpmEntriesInfo(); 378 this.generateAbcCacheFilesInfo(); 379 } 380 381 generateMergedAbcOfEs2Abc() { 382 // collect data error from subprocess 383 let errMsg: string = ''; 384 this.genDescriptionsForMergedEs2abc(); 385 const genAbcCmd: string = this.cmdArgs.join(' '); 386 try { 387 const child = this.triggerAsync(() => { 388 return childProcess.exec(genAbcCmd, { windowsHide: true }); 389 }); 390 child.on('exit', (code: any) => { 391 if (code === FAIL) { 392 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc'); 393 } 394 this.triggerEndSignal(); 395 }); 396 397 child.on('error', (err: any) => { 398 this.throwArkTsCompilerError(err.toString()); 399 }); 400 401 child.stderr.on('data', (data: any) => { 402 errMsg += data.toString(); 403 }); 404 405 child.stderr.on('end', (data: any) => { 406 if (errMsg !== undefined && errMsg.length > 0) { 407 this.logger.error(red, errMsg, reset); 408 } 409 }); 410 } catch (e) { 411 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc. Error message: ' + e.toString()); 412 } 413 } 414 415 filterModulesByHashJson() { 416 if (this.hashJsonFilePath.length === 0 || !fs.existsSync(this.hashJsonFilePath)) { 417 for (const key of this.moduleInfos.keys()) { 418 this.filterModuleInfos.set(key, this.moduleInfos.get(key)); 419 } 420 return; 421 } 422 423 let updatedJsonObject: any = {}; 424 let jsonObject: any = {}; 425 let jsonFile: string = ''; 426 427 if (fs.existsSync(this.hashJsonFilePath)) { 428 jsonFile = fs.readFileSync(this.hashJsonFilePath).toString(); 429 jsonObject = JSON.parse(jsonFile); 430 this.filterModuleInfos = new Map<string, ModuleInfo>(); 431 for (const [key, value] of this.moduleInfos) { 432 const cacheFilePath: string = value.cacheFilePath; 433 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 434 if (!fs.existsSync(cacheFilePath)) { 435 this.throwArkTsCompilerError(`ArkTS:ERROR ${cacheFilePath} is lost`); 436 } 437 if (fs.existsSync(cacheProtoFilePath)) { 438 const hashCacheFileContentData: any = toHashData(cacheFilePath); 439 const hashProtoFileContentData: any = toHashData(cacheProtoFilePath); 440 if (jsonObject[cacheFilePath] === hashCacheFileContentData && 441 jsonObject[cacheProtoFilePath] === hashProtoFileContentData) { 442 updatedJsonObject[cacheFilePath] = cacheFilePath; 443 updatedJsonObject[cacheProtoFilePath] = cacheProtoFilePath; 444 continue; 445 } 446 } 447 this.filterModuleInfos.set(key, value); 448 } 449 } 450 451 this.hashJsonObject = updatedJsonObject; 452 } 453 454 getSplittedModulesByNumber() { 455 const result: any = []; 456 if (this.filterModuleInfos.size < this.workerNumber) { 457 for (const value of this.filterModuleInfos.values()) { 458 result.push([value]); 459 } 460 return result; 461 } 462 463 for (let i = 0; i < this.workerNumber; ++i) { 464 result.push([]); 465 } 466 467 let pos: number = 0; 468 for (const value of this.filterModuleInfos.values()) { 469 const chunk = pos % this.workerNumber; 470 result[chunk].push(value); 471 pos++; 472 } 473 474 return result; 475 } 476 477 invokeTs2AbcWorkersToGenProto(splittedModules) { 478 let ts2abcCmdArgs: string[] = this.cmdArgs.slice(0); 479 ts2abcCmdArgs.push('--output-proto'); 480 ts2abcCmdArgs.push('--merge-abc'); 481 ts2abcCmdArgs.push('--input-file'); 482 if (isMasterOrPrimary()) { 483 this.setupCluster(cluster); 484 this.workerNumber = splittedModules.length; 485 for (let i = 0; i < this.workerNumber; ++i) { 486 const sn: number = i + 1; 487 const workerFileName: string = `${FILESINFO}_${sn}${EXTNAME_TXT}`; 488 const workerData: any = { 489 inputs: JSON.stringify(splittedModules[i]), 490 cmd: ts2abcCmdArgs.join(' '), 491 workerFileName: workerFileName, 492 mode: ESMODULE, 493 cachePath: this.projectConfig.cachePath 494 }; 495 this.triggerAsync(() => { 496 const worker: any = cluster.fork(workerData); 497 worker.on('message', (errorMsg) => { 498 this.logger.error(red, errorMsg.data.toString(), reset); 499 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc'); 500 }); 501 }); 502 } 503 } 504 } 505 506 processTs2abcWorkersToGenAbc() { 507 this.generateNpmEntriesInfo(); 508 let workerCount: number = 0; 509 if (isMasterOrPrimary()) { 510 cluster.on('exit', (worker, code, signal) => { 511 if (code === FAIL) { 512 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc'); 513 } 514 workerCount++; 515 if (workerCount === this.workerNumber) { 516 this.generateNpmEntryToGenProto(); 517 this.generateProtoFilesInfo(); 518 this.mergeProtoToAbc(); 519 this.processAotIfNeeded(); 520 this.afterCompilationProcess(); 521 } 522 this.triggerEndSignal(); 523 }); 524 if (this.workerNumber == 0) { 525 // process aot for no source file changed. 526 this.processAotIfNeeded(); 527 } 528 } 529 } 530 531 private processAotIfNeeded(): void { 532 if (!needAotCompiler(this.projectConfig)) { 533 return; 534 } 535 let faultHandler: FaultHandler = ((error: string) => { this.throwArkTsCompilerError(error); }) 536 const builtinAbcPath: string = generateBuiltinAbc(this.arkConfig.arkRootPath, this.cmdArgs, 537 this.projectConfig.cachePath, this.logger, faultHandler); 538 generateAot(this.arkConfig.arkRootPath, builtinAbcPath, this.projectConfig, this.logger, faultHandler); 539 } 540 541 private genFileCachePath(filePath: string, projectRootPath: string, cachePath: string): string { 542 const sufStr: string = toUnixPath(filePath).replace(toUnixPath(projectRootPath), ''); 543 const output: string = path.join(cachePath, sufStr); 544 return output; 545 } 546 547 private getPkgModulesFilePkgName(pkgPath: string) { 548 pkgPath = toUnixPath(pkgPath); 549 const packageDir: string = this.projectConfig.packageDir; 550 const projectRootPath = toUnixPath(this.projectConfig.projectRootPath); 551 const projectPkgModulesPath: string = toUnixPath(path.join(projectRootPath, packageDir)); 552 let pkgName: string = ''; 553 if (pkgPath.includes(projectPkgModulesPath)) { 554 pkgName = path.join(PACKAGES, pkgPath.replace(projectPkgModulesPath, '')); 555 } else { 556 for (const key in this.projectConfig.modulePathMap) { 557 const value: string = this.projectConfig.modulePathMap[key]; 558 const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir)); 559 if (pkgPath.indexOf(fakeModulePkgModulesPath) !== -1) { 560 const tempFilePath: string = pkgPath.replace(projectRootPath, ''); 561 pkgName = path.join(`${PACKAGES}@${key}`, 562 tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1)); 563 break; 564 } 565 } 566 } 567 568 return pkgName.replace(new RegExp(packageDir, 'g'), PACKAGES); 569 } 570 571 private generateProtoFilesInfo() { 572 validateFilePathLength(this.protoFilePath, this.logger); 573 mkdirsSync(path.dirname(this.protoFilePath)); 574 let protoFilesInfo: string = ''; 575 const sortModuleInfos: any = new Map([...this.moduleInfos].sort()); 576 for (const value of sortModuleInfos.values()) { 577 const cacheProtoPath: string = changeFileExtension(value.cacheFilePath, EXTNAME_PROTO_BIN); 578 protoFilesInfo += `${toUnixPath(cacheProtoPath)}\n`; 579 } 580 if (this.pkgEntryInfos.size > 0) { 581 protoFilesInfo += `${toUnixPath(this.npmEntriesProtoFilePath)}\n`; 582 } 583 fs.writeFileSync(this.protoFilePath, protoFilesInfo, 'utf-8'); 584 } 585 586 private mergeProtoToAbc() { 587 mkdirsSync(this.projectConfig.aceModuleBuild); 588 const cmd: any = `"${this.arkConfig.mergeAbcPath}" --input "@${this.protoFilePath}" --outputFilePath "${ 589 this.projectConfig.aceModuleBuild}" --output ${MODULES_ABC} --suffix protoBin`; 590 try { 591 childProcess.execSync(cmd, { windowsHide: true }); 592 } catch (e) { 593 this.throwArkTsCompilerError(`ArkTS:ERROR failed to merge proto file to abc, error message:` + e.toString()); 594 } 595 } 596 597 private afterCompilationProcess() { 598 this.writeHashJson(); 599 } 600 601 private writeHashJson() { 602 if (this.hashJsonFilePath.length === 0) { 603 return; 604 } 605 606 for (const value of this.filterModuleInfos.values()) { 607 const cacheFilePath: string = value.cacheFilePath; 608 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 609 if (!fs.existsSync(cacheFilePath) || !fs.existsSync(cacheProtoFilePath)) { 610 this.throwArkTsCompilerError( 611 `ArkTS:ERROR ${cacheFilePath} or ${cacheProtoFilePath} is lost` 612 ); 613 } 614 const hashCacheFileContentData: any = toHashData(cacheFilePath); 615 const hashCacheProtoContentData: any = toHashData(cacheProtoFilePath); 616 this.hashJsonObject[cacheFilePath] = hashCacheFileContentData; 617 this.hashJsonObject[cacheProtoFilePath] = hashCacheProtoContentData; 618 } 619 620 fs.writeFileSync(this.hashJsonFilePath, JSON.stringify(this.hashJsonObject)); 621 } 622 623 private generateNpmEntryToGenProto() { 624 if (this.pkgEntryInfos.size <= 0) { 625 return; 626 } 627 mkdirsSync(path.dirname(this.npmEntriesProtoFilePath)); 628 const cmd: string = `"${this.arkConfig.js2abcPath}" --compile-npm-entries "${ 629 this.npmEntriesInfoPath}" "${this.npmEntriesProtoFilePath}"`; 630 try { 631 childProcess.execSync(cmd, { windowsHide: true }); 632 } catch (e) { 633 this.throwArkTsCompilerError(`ArkTS:ERROR failed to generate npm proto file to abc. Error message: ` + e.toString()); 634 } 635 } 636 637 private removeCompilationCache(): void { 638 if (isEs2Abc(this.projectConfig)) { 639 this.removeEs2abcCompilationCache(); 640 } else if (isTs2Abc(this.projectConfig)) { 641 this.removeTs2abcCompilationCache(); 642 } else { 643 this.throwArkTsCompilerError(`Invalid projectConfig.pandaMode for module build, should be either 644 "${TS2ABC}" or "${ES2ABC}"`); 645 } 646 } 647 648 private removeEs2abcCompilationCache(): void { 649 if (fs.existsSync(this.cacheFilePath)) { 650 const data: string = fs.readFileSync(this.cacheFilePath, 'utf-8'); 651 const lines: any = data.split(/\r?\n/); 652 lines.forEach(line => { 653 const [, abcCacheFilePath]: any = line.split(';'); 654 if (fs.existsSync(abcCacheFilePath)) { 655 fs.unlinkSync(abcCacheFilePath); 656 } 657 }); 658 fs.unlinkSync(this.cacheFilePath); 659 } 660 } 661 662 private removeTs2abcCompilationCache(): void { 663 if (fs.existsSync(this.hashJsonFilePath)) { 664 fs.unlinkSync(this.hashJsonFilePath); 665 } 666 if (fs.existsSync(this.protoFilePath)) { 667 const data: string = fs.readFileSync(this.protoFilePath, 'utf-8'); 668 const lines: any = data.split(/\r?\n/); 669 lines.forEach(line => { 670 const protoFilePath: string = line; 671 if (fs.existsSync(protoFilePath)) { 672 fs.unlinkSync(protoFilePath); 673 } 674 }); 675 fs.unlinkSync(this.protoFilePath); 676 } 677 } 678} 679