1/* 2 * Copyright (c) 2024 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 fs from 'fs'; 17import path from 'path'; 18 19import { ModuleMode } from './module_mode'; 20import { 21 blue, 22 reset, 23 MODULES_ABC, 24 SOURCEMAPS, 25 SYMBOLMAP 26} from '../common/ark_define'; 27import { isJsonSourceFile } from '../utils'; 28import { 29 mkdirsSync, 30 toUnixPath, 31 validateFilePathLength 32} from '../../../utils'; 33import { 34 createAndStartEvent, 35 stopEvent 36} from '../../../ark_utils'; 37import { SourceMapGenerator } from '../generate_sourcemap'; 38import { 39 ArkTSInternalErrorDescription, 40 ErrorCode 41} from '../error_code'; 42import { 43 LogData, 44 LogDataFactory 45} from '../logger'; 46 47let isFirstBuild: boolean = true; 48 49export class ModuleColdreloadMode extends ModuleMode { 50 symbolMapFilePath: string; 51 constructor(rollupObject: Object) { 52 super(rollupObject); 53 if (this.projectConfig.oldMapFilePath) { 54 this.symbolMapFilePath = path.join(this.projectConfig.oldMapFilePath, SYMBOLMAP); 55 } else { 56 const errInfo: LogData = LogDataFactory.newInstance( 57 ErrorCode.ETS2BUNDLE_INTERNAL_COLD_RELOAD_FAILED_INCORRECT_SYMBOL_MAP_CONFIG, 58 ArkTSInternalErrorDescription, 59 'Coldreload failed, symbolMap file is not correctly configured.' 60 ); 61 this.logger.printErrorAndExit(errInfo); 62 } 63 } 64 65 generateAbc(rollupObject: Object, parentEvent: Object): void { 66 isFirstBuild = this.projectConfig.isFirstBuild; 67 if (isFirstBuild) { 68 this.compileAllFiles(rollupObject, parentEvent); 69 } else { 70 this.compileChangeListFiles(rollupObject, parentEvent); 71 } 72 } 73 74 addColdReloadArgs(): void { 75 if (isFirstBuild) { 76 this.cmdArgs.push('--dump-symbol-table'); 77 this.cmdArgs.push(`"${this.symbolMapFilePath}"`); 78 return; 79 } 80 this.addCacheFileArgs(); 81 this.cmdArgs.push('--input-symbol-table'); 82 this.cmdArgs.push(`"${this.symbolMapFilePath}"`); 83 this.cmdArgs.push('--cold-reload'); 84 } 85 86 private compileAllFiles(rollupObject: Object, parentEvent: Object): void { 87 this.prepareForCompilation(rollupObject, parentEvent); 88 SourceMapGenerator.getInstance().buildModuleSourceMapInfo(parentEvent); 89 this.generateAbcByEs2abc(parentEvent, !!this.projectConfig.byteCodeHarInfo); 90 } 91 92 private compileChangeListFiles(rollupObject: Object, parentEvent: Object): void { 93 if (!fs.existsSync(this.projectConfig.changedFileList)) { 94 this.logger.debug(blue, `ArkTS: Cannot find file: ${ 95 this.projectConfig.changedFileList}, skip cold reload build`, reset); 96 return; 97 } 98 99 const changedFileListJson: string = fs.readFileSync(this.projectConfig.changedFileList).toString(); 100 const changedFileList: Array<string> = JSON.parse(changedFileListJson).modifiedFiles; 101 if (typeof changedFileList === 'undefined' || changedFileList.length === 0) { 102 this.logger.debug(blue, `ArkTS: No changed files found, skip cold reload build`, reset); 103 return; 104 } 105 106 let needColdreloadBuild: boolean = true; 107 let changedFileListInAbsolutePath: Array<string> = changedFileList.map((file) => { 108 if (isJsonSourceFile(file)) { 109 this.logger.debug(blue, `ARKTS: json source file: ${file} changed, skip cold reload build!`, reset); 110 needColdreloadBuild = false; 111 } 112 return path.join(this.projectConfig.projectPath, file); 113 }); 114 115 if (!needColdreloadBuild) { 116 return; 117 } 118 119 const eventCollectModuleFileList = createAndStartEvent(parentEvent, 'collect module file list'); 120 this.collectModuleFileList(rollupObject, changedFileListInAbsolutePath[Symbol.iterator]()); 121 stopEvent(eventCollectModuleFileList); 122 123 if (!fs.existsSync(this.projectConfig.patchAbcPath)) { 124 mkdirsSync(this.projectConfig.patchAbcPath); 125 } 126 127 this.updateSourceMapFromFileList( 128 SourceMapGenerator.getInstance().isNewSourceMaps() ? changedFileListInAbsolutePath : changedFileList, 129 parentEvent); 130 const outputABCPath: string = path.join(this.projectConfig.patchAbcPath, MODULES_ABC); 131 validateFilePathLength(outputABCPath, this.logger); 132 this.moduleAbcPath = outputABCPath; 133 // During incremental compilation, the bytecode har path must be blocked from being written to filesInfo. 134 this.generateAbcByEs2abc(parentEvent, false); 135 } 136 137 private updateSourceMapFromFileList(fileList: Array<string>, parentEvent: Object): void { 138 const eventUpdateSourceMapFromFileList = createAndStartEvent(parentEvent, 'update source map from file list'); 139 const sourceMapGenerator = SourceMapGenerator.getInstance(); 140 const relativeProjectPath: string = this.projectConfig.projectPath.slice( 141 this.projectConfig.projectRootPath.length + path.sep.length); 142 let coldReloadSourceMap: Object = {}; 143 for (const file of fileList) { 144 if (sourceMapGenerator.isNewSourceMaps()) { 145 validateFilePathLength(file, this.logger); 146 coldReloadSourceMap[sourceMapGenerator.genKey(file)] = sourceMapGenerator.getSourceMap(file); 147 } else { 148 const sourceMapPath: string = toUnixPath(path.join(relativeProjectPath, file)); 149 validateFilePathLength(sourceMapPath, this.logger); 150 coldReloadSourceMap[sourceMapPath] = sourceMapGenerator.getSourceMap(sourceMapPath); 151 } 152 } 153 if (!sourceMapGenerator.isNewSourceMaps()) { 154 sourceMapGenerator.modifySourceMapKeyToCachePath(coldReloadSourceMap); 155 } 156 const sourceMapFilePath: string = path.join(this.projectConfig.patchAbcPath, SOURCEMAPS); 157 validateFilePathLength(sourceMapFilePath, this.logger); 158 fs.writeFileSync(sourceMapFilePath, 159 JSON.stringify(coldReloadSourceMap, null, 2), 'utf-8'); 160 stopEvent(eventUpdateSourceMapFromFileList); 161 } 162 163 private generateAbcByEs2abc(parentEvent: Object, includeByteCodeHarInfo: boolean): void { 164 this.generateEs2AbcCmd(); 165 this.addColdReloadArgs(); 166 this.genDescriptionsForMergedEs2abc(includeByteCodeHarInfo); 167 this.generateMergedAbcOfEs2Abc(parentEvent); 168 } 169} 170