1/* 2 * Copyright (c) 2025 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 * as path from 'path'; 17 18import { ABC_SUFFIX, ARKTSCONFIG_JSON_FILE, LANGUAGE_VERSION } from '../preDefine'; 19import { changeFileExtension } from '../utils'; 20import { BuildConfig, DependentModuleConfig, ModuleInfo, CompileFileInfo } from '../types'; 21import { ArkTSConfigGenerator } from './generateArkTSConfig'; 22 23export class BuildMode { 24 buildConfig: BuildConfig; 25 entryFiles: Set<string>; 26 compileFiles: Map<string, CompileFileInfo>; 27 cacheDir: string; 28 pandaSdkPath: string; 29 buildSdkPath: string; 30 packageName: string; 31 sourceRoots: string[]; 32 moduleRootPath: string; 33 moduleType: string; 34 dependentModuleList: DependentModuleConfig[]; 35 moduleInfos: Map<string, ModuleInfo>; 36 declgenV1OutPath: string | undefined; 37 declgenBridgeCodePath: string | undefined; 38 hasMainModule: boolean; 39 40 constructor(buildConfig: BuildConfig) { 41 this.buildConfig = buildConfig; 42 this.entryFiles = new Set<string>(buildConfig.compileFiles as string[]); 43 this.cacheDir = buildConfig.cachePath as string; 44 this.pandaSdkPath = buildConfig.pandaSdkPath as string; 45 this.buildSdkPath = buildConfig.buildSdkPath as string; 46 this.packageName = buildConfig.packageName as string; 47 this.sourceRoots = buildConfig.sourceRoots as string[]; 48 this.moduleRootPath = buildConfig.moduleRootPath as string; 49 this.moduleType = buildConfig.moduleType as string; 50 this.dependentModuleList = buildConfig.dependentModuleList; 51 this.hasMainModule = buildConfig.hasMainModule; 52 53 this.declgenV1OutPath = buildConfig.declgenV1OutPath as string | undefined; 54 this.declgenBridgeCodePath = buildConfig.declgenBridgeCodePath as string | undefined; 55 56 this.moduleInfos = new Map<string, ModuleInfo>(); 57 this.compileFiles = new Map<string, CompileFileInfo>(); 58 } 59 60 private getDependentModules(moduleInfo: ModuleInfo): Map<string, ModuleInfo>[] { 61 let dynamicDepModules: Map<string, ModuleInfo> = new Map<string, ModuleInfo>(); 62 let staticDepModules: Map<string, ModuleInfo> = new Map<string, ModuleInfo>(); 63 64 if (moduleInfo.isMainModule) { 65 this.moduleInfos.forEach((module: ModuleInfo, packageName: string) => { 66 if (module.isMainModule) { 67 return; 68 } 69 module.language === LANGUAGE_VERSION.ARKTS_1_2 70 ? staticDepModules.set(packageName, module) 71 : dynamicDepModules.set(packageName, module); 72 }); 73 return [dynamicDepModules, staticDepModules]; 74 } 75 76 if (moduleInfo.dependencies) { 77 moduleInfo.dependencies.forEach((packageName: string) => { 78 let depModuleInfo: ModuleInfo | undefined = this.moduleInfos.get(packageName); 79 if (!depModuleInfo) { 80 console.error(`Module ${packageName} not found in moduleInfos`); 81 } else { 82 depModuleInfo.language === LANGUAGE_VERSION.ARKTS_1_2 83 ? staticDepModules.set(packageName, depModuleInfo) 84 : dynamicDepModules.set(packageName, depModuleInfo); 85 } 86 }); 87 } 88 return [dynamicDepModules, staticDepModules]; 89 } 90 91 private generateArkTSConfigForModules(compileFileInfos: Record<string, string>): void { 92 let generator = ArkTSConfigGenerator.getGenerator(this.buildConfig, this.moduleInfos); 93 this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleRootPath: string) => { 94 for (const fileInfo of moduleInfo.compileFileInfos) { 95 compileFileInfos[fileInfo.filePath] = fileInfo.arktsConfigFile; 96 } 97 generator.writeArkTSConfigFile(moduleInfo); 98 }); 99 } 100 101 private collectDepModuleInfos(): void { 102 this.moduleInfos.forEach((moduleInfo) => { 103 let [dynamicDepModules, staticDepModules] = this.getDependentModules(moduleInfo); 104 moduleInfo.dynamicDepModuleInfos = dynamicDepModules; 105 moduleInfo.staticDepModuleInfos = staticDepModules; 106 }); 107 } 108 109 private collectModuleInfos(): void { 110 if (this.hasMainModule && (!this.packageName || !this.moduleRootPath || !this.sourceRoots)) { 111 console.error('Main module info from hvigor is not correct.'); 112 } 113 let mainModuleInfo: ModuleInfo = { 114 isMainModule: this.hasMainModule, 115 packageName: this.packageName, 116 moduleRootPath: this.moduleRootPath, 117 moduleType: this.moduleType, 118 sourceRoots: this.sourceRoots, 119 entryFile: '', 120 arktsConfigFile: path.resolve(this.cacheDir, this.packageName, ARKTSCONFIG_JSON_FILE), 121 dynamicDepModuleInfos: new Map<string, ModuleInfo>(), 122 staticDepModuleInfos: new Map<string, ModuleInfo>(), 123 compileFileInfos: [], 124 declgenV1OutPath: this.declgenV1OutPath, 125 declgenBridgeCodePath: this.declgenBridgeCodePath 126 }; 127 this.moduleInfos.set(this.packageName, mainModuleInfo); 128 this.dependentModuleList.forEach((module: DependentModuleConfig) => { 129 if (!module.packageName || !module.modulePath || !module.sourceRoots || !module.entryFile) { 130 console.error('Dependent module info from hvigor is not correct.'); 131 } 132 let moduleInfo: ModuleInfo = { 133 isMainModule: false, 134 packageName: module.packageName, 135 moduleRootPath: module.modulePath, 136 moduleType: module.moduleType, 137 sourceRoots: module.sourceRoots, 138 entryFile: module.entryFile, 139 arktsConfigFile: path.resolve(this.cacheDir, module.packageName, ARKTSCONFIG_JSON_FILE), 140 compileFileInfos: [], 141 dynamicDepModuleInfos: new Map<string, ModuleInfo>(), 142 staticDepModuleInfos: new Map<string, ModuleInfo>(), 143 declgenV1OutPath: undefined, 144 declgenBridgeCodePath: undefined, 145 language: module.language, 146 declFilesPath: module.declFilesPath, 147 dependencies: module.dependencies 148 }; 149 this.moduleInfos.set(module.packageName, moduleInfo); 150 }); 151 this.collectDepModuleInfos(); 152 } 153 154 private collectCompileFiles(): void { 155 this.entryFiles.forEach((file: string) => { 156 for (const [packageName, moduleInfo] of this.moduleInfos) { 157 if (!file.startsWith(moduleInfo.moduleRootPath)) { 158 continue; 159 } 160 let filePathFromModuleRoot: string = path.relative(moduleInfo.moduleRootPath, file); 161 let filePathInCache: string = path.join(this.cacheDir, moduleInfo.packageName, filePathFromModuleRoot); 162 let abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); 163 164 let fileInfo: CompileFileInfo = { 165 filePath: file, 166 dependentFiles: [], 167 abcFilePath: abcFilePath, 168 arktsConfigFile: moduleInfo.arktsConfigFile, 169 packageName: moduleInfo.packageName 170 }; 171 moduleInfo.compileFileInfos.push(fileInfo); 172 this.compileFiles.set(file, fileInfo); 173 return; 174 } 175 console.error('File does not belong to any module in moduleInfos.'); 176 }); 177 } 178 179 public generateModuleInfos(): void { 180 this.collectModuleInfos(); 181 this.collectCompileFiles(); 182 } 183 184 public async generateArkTSConfig(compileFileInfos: Record<string, string>): Promise<void> { 185 this.generateModuleInfos(); 186 this.generateArkTSConfigForModules(compileFileInfos); 187 } 188} 189