• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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