diff --git a/ets2panda/driver/build_system/src/build/base_mode.ts b/ets2panda/driver/build_system/src/build/base_mode.ts index eeb15985ea85f5a39b11127d44e3c6be469d4b31..be84136cd5e89316227a0476a4a5282661c630eb 100644 --- a/ets2panda/driver/build_system/src/build/base_mode.ts +++ b/ets2panda/driver/build_system/src/build/base_mode.ts @@ -17,1526 +17,1143 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; import * as child_process from 'child_process'; -import * as crypto from 'crypto'; -import { Worker as ThreadWorker } from 'worker_threads'; import { - ABC_SUFFIX, - ARKTSCONFIG_JSON_FILE, - DECL_ETS_SUFFIX, - DECL_TS_SUFFIX, - DEPENDENCY_INPUT_FILE, - DEPENDENCY_JSON_FILE, - LANGUAGE_VERSION, - LINKER_INPUT_FILE, - MERGED_ABC_FILE, - MERGED_INTERMEDIATE_FILE, - STATIC_RECORD_FILE, - STATIC_RECORD_FILE_CONTENT, - TS_SUFFIX + ABC_SUFFIX, + ARKTSCONFIG_JSON_FILE, + DECL_ETS_SUFFIX, + DECL_TS_SUFFIX, + LANGUAGE_VERSION, + LINKER_INPUT_FILE, + MERGED_ABC_FILE, + MERGED_CYCLE_FILE, + STATIC_RECORD_FILE, + STATIC_RECORD_FILE_CONTENT, + TS_SUFFIX } from '../pre_define'; import { - changeDeclgenFileExtension, - changeFileExtension, - createFileIfNotExists, - ensurePathExists, - getFileHash, - isMac, - isMixCompileProject, - serializeWithIgnore + changeDeclgenFileExtension, + changeFileExtension, + createFileIfNotExists, + ensurePathExists, + getFileHash, + isMac, + isMixCompileProject, + checkDependencyModuleInfoCorrectness, } from '../util/utils'; import { - PluginDriver, - PluginHook + PluginDriver, + PluginHook } from '../plugins/plugins_driver'; import { - Logger, - LogData, - LogDataFactory + Logger, + LogData, + LogDataFactory } from '../logger'; -import { ErrorCode } from '../error_code'; +import { DependencyAnalyzer } from '../dependency_analyzer'; +import { ErrorCode, DriverError } from '../util/error'; import { - ArkTS, - ArkTSGlobal, - BuildConfig, - BUILD_MODE, - OHOS_MODULE_TYPE, - CompileFileInfo, - DependencyFileConfig, - DependentModuleConfig, - JobInfo, - KPointer, - ModuleInfo, - ES2PANDA_MODE, - CompilePayload + BuildConfig, + BUILD_MODE, + OHOS_MODULE_TYPE, + CompileFileInfo, + DependencyModuleConfig, + KPointer, + ModuleInfo, + ES2PANDA_MODE, + CompileTask as ProcessCompileTask, + CompileJobInfo, + JobInfo } from '../types'; import { - ArkTSConfig, - ArkTSConfigGenerator + ArkTSConfigGenerator } from './generate_arktsconfig'; import { KitImportTransformer } from '../plugins/KitImportTransformer'; -import { - BS_PERF_FILE_NAME, - CompileSingleData, - RECORDE_COMPILE_NODE, - RECORDE_MODULE_NODE, - RECORDE_RUN_NODE +import { + BS_PERF_FILE_NAME, + CompileSingleData, + RECORDE_COMPILE_NODE, + RECORDE_MODULE_NODE, + RECORDE_RUN_NODE } from '../utils/record_time_mem'; -import { TaskManager } from '../util/TaskManager'; import { handleCompileWorkerExit, handleDeclgenWorkerExit } from '../util/worker_exit_handler'; -import { initKoalaModules } from '../init/init_koala_modules'; +// import { WorkerInfo, TaskManager, DriverProcess, DriverThread } from '../util/TaskManager'; export abstract class BaseMode { - public buildConfig: BuildConfig; - public entryFiles: Set; - public allFiles: Map; - public compileFiles: Map; - public outputDir: string; - public cacheDir: string; - public pandaSdkPath: string; - public buildSdkPath: string; - public packageName: string; - public sourceRoots: string[]; - public moduleRootPath: string; - public moduleType: string; - public dependentModuleList: DependentModuleConfig[]; - public moduleInfos: Map; - public mergedAbcFile: string; - public dependencyJsonFile: string; - public abcLinkerCmd: string[]; - public dependencyAnalyzerCmd: string[]; - public logger: Logger; - public isDebug: boolean; - public enableDeclgenEts2Ts: boolean; - public declgenV1OutPath: string | undefined; - public declgenV2OutPath: string | undefined; - public declgenBridgeCodePath: string | undefined; - public hasMainModule: boolean; - public abcFiles: Set; - public hashCacheFile: string; - public isCacheFileExists: boolean; - public hashCache: Record; - public dependencyFileMap: DependencyFileConfig | null; - public isBuildConfigModified: boolean | undefined; - public hasCleanWorker: boolean; - public byteCodeHar: boolean; - public es2pandaMode: number; - public skipDeclCheck: boolean; - - constructor(buildConfig: BuildConfig) { - this.buildConfig = buildConfig; - this.entryFiles = new Set(buildConfig.compileFiles as string[]); - this.allFiles = new Map(); - this.compileFiles = new Map(); - this.outputDir = buildConfig.loaderOutPath as string; - this.cacheDir = buildConfig.cachePath as string; - this.pandaSdkPath = buildConfig.pandaSdkPath as string; - this.buildSdkPath = buildConfig.buildSdkPath as string; - this.packageName = buildConfig.packageName as string; - this.sourceRoots = buildConfig.sourceRoots as string[]; - this.moduleRootPath = buildConfig.moduleRootPath as string; - this.moduleType = buildConfig.moduleType as string; - this.dependentModuleList = buildConfig.dependentModuleList; - this.moduleInfos = new Map(); - this.mergedAbcFile = path.resolve(this.outputDir, MERGED_ABC_FILE); - this.dependencyJsonFile = path.resolve(this.cacheDir, DEPENDENCY_JSON_FILE); - this.abcLinkerCmd = ['"' + this.buildConfig.abcLinkerPath + '"']; - this.dependencyAnalyzerCmd = ['"' + this.buildConfig.dependencyAnalyzerPath + '"']; - this.logger = Logger.getInstance(); - this.isDebug = buildConfig.buildMode as string === BUILD_MODE.DEBUG; - this.enableDeclgenEts2Ts = buildConfig.enableDeclgenEts2Ts as boolean; - this.declgenV1OutPath = buildConfig.declgenV1OutPath as string | undefined; - this.declgenV2OutPath = buildConfig.declgenV2OutPath as string | undefined; - this.declgenBridgeCodePath = buildConfig.declgenBridgeCodePath as string | undefined; - this.hasMainModule = buildConfig.hasMainModule; - this.abcFiles = new Set(); - this.hashCacheFile = path.join(this.cacheDir, 'hash_cache.json'); - this.isCacheFileExists = fs.existsSync(this.hashCacheFile); - this.hashCache = this.loadHashCache(); - this.dependencyFileMap = null; - this.isBuildConfigModified = buildConfig.isBuildConfigModified as boolean | undefined; - this.hasCleanWorker = false; - this.byteCodeHar = buildConfig.byteCodeHar as boolean; - this.es2pandaMode = buildConfig?.es2pandaMode ?? ( - isMixCompileProject(buildConfig) - ? ES2PANDA_MODE.RUN_PARALLEL - : ES2PANDA_MODE.RUN - ); - this.skipDeclCheck = buildConfig?.skipDeclCheck as boolean ?? true; - } - - public declgen(fileInfo: CompileFileInfo): void { - const source = fs.readFileSync(fileInfo.filePath, 'utf8'); - const moduleInfo: ModuleInfo = this.moduleInfos.get(fileInfo.packageName)!; - const filePathFromModuleRoot: string = path.relative(moduleInfo.moduleRootPath, fileInfo.filePath); - const declEtsOutputPath: string = changeDeclgenFileExtension( - path.join(moduleInfo.declgenV1OutPath as string, moduleInfo.packageName, filePathFromModuleRoot), - DECL_ETS_SUFFIX - ); - const etsOutputPath: string = changeDeclgenFileExtension( - path.join(moduleInfo.declgenBridgeCodePath as string, moduleInfo.packageName, filePathFromModuleRoot), - TS_SUFFIX - ); - ensurePathExists(declEtsOutputPath); - ensurePathExists(etsOutputPath); - const arktsGlobal: ArkTSGlobal = this.buildConfig.arktsGlobal; - const arkts: ArkTS = this.buildConfig.arkts; - let errorStatus = false; - try { - const staticRecordPath = path.join( - moduleInfo.declgenV1OutPath as string, - STATIC_RECORD_FILE - ) - const declEtsOutputDir = path.dirname(declEtsOutputPath); - const staticRecordRelativePath = changeFileExtension( - path.relative(declEtsOutputDir, staticRecordPath).replace(/\\/g, '\/'), - "", - DECL_TS_SUFFIX - ); - createFileIfNotExists(staticRecordPath, STATIC_RECORD_FILE_CONTENT); - - arktsGlobal.filePath = fileInfo.filePath; - arktsGlobal.config = arkts.Config.create([ - '_', - '--extension', - 'ets', - '--arktsconfig', - fileInfo.arktsConfigFile, - fileInfo.filePath - ]).peer; - arktsGlobal.compilerContext = arkts.Context.createFromStringWithHistory(source); - PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer, this.skipDeclCheck); - - let ast = arkts.EtsScript.fromContext(); - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer, this.skipDeclCheck); - - ast = arkts.EtsScript.fromContext(); - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); - PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); - - arkts.generateTsDeclarationsFromContext( - declEtsOutputPath, - etsOutputPath, - false, - false, - staticRecordRelativePath - ); // Generate 1.0 declaration files & 1.0 glue code - this.logger.printInfo('declaration files generated'); - } catch (error) { - errorStatus = true; - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_DECLGEN_FAIL, - 'Generate declaration files failed.', - error.message, - fileInfo.filePath - ); - this.logger.printError(logData); - } - } finally { - if (!errorStatus) { - // when error occur,wrapper will destroy context. - arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); - } - arkts.destroyConfig(arktsGlobal.config); + private buildConfig: BuildConfig; + public entryFiles: Set; + public fileToModule: Map; + public moduleInfos: Map; + public mergedAbcFile: string; + public abcLinkerCmd: string[]; + public logger: Logger; + public depAnalyzer: DependencyAnalyzer; + public abcFiles: Set; + public hashCacheFile: string; + public filesHashCache: Record; + public jobs: Record; + public jobQueue: JobInfo[]; + public completedJobQueue: CompileJobInfo[]; + // NOTE: should be Ets2panda Wrapper Module + // NOTE: to be refactored + public koalaModule: any; + + constructor(buildConfig: BuildConfig) { + this.buildConfig = buildConfig; + this.entryFiles = new Set(buildConfig.compileFiles); + this.fileToModule = new Map(); + this.moduleInfos = new Map(); + this.mergedAbcFile = path.resolve(this.outputDir, MERGED_ABC_FILE); + this.abcLinkerCmd = ['"' + this.buildConfig.abcLinkerPath + '"']; + this.logger = Logger.getInstance(); + this.depAnalyzer = new DependencyAnalyzer(this.buildConfig); + this.abcFiles = new Set(); + this.hashCacheFile = path.join(this.cacheDir, 'hash_cache.json'); + this.filesHashCache = this.loadHashCache(); + this.jobs = {}; + this.jobQueue = []; + this.completedJobQueue = []; } - } - - public compile(fileInfo: CompileFileInfo): void { - ensurePathExists(fileInfo.abcFilePath); - - const ets2pandaCmd: string[] = [ - '_', - '--extension', - 'ets', - '--arktsconfig', - fileInfo.arktsConfigFile, - '--output', - fileInfo.abcFilePath, - ]; - - if (this.isDebug) { - ets2pandaCmd.push('--debug-info'); - ets2pandaCmd.push('--opt-level=0'); + + public get skipDeclCheck() { + return this.buildConfig.skipDeclCheck ?? true; } - ets2pandaCmd.push(fileInfo.filePath); - this.logger.printInfo('ets2pandaCmd: ' + ets2pandaCmd.join(' ')); - - let { arkts, arktsGlobal } = initKoalaModules(this.buildConfig) - let errorStatus = false; - try { - arktsGlobal.filePath = fileInfo.filePath; - arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer; - const source = fs.readFileSync(fileInfo.filePath).toString(); - arktsGlobal.compilerContext = arkts.Context.createFromString(source); - PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); - this.logger.printInfo('es2panda proceedToState parsed'); - let ast = arkts.EtsScript.fromContext(); - if (this.buildConfig.aliasConfig && Object.keys(this.buildConfig.aliasConfig).length > 0) { - // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin - this.logger.printInfo('Transforming import statements with alias config'); - let transformAst = new KitImportTransformer( - arkts, - arktsGlobal.compilerContext.program, - this.buildConfig.buildSdkPath, - this.buildConfig.aliasConfig - ).transform(ast); - PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); - } else { - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); - } - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - this.logger.printInfo('plugin parsed finished'); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); - this.logger.printInfo('es2panda proceedToState checked'); - - if (this.hasMainModule && (this.byteCodeHar || this.moduleType === OHOS_MODULE_TYPE.SHARED)) { - let filePathFromModuleRoot: string = path.relative(this.moduleRootPath, fileInfo.filePath); - let declEtsOutputPath: string = changeFileExtension( - path.join(this.declgenV2OutPath as string, filePathFromModuleRoot), - DECL_ETS_SUFFIX - ); - ensurePathExists(declEtsOutputPath); - // Generate 1.2 declaration files(a temporary solution while binary import not pushed) - arkts.generateStaticDeclarationsFromContext(declEtsOutputPath); - } - - ast = arkts.EtsScript.fromContext(); - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); - PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); - this.logger.printInfo('plugin checked finished'); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer); - this.logger.printInfo('es2panda bin generated'); - } catch (error) { - errorStatus = true; - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, - 'Compile abc files failed.', - error.message, - fileInfo.filePath - ); - this.logger.printError(logData); - } - } finally { - if (!errorStatus) { - // when error occur,wrapper will destroy context. - arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); - } - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - arkts.destroyConfig(arktsGlobal.config); + public get es2pandaMode() { + return this.buildConfig.es2pandaMode } - } - - public compileMultiFiles(moduleInfo: ModuleInfo): void { - let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); - compileSingleData.record(RECORDE_COMPILE_NODE.PROCEED_PARSE); - - const intermediateFilePath = path.resolve(this.cacheDir, MERGED_INTERMEDIATE_FILE); - this.abcFiles.clear(); - this.abcFiles.add(intermediateFilePath); - - let ets2pandaCmd: string[] = [ - '_', - '--extension', - 'ets', - '--arktsconfig', - moduleInfo.arktsConfigFile, - '--output', - intermediateFilePath, - '--simultaneous' - ]; - ensurePathExists(intermediateFilePath); - if (this.isDebug) { - ets2pandaCmd.push('--debug-info'); - ets2pandaCmd.push('--opt-level=0'); + + public get entryFile() { + return this.buildConfig.entryFile; } - ets2pandaCmd.push(this.buildConfig.compileFiles[0]); - this.logger.printInfo('ets2pandaCmd: ' + ets2pandaCmd.join(' ')); - - let { arkts, arktsGlobal } = initKoalaModules(this.buildConfig); - let errorStatus = false; - try { - arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer; - //@ts-ignore - arktsGlobal.compilerContext = arkts.Context.createContextGenerateAbcForExternalSourceFiles(this.buildConfig.compileFiles);; - PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); - this.logger.printInfo('es2panda proceedToState parsed'); - compileSingleData.record(RECORDE_COMPILE_NODE.PLUGIN_PARSE, RECORDE_COMPILE_NODE.PROCEED_PARSE); - - let ast = arkts.EtsScript.fromContext(); - - if (this.buildConfig.aliasConfig && Object.keys(this.buildConfig.aliasConfig).length > 0) { - // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin - this.logger.printInfo('Transforming import statements with alias config'); - let transformAst = new KitImportTransformer( - arkts, - arktsGlobal.compilerContext.program, - this.buildConfig.buildSdkPath, - this.buildConfig.aliasConfig - ).transform(ast); - PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); - } else { - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); - } - - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - this.logger.printInfo('plugin parsed finished'); - compileSingleData.record(RECORDE_COMPILE_NODE.PROCEED_CHECK, RECORDE_COMPILE_NODE.PLUGIN_PARSE); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); - this.logger.printInfo('es2panda proceedToState checked'); - compileSingleData.record(RECORDE_COMPILE_NODE.PLUGIN_CHECK, RECORDE_COMPILE_NODE.PROCEED_CHECK); - - if (this.hasMainModule && (this.byteCodeHar || this.moduleType === OHOS_MODULE_TYPE.SHARED)) { - for (const sourceFilePath of this.buildConfig.compileFiles) { - const filePathFromModuleRoot: string = path.relative(this.moduleRootPath, sourceFilePath); - - const declEtsOutputPath: string = changeFileExtension( - path.join(this.declgenV2OutPath as string, filePathFromModuleRoot), - DECL_ETS_SUFFIX - ); - ensurePathExists(declEtsOutputPath); - arkts.generateStaticDeclarationsFromContext(declEtsOutputPath); - } - } - - ast = arkts.EtsScript.fromContext(); - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); - PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); - this.logger.printInfo('plugin checked finished'); - compileSingleData.record(RECORDE_COMPILE_NODE.BIN_GENERATE, RECORDE_COMPILE_NODE.PLUGIN_CHECK); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer); - this.logger.printInfo('es2panda bin generated'); - compileSingleData.record(RECORDE_COMPILE_NODE.CFG_DESTROY, RECORDE_COMPILE_NODE.BIN_GENERATE); - } catch (error) { - errorStatus = true; - throw error; - } finally { - if (!errorStatus) { - // when error occur,wrapper will destroy context. - arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); - } - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - arkts.destroyConfig(arktsGlobal.config); - compileSingleData.record(RECORDE_COMPILE_NODE.END, RECORDE_COMPILE_NODE.CFG_DESTROY); - compileSingleData.writeSumSingle(path.resolve()); + public get mainPackageName() { + return this.buildConfig.packageName; } - } - - public mergeAbcFiles(): void { - let linkerInputFile: string = path.join(this.cacheDir, LINKER_INPUT_FILE); - let linkerInputContent: string = ''; - this.abcFiles.forEach((abcFile: string) => { - linkerInputContent += abcFile + os.EOL; - }); - fs.writeFileSync(linkerInputFile, linkerInputContent); - this.abcLinkerCmd.push('--strip-unused'); - this.abcLinkerCmd.push('--output'); - this.abcLinkerCmd.push('"' + this.mergedAbcFile + '"'); - this.abcLinkerCmd.push('--'); - this.abcLinkerCmd.push('@' + '"' + linkerInputFile + '"'); - - let abcLinkerCmdStr: string = this.abcLinkerCmd.join(' '); - if (isMac()) { - const loadLibrary = 'DYLD_LIBRARY_PATH=' + '"' + process.env.DYLD_LIBRARY_PATH + '"'; - abcLinkerCmdStr = loadLibrary + ' ' + abcLinkerCmdStr; + + public get mainModuleRootPath() { + return this.buildConfig.moduleRootPath; } - this.logger.printInfo(abcLinkerCmdStr); - - ensurePathExists(this.mergedAbcFile); - try { - child_process.execSync(abcLinkerCmdStr).toString(); - } catch (error) { - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_LINK_ABC_FAIL, - 'Link abc files failed.', - error.message - ); - this.logger.printError(logData); - } + + public get mainModuleType() { + return this.buildConfig.moduleType; } - } - - private getDependentModules(moduleInfo: ModuleInfo): Map[] { - const dynamicDepModules: Map = new Map(); - const staticDepModules: Map = new Map(); - this.collectDependencyModules(moduleInfo.packageName, moduleInfo, dynamicDepModules, staticDepModules); - - if (moduleInfo.dependencies) { - moduleInfo.dependencies.forEach((packageName: string) => { - let depModuleInfo: ModuleInfo | undefined = this.moduleInfos.get(packageName); - if (!depModuleInfo) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND, - `Module ${packageName} not found in moduleInfos` - ); - this.logger.printErrorAndExit(logData); - } else { - this.collectDependencyModules(packageName, depModuleInfo, dynamicDepModules, staticDepModules); - } - }); + + public get outputDir() { + return this.buildConfig.loaderOutPath; } - return [dynamicDepModules, staticDepModules]; - } - - private collectDependencyModules( - packageName: string, - module: ModuleInfo, - dynamicDepModules: Map, - staticDepModules: Map - ): void { - if (module.language === LANGUAGE_VERSION.ARKTS_1_2) { - staticDepModules.set(packageName, module); - } else if (module.language === LANGUAGE_VERSION.ARKTS_1_1) { - dynamicDepModules.set(packageName, module); - } else if (module.language === LANGUAGE_VERSION.ARKTS_HYBRID) { - staticDepModules.set(packageName, module); - dynamicDepModules.set(packageName, module); + + public get cacheDir() { + return this.buildConfig.cachePath; } - } - - protected generateArkTSConfigForModules(): void { - let taskList: ModuleInfo[] = []; - this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleRootPath: string) => { - if (moduleInfo.dependenciesSet.size === 0) { - taskList.push(moduleInfo); - } - - ArkTSConfigGenerator.getInstance(this.buildConfig, this.moduleInfos) - .generateArkTSConfigFile(moduleInfo, this.enableDeclgenEts2Ts); - }); - - while (taskList.length > 0) { - const task = taskList.pop(); - const arktsConfig = ArkTSConfigGenerator.getInstance().getArktsConfigPackageName(task!!.packageName) - task?.dependencies?.forEach(dependecyModule => { - arktsConfig?.mergeArktsConfig( - ArkTSConfigGenerator.getInstance().getArktsConfigPackageName(dependecyModule) - ); - }); - fs.writeFileSync(task!!.arktsConfigFile, JSON.stringify(arktsConfig!!.getCompilerOptions(), null, 2)) - task?.dependentSet.forEach((dependentTask) => { - const dependentModule = this.moduleInfos.get(dependentTask); - dependentModule?.dependenciesSet.delete(task.packageName); - if (dependentModule?.dependenciesSet.size === 0) { - taskList.push(dependentModule); - } - }); + + public set cacheDir(dir: string) { + this.buildConfig.cachePath = dir; } - } - private collectDepModuleInfos(): void { - this.moduleInfos.forEach((moduleInfo: ModuleInfo) => { - let [dynamicDepModules, staticDepModules] = this.getDependentModules(moduleInfo); - moduleInfo.dynamicDepModuleInfos = dynamicDepModules; - moduleInfo.staticDepModuleInfos = staticDepModules; + public get dependencyModuleList() { + return this.buildConfig.dependencyModuleList; + } - [...dynamicDepModules.keys(), ...staticDepModules.keys()].forEach(depName => { - moduleInfo.dependenciesSet.add(depName); - }); - moduleInfo.dependenciesSet.delete(moduleInfo.packageName); - moduleInfo.dependencies?.forEach(moduleName => { - this.moduleInfos.get(moduleName)?.dependentSet.add(moduleInfo.packageName); - }); - }); - } - - protected collectModuleInfos(): void { - if (this.hasMainModule && (!this.packageName || !this.moduleRootPath || !this.sourceRoots)) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL, - 'Main module info from hvigor is not correct.' - ); - this.logger.printError(logData); + public get enableDeclgenEts2Ts() { + return this.buildConfig.enableDeclgenEts2Ts; } - const mainModuleInfo: ModuleInfo = this.getMainModuleInfo(); - this.moduleInfos.set(this.packageName, mainModuleInfo); - this.dependentModuleList.forEach((module: DependentModuleConfig) => { - if (!module.packageName || !module.modulePath || !module.sourceRoots || !module.entryFile) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_CORRECT_FAIL, - 'Dependent module info from hvigor is not correct.' - ); - this.logger.printError(logData); - } - if (this.moduleInfos.has(module.packageName)) { - return; - } - let moduleInfo: ModuleInfo = { - isMainModule: false, - packageName: module.packageName, - moduleRootPath: module.modulePath, - moduleType: module.moduleType, - sourceRoots: module.sourceRoots, - entryFile: module.entryFile, - arktsConfigFile: path.resolve(this.cacheDir, module.packageName, ARKTSCONFIG_JSON_FILE), - compileFileInfos: [], - dynamicDepModuleInfos: new Map(), - staticDepModuleInfos: new Map(), - declgenV1OutPath: module.declgenV1OutPath, - declgenV2OutPath: module.declgenV2OutPath, - declgenBridgeCodePath: module.declgenBridgeCodePath, - language: module.language, - declFilesPath: module.declFilesPath, - dependencies: module.dependencies, - byteCodeHar: module.byteCodeHar, - abcPath: module.abcPath, - dependenciesSet: new Set(module?.dependencies), - dependentSet: new Set(), - }; - this.moduleInfos.set(module.packageName, moduleInfo); - }); - this.collectDepModuleInfos(); - } - - protected getMainModuleInfo(): ModuleInfo { - const mainModuleInfo = this.dependentModuleList.find((module: DependentModuleConfig) => module.packageName === this.packageName); - return { - isMainModule: this.hasMainModule, - packageName: mainModuleInfo?.packageName ?? this.packageName, - moduleRootPath: mainModuleInfo?.modulePath ?? this.moduleRootPath, - moduleType: mainModuleInfo?.moduleType ?? this.moduleType, - sourceRoots: this.sourceRoots, - entryFile: '', - arktsConfigFile: path.resolve(this.cacheDir, this.packageName, ARKTSCONFIG_JSON_FILE), - dynamicDepModuleInfos: new Map(), - staticDepModuleInfos: new Map(), - compileFileInfos: [], - declgenV1OutPath: mainModuleInfo?.declgenV1OutPath ?? this.declgenV1OutPath, - declgenV2OutPath: mainModuleInfo?.declgenV2OutPath ?? this.declgenV2OutPath, - declgenBridgeCodePath: mainModuleInfo?.declgenBridgeCodePath ?? this.declgenBridgeCodePath, - byteCodeHar: this.byteCodeHar, - language: mainModuleInfo?.language ?? LANGUAGE_VERSION.ARKTS_1_2, - declFilesPath: mainModuleInfo?.declFilesPath, - dependentSet: new Set(), - dependenciesSet: new Set(mainModuleInfo?.dependencies), - dependencies: mainModuleInfo?.dependencies ?? [] - }; - } - - private loadHashCache(): Record { - try { - if (!fs.existsSync(this.hashCacheFile)) { - return {}; - } - - const cacheContent: string = fs.readFileSync(this.hashCacheFile, 'utf-8'); - const cacheData: Record = JSON.parse(cacheContent); - const filteredCache: Record = Object.fromEntries( - Object.entries(cacheData).filter(([file]) => this.entryFiles.has(file)) - ); - return filteredCache; - } catch (error) { - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_LOAD_HASH_CACHE_FAIL, - 'Failed to load hash cache.', - error.message - ); - this.logger.printError(logData); - } - return {}; + + public set enableDeclgenEts2Ts(enable: boolean) { + this.buildConfig.enableDeclgenEts2Ts = enable; } - } - - private saveHashCache(): void { - ensurePathExists(this.hashCacheFile); - fs.writeFileSync(this.hashCacheFile, JSON.stringify(this.hashCache, null, 2)); - } - - private isFileChanged(etsFilePath: string, abcFilePath: string): boolean { - if (fs.existsSync(abcFilePath)) { - const etsFileLastModified: number = fs.statSync(etsFilePath).mtimeMs; - const abcFileLastModified: number = fs.statSync(abcFilePath).mtimeMs; - if (etsFileLastModified < abcFileLastModified) { - const currentHash = getFileHash(etsFilePath); - const cachedHash = this.hashCache[etsFilePath]; - if (cachedHash && cachedHash === currentHash) { - return false; - } - } + + public get isBuildConfigModified(): boolean | undefined { + return this.buildConfig.isBuildConfigModified; } - return true; - } - - private collectDependentCompileFiles(): void { - if (!this.dependencyFileMap) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_Dependency_Analyze_FAIL, - 'Analyze files dependency failed.', - 'Dependency map not initialized.' - ); - this.logger.printError(logData); - return; + + public set isBuildConfigModified(modified: boolean) { + this.buildConfig.isBuildConfigModified = modified; } - const compileFiles = new Set(); - const processed = new Set(); - const queue: string[] = []; - - this.entryFiles.forEach((file: string) => { - // Skip the declaration files when compiling abc - if (file.endsWith(DECL_ETS_SUFFIX)) { - return; - } - let hasModule = false; - for (const [_, moduleInfo] of this.moduleInfos) { - if (!file.startsWith(moduleInfo.moduleRootPath)) { - continue; - } + public get byteCodeHar() { + return this.buildConfig.byteCodeHar; + } - hasModule = true; - const filePathFromModuleRoot = path.relative(moduleInfo.moduleRootPath, file); - const filePathInCache = path.join(this.cacheDir, moduleInfo.packageName, filePathFromModuleRoot); - const abcFilePath = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); - this.abcFiles.add(abcFilePath); - - const fileInfo: CompileFileInfo = { - filePath: file, - dependentFiles: this.dependencyFileMap?.dependants[file] || [], - abcFilePath, - arktsConfigFile: moduleInfo.arktsConfigFile, - packageName: moduleInfo.packageName - }; - this.allFiles.set(file, fileInfo); + public get mainSourceRoots() { + return this.buildConfig.sourceRoots; + } + + public get declgenV1OutPath(): string | undefined { + return this.buildConfig.declgenV1OutPath; + } - if (this.isBuildConfigModified || this.isFileChanged(file, abcFilePath)) { - compileFiles.add(file); - queue.push(file); + public get declgenV2OutPath(): string | undefined { + return this.buildConfig.declgenV2OutPath; + } + + public get declgenBridgeCodePath(): string | undefined { + return this.buildConfig.declgenBridgeCodePath; + } + + public get isDebug() { + return this.buildConfig.buildMode === BUILD_MODE.DEBUG; + } + + + + private formEts2pandaCmd(jobInfo: CompileJobInfo, simultaneous: boolean = false): string[] { + let { inputFilePath, outputFilePath, arktsConfigFile }: CompileFileInfo = jobInfo.compileFileInfo; + + const ets2pandaCmd: string[] = [ + '_', + '--extension', + 'ets', + '--arktsconfig', + arktsConfigFile + ] + + if (simultaneous) { + ets2pandaCmd.push('--simultaneous') } - this.hashCache[file] = getFileHash(file); - break; - } - if (!hasModule) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, - 'File does not belong to any module in moduleInfos.', - '', - file - ); - this.logger.printError(logData); - return; - } - }); - this.collectAbcFileFromByteCodeHar(); - - while (queue.length > 0) { - const currentFile = queue.shift()!; - processed.add(currentFile); - - (this.dependencyFileMap?.dependants[currentFile] || []).forEach(dependant => { - // For the 1.1 declaration file referenced in dependencies, if a path is detected as non-existent, it will be skipped. - const isFileExist = fs.existsSync(dependant); - if (!isFileExist) { - return; + + if (jobInfo.isAbcJob) { + ets2pandaCmd.push('--output') + ets2pandaCmd.push(outputFilePath) } - if (!compileFiles.has(dependant) && !processed.has(dependant)) { - queue.push(dependant); + + if (this.isDebug) { + ets2pandaCmd.push('--debug-info'); + ets2pandaCmd.push('--opt-level=0'); } - compileFiles.add(dependant); - }); + + ets2pandaCmd.push(inputFilePath) + return ets2pandaCmd } - compileFiles.forEach((file: string) => { - let hasModule = false; - for (const [_, moduleInfo] of this.moduleInfos) { - if (!file.startsWith(moduleInfo.moduleRootPath)) { - continue; + private compile(job: CompileJobInfo) { + this.logger.printInfo("compile START") + this.logger.printInfo(`job ${JSON.stringify(job, null, 1)}`) + + let { inputFilePath, outputFilePath }: CompileFileInfo = job.compileFileInfo; + ensurePathExists(inputFilePath); + const source = fs.readFileSync(inputFilePath, 'utf-8'); + + const ets2pandaCmd: string[] = this.formEts2pandaCmd(job) + this.logger.printInfo('ets2pandaCmd: ' + ets2pandaCmd.join(' ')); + + const { arkts, arktsGlobal } = this.koalaModule; + + try { + arktsGlobal.filePath = inputFilePath; + arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer; + arktsGlobal.compilerContext = arkts.Context.createFromString(source); + PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); + this.logger.printInfo('es2panda proceedToState parsed'); + let ast = arkts.EtsScript.fromContext(); + if (this.buildConfig.aliasConfig && Object.keys(this.buildConfig.aliasConfig).length > 0) { + // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin + this.logger.printInfo('Transforming import statements with alias config'); + let transformAst = new KitImportTransformer( + arkts, + arktsGlobal.compilerContext.program, + this.buildConfig.buildSdkPath, + this.buildConfig.aliasConfig + ).transform(ast); + PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); + } else { + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + } + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + this.logger.printInfo('plugin parsed finished'); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); + this.logger.printInfo('es2panda proceedToState checked'); + + // if ((this.byteCodeHar || this.mainModuleType === OHOS_MODULE_TYPE.SHARED) && (this.declgenV2OutPath)) { + if (!job.isAbcJob) { + ensurePathExists(outputFilePath); + + // Generate 1.2 declaration files(a temporary solution while binary import not pushed) + arkts.generateStaticDeclarationsFromContext(outputFilePath); + this.logger.printInfo("compile FINISH [DECL]") + } else { + ast = arkts.EtsScript.fromContext(); + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); + this.logger.printInfo('plugin checked finished'); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer); + this.logger.printInfo('es2panda bin generated'); + this.logger.printInfo("compile FINISH [ABC]") + } + } catch (error) { + if (error instanceof Error) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, + 'Compile abc files failed.', + error.message, + inputFilePath + ) + ); + } + } finally { + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + arkts.destroyConfig(arktsGlobal.config); } - hasModule = true; - const filePathFromModuleRoot = path.relative(moduleInfo.moduleRootPath, file); - const filePathInCache = path.join(this.cacheDir, moduleInfo.packageName, filePathFromModuleRoot); - const abcFilePath = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); - - const fileInfo: CompileFileInfo = { - filePath: file, - dependentFiles: this.dependencyFileMap?.dependants[file] || [], - abcFilePath, - arktsConfigFile: moduleInfo.arktsConfigFile, - packageName: moduleInfo.packageName - }; + } - moduleInfo.compileFileInfos.push(fileInfo); - this.compileFiles.set(file, fileInfo); - break; - } - if (!hasModule) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, - 'File does not belong to any module in moduleInfos.', - '', - file - ); - this.logger.printError(logData); - } - }); - } - - private shouldSkipFile(file: string, moduleInfo: ModuleInfo, filePathFromModuleRoot: string, abcFilePath: string): boolean { - const targetPath = this.enableDeclgenEts2Ts - ? changeFileExtension(path.join(moduleInfo.declgenBridgeCodePath as string, moduleInfo.packageName, filePathFromModuleRoot), TS_SUFFIX) - : abcFilePath; - return !this.isFileChanged(file, targetPath); - } - - protected collectCompileFiles(): void { - this.entryFiles.forEach((file: string) => { - for (const [packageName, moduleInfo] of this.moduleInfos) { - const relativePath = path.relative(moduleInfo.moduleRootPath, file); - if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) { - continue; + public async run(): Promise { + this.jobs = this.depAnalyzer.collectJobs(this.entryFiles, this.fileToModule, this.moduleInfos); + this.initCompileQueues(); + + debugger; + while (this.haveQueuedJobs()) { + let job: CompileJobInfo = this.consumeJob()! + if (job.fileList.length > 1) { + // Compile cycle simultaneous + this.logger.printInfo("Compiling cycle....") + this.logger.printInfo(`file list: \n${job.fileList.join('\n')}`) + this.compileSimultaneous(job) + } else { + this.compile(job) + } + this.dispatchNextJob(job) } - const filePathFromModuleRoot: string = path.relative(moduleInfo.moduleRootPath, file); - const filePathInCache: string = path.join(this.cacheDir, moduleInfo.packageName, filePathFromModuleRoot); - const abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); - this.abcFiles.add(abcFilePath); - this.hashCache[file] = getFileHash(file); - const fileInfo: CompileFileInfo = { - filePath: path.resolve(file), - dependentFiles: [], - abcFilePath: abcFilePath, - arktsConfigFile: moduleInfo.arktsConfigFile, - packageName: moduleInfo.packageName - }; - moduleInfo.compileFileInfos.push(fileInfo); - this.compileFiles.set(path.resolve(file), fileInfo); - return; - } - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, - 'File does not belong to any module in moduleInfos.', - '', - file - ); - this.logger.printError(logData); - }); - } - - protected collectAbcFileFromByteCodeHar(): void { - // the abc of the dependent bytecode har needs to be included When compiling hsp/hap - // but it's not required when compiling har - if (this.buildConfig.moduleType === OHOS_MODULE_TYPE.HAR) { - return; - } - for (const [packageName, moduleInfo] of this.moduleInfos) { - if (!(moduleInfo.moduleType === OHOS_MODULE_TYPE.HAR && moduleInfo.byteCodeHar)) { - continue; - } - if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_1_1) { - continue; - } - if (!moduleInfo.abcPath) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_ABC_FILE_MISSING_IN_BCHAR, - `abc file not found in bytecode har ${packageName}. ` - ); - this.logger.printError(logData); - continue; - } - if (!fs.existsSync(moduleInfo.abcPath)) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_ABC_FILE_NOT_EXIST_IN_BCHAR, - `${moduleInfo.abcPath} does not exist. ` - ); - this.logger.printErrorAndExit(logData); - } - this.abcFiles.add(moduleInfo.abcPath); - } - } - - protected generateModuleInfos(): void { - let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); - compileSingleData.record(RECORDE_MODULE_NODE.COLLECT_INFO); - this.collectModuleInfos(); - compileSingleData.record(RECORDE_MODULE_NODE.GEN_CONFIG, RECORDE_MODULE_NODE.COLLECT_INFO); - this.generateArkTSConfigForModules(); - compileSingleData.record(RECORDE_MODULE_NODE.CLT_FILES, RECORDE_MODULE_NODE.GEN_CONFIG); - this.collectCompileFiles(); - compileSingleData.record(RECORDE_MODULE_NODE.SAVE_CACHE, RECORDE_MODULE_NODE.CLT_FILES); - this.saveHashCache(); - compileSingleData.record(RECORDE_MODULE_NODE.END, RECORDE_MODULE_NODE.SAVE_CACHE); - compileSingleData.writeSumSingle(path.resolve()); - } - - public async generateDeclaration(): Promise { - this.generateModuleInfos(); - - const compilePromises: Promise[] = []; - this.compileFiles.forEach((fileInfo: CompileFileInfo, _: string) => { - compilePromises.push(new Promise((resolve) => { - this.declgen(fileInfo); - resolve(); - })); - }); - await Promise.all(compilePromises); - } - - public async run(): Promise { - let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); - compileSingleData.record(RECORDE_RUN_NODE.GEN_MODULE); - this.generateModuleInfos(); - - const compilePromises: Promise[] = []; - let moduleToFile = new Map(); - this.compileFiles.forEach((fileInfo: CompileFileInfo, file: string) => { - if (!moduleToFile.has(fileInfo.packageName)) { - moduleToFile.set(fileInfo.packageName, []); - } - moduleToFile.get(fileInfo.packageName)?.push(fileInfo.filePath); - }); - compileSingleData.record(RECORDE_RUN_NODE.COMPILE_FILES, RECORDE_RUN_NODE.GEN_MODULE); - try { - //@ts-ignore - this.compileMultiFiles(this.moduleInfos.get(this.packageName)); - compileSingleData.record(RECORDE_RUN_NODE.END, RECORDE_RUN_NODE.COMPILE_FILES); - } catch (error) { - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, - 'Compile abc files failed.', - error.message - ); - this.logger.printErrorAndExit(logData); - } + this.mergeAbcFiles() } - this.mergeAbcFiles(); - compileSingleData.writeSumSingle(path.resolve()); - } - - // -- runParallell code begins -- + public compileSimultaneous(job: CompileJobInfo): void { + let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); + compileSingleData.record(RECORDE_COMPILE_NODE.PROCEED_PARSE); + + const ets2pandaCmd: string[] = this.formEts2pandaCmd(job, true) + this.logger.printInfo('ets2pandaCmd: ' + ets2pandaCmd.join(' ')); + + let { arkts, arktsGlobal } = this.koalaModule; + try { + arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer; + arktsGlobal.compilerContext = arkts.Context.createContextGenerateAbcForExternalSourceFiles(job.fileList); + PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); + this.logger.printInfo('es2panda proceedToState parsed'); + compileSingleData.record(RECORDE_COMPILE_NODE.PLUGIN_PARSE, RECORDE_COMPILE_NODE.PROCEED_PARSE); + + let ast = arkts.EtsScript.fromContext(); + + if (this.buildConfig.aliasConfig && Object.keys(this.buildConfig.aliasConfig).length > 0) { + // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin + this.logger.printInfo('Transforming import statements with alias config'); + let transformAst = new KitImportTransformer( + arkts, + arktsGlobal.compilerContext.program, + this.buildConfig.buildSdkPath, + this.buildConfig.aliasConfig + ).transform(ast); + PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); + } else { + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + } - public generatedependencyFileMap(): void { - if (this.enableDeclgenEts2Ts) { - return; - } - const dependencyInputFile: string = path.join(this.cacheDir, DEPENDENCY_INPUT_FILE); - let dependencyInputContent: string = ''; - this.entryFiles.forEach((entryFile: string) => { - dependencyInputContent += entryFile + os.EOL; - }); - fs.writeFileSync(dependencyInputFile, dependencyInputContent); - - this.dependencyAnalyzerCmd.push('@' + '"' + dependencyInputFile + '"'); - for (const [_, module] of this.moduleInfos) { - if (module.isMainModule) { - this.dependencyAnalyzerCmd.push('--arktsconfig=' + '"' + module.arktsConfigFile + '"'); - break; - } - } - this.dependencyAnalyzerCmd.push('--output=' + '"' + this.dependencyJsonFile + '"'); - let dependencyAnalyzerCmdStr: string = this.dependencyAnalyzerCmd.join(' '); - if (isMac()) { - const loadLibrary = 'DYLD_LIBRARY_PATH=' + '"' + process.env.DYLD_LIBRARY_PATH + '"'; - dependencyAnalyzerCmdStr = loadLibrary + ' ' + dependencyAnalyzerCmdStr; + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + this.logger.printInfo('plugin parsed finished'); + compileSingleData.record(RECORDE_COMPILE_NODE.PROCEED_CHECK, RECORDE_COMPILE_NODE.PLUGIN_PARSE); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); + this.logger.printInfo('es2panda proceedToState checked'); + compileSingleData.record(RECORDE_COMPILE_NODE.PLUGIN_CHECK, RECORDE_COMPILE_NODE.PROCEED_CHECK); + + // if (this.byteCodeHar || this.moduleType === OHOS_MODULE_TYPE.SHARED) { + for (const file of job.fileList) { + const module = this.fileToModule.get(file)! + const declEtsOutputPath: string = changeFileExtension( + path.resolve(this.cacheDir, module.packageName, + path.relative(module.moduleRootPath, file) + ), + DECL_ETS_SUFFIX + ) + + // const declEtsOutputPath: string = changeFileExtension( + // path.join(this.declgenV2OutPath as string, filePathFromModuleRoot), + // DECL_ETS_SUFFIX + // ); + ensurePathExists(declEtsOutputPath); + + arkts.generateStaticDeclarationsFromContext(declEtsOutputPath); + } + // } + + ast = arkts.EtsScript.fromContext(); + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); + this.logger.printInfo('plugin checked finished'); + compileSingleData.record(RECORDE_COMPILE_NODE.BIN_GENERATE, RECORDE_COMPILE_NODE.PLUGIN_CHECK); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer); + this.logger.printInfo('es2panda bin generated'); + compileSingleData.record(RECORDE_COMPILE_NODE.CFG_DESTROY, RECORDE_COMPILE_NODE.BIN_GENERATE); + } catch (error) { + if (error instanceof Error) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, + 'Compile abc files failed.', + error.message, + job.compileFileInfo.inputFilePath + ) + ); + } + } finally { + arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + arkts.destroyConfig(arktsGlobal.config); + compileSingleData.record(RECORDE_COMPILE_NODE.END, RECORDE_COMPILE_NODE.CFG_DESTROY); + compileSingleData.writeSumSingle(path.resolve()); + } } - this.logger.printInfo(dependencyAnalyzerCmdStr); - ensurePathExists(this.dependencyJsonFile); - try { - const output = child_process.execSync(dependencyAnalyzerCmdStr, { - stdio: 'pipe', - encoding: 'utf-8' - }); - if (output.trim() !== '') { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_Dependency_Analyze_FAIL, - 'Analyze files dependency failed.', - output - ); - this.logger.printError(logData); - return; - } - const dependencyJsonContent = fs.readFileSync(this.dependencyJsonFile, 'utf-8'); - this.dependencyFileMap = JSON.parse(dependencyJsonContent); - } catch (error) { - if (error instanceof Error) { - const execError = error as child_process.ExecException; - let fullErrorMessage = execError.message; - if (execError.stderr) { - fullErrorMessage += `\nError output: ${execError.stderr}`; + private mergeAbcFiles(): void { + let linkerInputFile: string = path.join(this.cacheDir, LINKER_INPUT_FILE); + let linkerInputContent: string = ''; + // this.abcFiles.forEach((abcFile: string) => { + // linkerInputContent += abcFile + os.EOL; + // }); + this.completedJobQueue.forEach((job: CompileJobInfo) => { + if (job.compileFileInfo.outputFilePath.endsWith(ABC_SUFFIX)) { + linkerInputContent += job.compileFileInfo.outputFilePath + os.EOL; + } + }) + + fs.writeFileSync(linkerInputFile, linkerInputContent); + this.abcLinkerCmd.push('--strip-unused'); + this.abcLinkerCmd.push('--output'); + this.abcLinkerCmd.push('"' + this.mergedAbcFile + '"'); + this.abcLinkerCmd.push('--'); + this.abcLinkerCmd.push('@' + '"' + linkerInputFile + '"'); + + let abcLinkerCmdStr: string = this.abcLinkerCmd.join(' '); + if (isMac()) { + const loadLibrary = 'DYLD_LIBRARY_PATH=' + '"' + process.env.DYLD_LIBRARY_PATH + '"'; + abcLinkerCmdStr = loadLibrary + ' ' + abcLinkerCmdStr; } - if (execError.stdout) { - fullErrorMessage += `\nOutput: ${execError.stdout}`; + this.logger.printInfo(abcLinkerCmdStr); + + ensurePathExists(this.mergedAbcFile); + try { + child_process.execSync(abcLinkerCmdStr).toString(); + } catch (error) { + if (error instanceof Error) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_LINK_ABC_FAIL, + 'Link abc files failed.', + error.message + ) + ); + } } - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_Dependency_Analyze_FAIL, - 'Analyze files dependency failed.', - fullErrorMessage - ); - this.logger.printError(logData); - } } - } - public async runParallel(): Promise { - this.generateModuleInfos(); + private getDependencyModules(moduleInfo: ModuleInfo): Map[] { + const dynamicDependencyModules: Map = new Map(); + const staticDependencyModules: Map = new Map(); + + if (moduleInfo.dependencies) { + moduleInfo.dependencies.forEach((packageName: string) => { + let dependency: ModuleInfo | undefined = this.moduleInfos.get(packageName); + if (!dependency) { + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND, + `Module ${packageName} not found in moduleInfos` + ); + this.logger.printErrorAndExit(logData); + } else { + this.processDependencyModule(packageName, dependency, dynamicDependencyModules, staticDependencyModules); + } + }); + } + return [dynamicDependencyModules, staticDependencyModules]; + } - const taskManager = new TaskManager(path.resolve(__dirname, 'compile_worker.js'), handleCompileWorkerExit); + private processDependencyModule( + packageName: string, + module: ModuleInfo, + dynamicDependencyModules: Map, + staticDependencyModules: Map + ): void { + if (module.language === LANGUAGE_VERSION.ARKTS_1_2) { + staticDependencyModules.set(packageName, module); + } else if (module.language === LANGUAGE_VERSION.ARKTS_1_1) { + dynamicDependencyModules.set(packageName, module); + } else if (module.language === LANGUAGE_VERSION.ARKTS_HYBRID) { + staticDependencyModules.set(packageName, module); + dynamicDependencyModules.set(packageName, module); + } + } - try { - taskManager.startWorkers(); + protected generateArkTSConfigForModules(): void { + // Just to init the generator + ArkTSConfigGenerator.getInstance(this.buildConfig) - const taskPromises = Array.from(this.compileFiles.values()).map(task => - taskManager.submitTask({ - fileInfo: task, - buildConfig: this.getSerializableConfig() as BuildConfig, - moduleInfos: Array.from(this.moduleInfos.entries()) - }) - ); - - await Promise.all(taskPromises); - - this.mergeAbcFiles(); - } catch (error) { - this.logger.printError(LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, - 'Compile abc files failed.' - )); - } finally { - await taskManager.shutdown(); - } - } + const dependenciesSets = new Map>; - public async generateDeclarationParallell(): Promise { - this.generateModuleInfos(); + // Fill dependenciesSets + this.moduleInfos.forEach((moduleInfo: ModuleInfo) => { + dependenciesSets.set(moduleInfo.packageName, new Set()) + moduleInfo.dependencies?.forEach((dependency: string) => { + dependenciesSets.get(moduleInfo.packageName)!.add(this.moduleInfos.get(dependency)!) + }); - const taskManager = new TaskManager( - path.resolve(__dirname, 'declgen_worker.js'), - handleDeclgenWorkerExit - ); + }); - try { - taskManager.startWorkers(); + // Generate ArktsConfigs + dependenciesSets.forEach((dependencies: Set, module: string) => { + let moduleInfo = this.moduleInfos.get(module)! + let arktsConfig = ArkTSConfigGenerator.getInstance(). + generateArkTSConfigFile(moduleInfo, this.enableDeclgenEts2Ts); + dependencies.forEach((dependency: ModuleInfo) => { + arktsConfig.addPathMappings({ [dependency.packageName]: [dependency.moduleRootPath] }) + }); + fs.writeFileSync(moduleInfo.arktsConfigFile, JSON.stringify(arktsConfig.object, null, 2)) + }); + } - const taskPromises = Array.from(this.compileFiles.values()).map(task => - taskManager.submitTask({ - fileInfo: task, - buildConfig: this.getSerializableConfig() as BuildConfig, - moduleInfos: Array.from(this.moduleInfos.entries()) - }) - ); - - await Promise.allSettled(taskPromises); - - this.logger.printInfo('All declaration generation tasks complete.'); - } catch (error) { - this.logger.printError(LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_DECLGEN_FAIL, - `Generate declaration files failed.\n${(error as Error)?.message || error}` - )); - } finally { - await taskManager.shutdown(); + private collectModuleDependencies(): void { + this.moduleInfos.forEach((moduleInfo: ModuleInfo) => { + let [dynamicDepModules, staticDepModules] = this.getDependencyModules(moduleInfo); + moduleInfo.dynamicDependencyModules = dynamicDepModules; + moduleInfo.staticDependencyModules = staticDepModules; + }); } - } - private getSerializableConfig(): Object { - return serializeWithIgnore(this.buildConfig, ['arkts']); - } + protected collectModuleInfos(): void { + if (!this.mainPackageName || !this.mainModuleRootPath || !this.mainSourceRoots) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL, + 'Main module info is not correct.' + ) + ); + } - // -- runParallell code ends -- + const mainModuleInfo: ModuleInfo = this.getMainModuleInfo(); + this.moduleInfos.set(this.mainPackageName, mainModuleInfo); + + this.dependencyModuleList.forEach((dependency: DependencyModuleConfig) => { + if (!checkDependencyModuleInfoCorrectness(dependency)) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_CORRECT_FAIL, + 'Dependent module info is not correct.' + ) + ); + } + if (this.moduleInfos.has(dependency.packageName)) { + return; + } + let moduleInfo: ModuleInfo = { + isMainModule: false, + packageName: dependency.packageName, + moduleRootPath: dependency.modulePath, + moduleType: dependency.moduleType, + sourceRoots: dependency.sourceRoots, + entryFile: path.join(dependency.modulePath, dependency.entryFile), + arktsConfigFile: path.resolve(this.cacheDir, dependency.packageName, ARKTSCONFIG_JSON_FILE), + dynamicDependencyModules: new Map(), + staticDependencyModules: new Map(), + declgenV1OutPath: dependency.declgenV1OutPath, + declgenV2OutPath: dependency.declgenV2OutPath, + declgenBridgeCodePath: dependency.declgenBridgeCodePath, + language: dependency.language, + declFilesPath: dependency.declFilesPath, + dependencies: dependency.dependencies ?? [], + byteCodeHar: dependency.byteCodeHar, + abcPath: dependency.abcPath, + }; + this.moduleInfos.set(dependency.packageName, moduleInfo); + this.moduleInfos.get(this.mainPackageName)!.dependencies.push(dependency.packageName) + }); + this.collectModuleDependencies(); + } - // -- runConcurrent code begins -- + protected getMainModuleInfo(): ModuleInfo { + return { + isMainModule: true, + packageName: this.mainPackageName, + moduleRootPath: this.mainModuleRootPath, + moduleType: this.mainModuleType, + sourceRoots: this.mainSourceRoots, + // NOTE: this.entryFile is almost always undefined + // NOTE: to be refactored + entryFile: this.entryFile ?? '', + arktsConfigFile: path.resolve(this.cacheDir, this.mainPackageName, ARKTSCONFIG_JSON_FILE), + dynamicDependencyModules: new Map(), + staticDependencyModules: new Map(), + declgenV1OutPath: this.declgenV1OutPath, + declgenV2OutPath: this.declgenV2OutPath, + declgenBridgeCodePath: this.declgenBridgeCodePath, + byteCodeHar: this.byteCodeHar, + language: LANGUAGE_VERSION.ARKTS_1_2, + dependencies: [] + }; + } - private findStronglyConnectedComponents(graph: DependencyFileConfig): Map> { - const adjacencyList: Record = {}; - const reverseAdjacencyList: Record = {}; - const allNodes = new Set(); + private loadHashCache(): Record { + try { + if (!fs.existsSync(this.hashCacheFile)) { + return {}; + } - for (const node in graph.dependencies) { - allNodes.add(node); - graph.dependencies[node].forEach(dep => allNodes.add(dep)); + const cacheContent: string = fs.readFileSync(this.hashCacheFile, 'utf-8'); + const cacheData: Record = JSON.parse(cacheContent); + const filteredCache: Record = Object.fromEntries( + Object.entries(cacheData).filter(([file]) => this.entryFiles.has(file)) + ); + return filteredCache; + } catch (error) { + if (error instanceof Error) { + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_LOAD_HASH_CACHE_FAIL, + 'Failed to load hash cache.', + error.message + ); + this.logger.printError(logData); + } + return {}; + } } - for (const node in graph.dependants) { - allNodes.add(node); - graph.dependants[node].forEach(dep => allNodes.add(dep)); + + private saveHashCache(): void { + ensurePathExists(this.hashCacheFile); + fs.writeFileSync(this.hashCacheFile, JSON.stringify(this.filesHashCache, null, 2)); } - Array.from(allNodes).forEach(node => { - adjacencyList[node] = graph.dependencies[node] || []; - reverseAdjacencyList[node] = graph.dependants[node] || []; - }); + protected processEntryFiles(): void { + this.entryFiles.forEach((file: string) => { + for (const [_, moduleInfo] of this.moduleInfos) { + const relativePath = path.relative(moduleInfo.moduleRootPath, file); + if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) { + continue; + } + this.filesHashCache[file] = getFileHash(file); + this.fileToModule.set(path.resolve(file), moduleInfo); + return; + } + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, + 'File does not belong to any module in moduleInfos.', + '', + file + ) + ); + }); + this.logger.printInfo(`collected fileToModule ${JSON.stringify([...this.fileToModule.entries()], null, 1)}`) + } - const visited = new Set(); - const order: string[] = []; - function dfs(node: string): void { - visited.add(node); - for (const neighbor of adjacencyList[node]) { - if (!visited.has(neighbor)) { - dfs(neighbor); - } - } - order.push(node); + protected processBuildConfig(): void { + let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); + compileSingleData.record(RECORDE_MODULE_NODE.COLLECT_INFO); + this.collectModuleInfos(); + this.logger.printInfo(`ModuleInfos: ${JSON.stringify([...this.moduleInfos], null, 1)}`) + compileSingleData.record(RECORDE_MODULE_NODE.GEN_CONFIG, RECORDE_MODULE_NODE.COLLECT_INFO); + this.generateArkTSConfigForModules(); + compileSingleData.record(RECORDE_MODULE_NODE.CLT_FILES, RECORDE_MODULE_NODE.GEN_CONFIG); + this.processEntryFiles(); + compileSingleData.record(RECORDE_MODULE_NODE.SAVE_CACHE, RECORDE_MODULE_NODE.CLT_FILES); + this.saveHashCache(); + compileSingleData.record(RECORDE_MODULE_NODE.END, RECORDE_MODULE_NODE.SAVE_CACHE); + compileSingleData.writeSumSingle(path.resolve()); } - Array.from(allNodes).forEach(node => { - if (!visited.has(node)) { - dfs(node); - } - }); - - visited.clear(); - const components = new Map>(); - - function reverseDfs(node: string, component: Set): void { - visited.add(node); - component.add(node); - for (const neighbor of reverseAdjacencyList[node]) { - if (!visited.has(neighbor)) { - reverseDfs(neighbor, component); + protected backwardCompatibilityWorkaroundStub() { + const mainModule: ModuleInfo = this.moduleInfos.get(this.mainPackageName)! + // NOTE: small workaround + // NOTE: to be refactored + if (Object.keys(this.jobs).length == 0) { + const mainModuleFileList: string[] = [...this.fileToModule.entries()].filter(([_, module]: [string, ModuleInfo]) => { + return module.isMainModule + }).map(([file, _]: [string, ModuleInfo]) => { return file }) + mainModule.entryFile = mainModuleFileList[0] + } else { + mainModule.entryFile = Object.entries(this.jobs).filter(([_, job]: [string, JobInfo]) => { + return job.jobDependants.length == 0 + })[0][1].fileList[0] } - } + this.logger.printInfo(`mainModule entryFile: ${mainModule.entryFile}`) } - for (let i = order.length - 1; i >= 0; i--) { - const node = order[i]; - if (!visited.has(node)) { - const component = new Set(); - reverseDfs(node, component); - if (component.size > 1) { - const sortedFiles = Array.from(component).sort(); - const hashKey = createHash(sortedFiles.join('|')); - components.set(hashKey, component); + public async runSimultaneous(): Promise { + let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); + compileSingleData.record(RECORDE_RUN_NODE.GEN_MODULE); + + const mainModule: ModuleInfo = this.moduleInfos.get(this.mainPackageName)! + // NOTE: workaround (main module entry file) + // NOTE: to be refactored + let entryFile: string; + let module: ModuleInfo; + let arktsConfigFile: string; + if (!mainModule.entryFile) { + entryFile = [...this.entryFiles][0] + module = this.fileToModule.get(entryFile)! + } else { + entryFile = mainModule.entryFile + module = mainModule } + arktsConfigFile = module.arktsConfigFile + + const outputFile: string = changeFileExtension( + path.resolve(mainModule.moduleRootPath, entryFile), + ABC_SUFFIX + ) + + this.logger.printInfo(`entryFile: ${entryFile}`) + this.logger.printInfo(`module: ${JSON.stringify(module, null, 1)}`) + this.logger.printInfo(`arktsConfigFile: ${arktsConfigFile}`) + + // We do not need any queues, just form CompileJob for main module entry file + // Ets2panda will build it simultaneous + this.compileSimultaneous({ + id: outputFile, + isAbcJob: true, + fileList: [...this.entryFiles], + jobDependencies: [], + jobDependants: [], + compileFileInfo: { + inputFilePath: entryFile, + outputFilePath: outputFile, + arktsConfigFile: arktsConfigFile + } + }); + compileSingleData.record(RECORDE_RUN_NODE.END, RECORDE_RUN_NODE.COMPILE_FILES); + compileSingleData.writeSumSingle(path.resolve()); + } - } + public async runParallel(): Promise { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, + 'Parallel mode is currently unavailable.', + ) + ) + /* NOTE: to be refactored + this.jobs = this.depAnalyzer.collectJobs(this.entryFiles, this.fileToModule, this.moduleInfos); + this.initCompileQueues(); + + const taskManager = new TaskManager(handleCompileWorkerExit); + taskManager.startWorkers(DriverProcess, path.resolve(__dirname, 'compile_worker.js')); + + const taskPromises = this.jobQueue.map((job: JobInfo) => { + let compileJob: CompileJobInfo = this.prepareCompileJob(job) + taskManager.submitJob(job.id, + { + job: compileJob, + // We need to pass buildConfig as well in order to init plugins, koalaModule and Logger + // for the child proccess + buildConfig: this.buildConfig + }) + }); + + await Promise.all(taskPromises); + this.mergeAbcFiles(); + await taskManager.shutdownWorkers(); + */ } - return components; - } + private addJobToQueue(job: JobInfo): void { + if (this.jobQueue.some((queuedJob: JobInfo) => queuedJob.id === job.id)) { + return; + } + this.logger.printInfo(`Added Job ${JSON.stringify(job, null, 1)} to the queue`) + this.jobQueue.push(job); + } - private getJobDependencies(fileDeps: string[], cycleFiles: Map): Set { - let depJobList: Set = new Set(); - fileDeps.forEach((file) => { - if (!cycleFiles.has(file)) { - depJobList.add(this.getExternalProgramJobId(file)); - } else { - cycleFiles.get(file)?.forEach((f) => { - depJobList.add(f); - }); - } - }); - - return depJobList; - } - - private getAbcJobId(file: string): string { - return '1' + file; - } - - private getExternalProgramJobId(file: string): string { - return '0' + file; - } - - private getJobDependants(fileDeps: string[], cycleFiles: Map): Set { - let depJobList: Set = new Set(); - fileDeps.forEach((file) => { - if (!file.endsWith(DECL_ETS_SUFFIX)) { - depJobList.add(this.getAbcJobId(file)); - } - if (cycleFiles.has(file)) { - cycleFiles.get(file)?.forEach((f) => { - depJobList.add(f); + private initCompileQueues(): void { + Object.values(this.jobs).forEach((job: JobInfo) => { + if (job.jobDependencies.length === 0) { + this.addJobToQueue(job); + } }); - } else { - depJobList.add(this.getExternalProgramJobId(file)); - } - }); - - return depJobList; - } - - private collectCompileJobs(jobs: Record): void { - let fileDepsInfo: DependencyFileConfig = this.dependencyFileMap!; - Object.keys(fileDepsInfo.dependants).forEach((file) => { - if (!(file in fileDepsInfo.dependencies)) { - fileDepsInfo.dependencies[file] = []; - } - }); - - const cycleGroups = this.findStronglyConnectedComponents(fileDepsInfo); - let cycleFiles: Map = new Map(); - cycleGroups.forEach((value: Set, key: string) => { - value.forEach((file) => { - cycleFiles.set(file, [key]); - }); - }); - - Object.entries(fileDepsInfo.dependencies).forEach(([key, value]) => { - if (this.entryFiles.has(key) && !this.compileFiles.has(key)) { - return; - } - let dependencies = this.getJobDependencies(value, cycleFiles); - - if (!key.endsWith(DECL_ETS_SUFFIX)) { - let abcJobId: string = this.getAbcJobId(key); - jobs[abcJobId] = { - id: abcJobId, - isDeclFile: false, - isInCycle: cycleFiles.has(key), - isAbcJob: true, - fileList: [key], - dependencies: Array.from(dependencies), // 依赖external program - dependants: [] - }; - } + } - if (cycleFiles.has(key)) { - const externalProgramJobIds = cycleFiles.get(key)!; - externalProgramJobIds.forEach((id) => { - let fileList: string[] = Array.from(cycleGroups.get(id)!); - this.createExternalProgramJob(id, fileList, jobs, dependencies, true); + private dispatchNextJob(completedJob: CompileJobInfo) { + let completedJobId = completedJob.id; + completedJob.jobDependants.forEach((dependantJobId: string) => { + const depJob: JobInfo = this.jobs[dependantJobId]; + const depIndex = depJob.jobDependencies.indexOf(completedJobId); + if (depIndex !== -1) { + depJob.jobDependencies.splice(depIndex, 1); + this.logger.printInfo(`Removed Job ${completedJobId} from the queue`) + if (depJob.jobDependencies.length === 0) { + this.addJobToQueue(depJob); + } else { + this.logger.printInfo(`Job ${depJob.id} still have dependencies ${JSON.stringify(depJob.jobDependencies, null, 1)}`) + } + } }); - } else { - const id = this.getExternalProgramJobId(key); - let fileList: string[] = [key]; - this.createExternalProgramJob(id, fileList, jobs, dependencies); - } - - if (key.endsWith(DECL_ETS_SUFFIX)) { - let fileInfo: CompileFileInfo = { - filePath: key, - dependentFiles: [], - abcFilePath: '', - arktsConfigFile: this.moduleInfos.get(this.packageName)!.arktsConfigFile, - packageName: this.moduleInfos.get(this.packageName)!.packageName - }; + this.completedJobQueue.push(completedJob) + } - if (!this.allFiles.has(key)) { - this.allFiles.set(key, fileInfo); + private haveQueuedJobs(): boolean { + return (this.jobQueue.length > 0); + } + + private prepareCompileJob(job: JobInfo) { + const isCycle: boolean = job.fileList.length > 1 + const inputFile: string = job.fileList[0]! + const module: ModuleInfo = this.fileToModule.get(inputFile)! + const arktsConfigFile: string = module.arktsConfigFile + let outputFile: string + + if (isCycle) { + outputFile = path.resolve(this.cacheDir, "cycles", job.id, MERGED_CYCLE_FILE) + } else { + if (job.isAbcJob) { + outputFile = path.resolve(this.cacheDir, module.packageName, + changeFileExtension( + path.relative(module.moduleRootPath, inputFile), + ABC_SUFFIX + ) + ) + } else { + outputFile = path.resolve(this.cacheDir, module.packageName, + changeFileExtension( + path.relative(module.moduleRootPath, inputFile), + DECL_ETS_SUFFIX + ) + ) + } } - } - }); - - Object.entries(fileDepsInfo.dependants).forEach(([key, value]) => { - if (this.entryFiles.has(key) && !this.compileFiles.has(key)) { - return; - } - let dependants = this.getJobDependants(value, cycleFiles); - - this.dealWithDependants(cycleFiles, key, jobs, dependants); - }); - } - - private dealWithDependants(cycleFiles: Map, key: string, jobs: Record, dependants: Set): void { - if (cycleFiles.has(key)) { - const externalProgramJobIds = cycleFiles.get(key)!; - externalProgramJobIds.forEach((id) => { - jobs[id].dependants.forEach(dep => { - dependants.add(dep); - }); - if (dependants.has(id)) { - dependants.delete(id); + ensurePathExists(outputFile); + let res: CompileJobInfo = { + ...job, + compileFileInfo: { + inputFilePath: inputFile, + outputFilePath: outputFile, + arktsConfigFile: arktsConfigFile + } } - - jobs[id].dependants = Array.from(dependants); - }); - } else { - const id = this.getExternalProgramJobId(key); - jobs[id].dependants.forEach(dep => { - dependants.add(dep); - }); - if (dependants.has(id)) { - dependants.delete(id); - } - jobs[id].dependants = Array.from(dependants); + return res; } - } - private createExternalProgramJob(id: string, fileList: string[], jobs: Record, dependencies: Set, isInCycle?: boolean): void { - if (dependencies.has(id)) { - dependencies.delete(id); - } + private consumeJob(): CompileJobInfo | null { + if (this.jobQueue.length == 0) { + this.logger.printInfo("Job queue is empty!") + return null; + } - // TODO: can be duplicated ids - if (jobs[id]) { - // If job already exists, merge the file lists and dependencies - const existingJob = jobs[id]; - const mergedDependencies = new Set([ - ...existingJob.dependencies, - ...Array.from(dependencies) - ]); - jobs[id] = { - ...existingJob, - dependencies: Array.from(mergedDependencies) - }; - } else { - jobs[id] = { - id, - fileList, - isDeclFile: true, - isInCycle, - isAbcJob: false, - dependencies: Array.from(dependencies), // 依赖external program - dependants: [] - }; + let job: JobInfo = this.jobQueue.shift()! + return this.prepareCompileJob(job) } - } - private addJobToQueues(job: Job, queues: Queues): void { - if (queues.externalProgramQueue.some(j => j.id === job.id) || - queues.abcQueue.some(j => j.id === job.id)) { - return; + public async runConcurrent(): Promise { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, + 'Concurrent mode is currently unavailable.' + ) + ) + // NOTE: to be refactored + // this.jobs = this.depAnalyzer.collectJobs(this.entryFiles, this.fileToModule, this.moduleInfos); + // this.initCompileQueues(); + + // const processingJobs = new Set(); + // const workers: DriverThread[] = []; + // await this.invokeWorkers(processingJobs, workers); } - if (!job.isAbcJob) { - queues.externalProgramQueue.push(job); - } else { - queues.abcQueue.push(job); - } - } - - private initCompileQueues(jobs: Record, queues: Queues): void { - this.collectCompileJobs(jobs); - Object.values(jobs).forEach(job => { - if (job.dependencies.length === 0) { - this.addJobToQueues(job, queues); - } - }); - } - - private checkAllTasksDone(queues: Queues, workerPool: WorkerInfo[]): boolean { - if (queues.externalProgramQueue.length === 0) { - for (let i = 0; i < workerPool.length; i++) { - if (!workerPool[i].isIdle) { - return false; + public async generateDeclaration(): Promise { + this.jobs = this.depAnalyzer.collectJobs(this.entryFiles, this.fileToModule, this.moduleInfos); + this.initCompileQueues(); + + debugger; + while (this.haveQueuedJobs()) { + let job: CompileJobInfo = this.consumeJob()! + if (!job.isAbcJob) { + this.declgen(job) + } + this.dispatchNextJob(job) } - } - return true; } - return false; - } - private processAfterCompile(config: KPointer, globalContext: KPointer): void { + private declgen(jobInfo: CompileJobInfo): void { + const inputFilePath = jobInfo.compileFileInfo.inputFilePath; + const source = fs.readFileSync(inputFilePath, 'utf8'); + const moduleInfo: ModuleInfo = this.fileToModule.get(inputFilePath)!; + const filePathFromModuleRoot: string = path.relative(moduleInfo.moduleRootPath, inputFilePath); + const declEtsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenV1OutPath as string, moduleInfo.packageName, filePathFromModuleRoot), + DECL_ETS_SUFFIX + ); + const etsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenBridgeCodePath as string, moduleInfo.packageName, filePathFromModuleRoot), + TS_SUFFIX + ); + ensurePathExists(declEtsOutputPath); + ensurePathExists(etsOutputPath); + + let { arkts, arktsGlobal } = this.koalaModule; + + try { + const staticRecordPath = path.join( + moduleInfo.declgenV1OutPath as string, + STATIC_RECORD_FILE + ) + const declEtsOutputDir = path.dirname(declEtsOutputPath); + const staticRecordRelativePath = changeFileExtension( + path.relative(declEtsOutputDir, staticRecordPath).replace(/\\/g, '\/'), + "", + DECL_TS_SUFFIX + ); + createFileIfNotExists(staticRecordPath, STATIC_RECORD_FILE_CONTENT); + + let ets2pandaCmd = [ + '_', + '--extension', + 'ets', + '--arktsconfig', + jobInfo.compileFileInfo.arktsConfigFile, + inputFilePath + ]; + this.logger.printInfo(`ets2panda cmd: ${ets2pandaCmd}`) + + arktsGlobal.filePath = inputFilePath; + arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer; + arktsGlobal.compilerContext = arkts.Context.createFromStringWithHistory(source); + PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer, this.skipDeclCheck); + + let ast = arkts.EtsScript.fromContext(); + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer, this.skipDeclCheck); + + ast = arkts.EtsScript.fromContext(); + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); + + arkts.generateTsDeclarationsFromContext( + declEtsOutputPath, + etsOutputPath, + false, + false, + staticRecordRelativePath + ); // Generate 1.0 declaration files & 1.0 glue code + this.logger.printInfo('declaration files generated'); + } catch (error) { + if (error instanceof Error) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DECLGEN_FAIL, + 'Generate declaration files failed.', + error.message, + inputFilePath + ) + ); + } + } finally { + arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + arkts.destroyConfig(arktsGlobal.config); + } + } - if (this.hasCleanWorker) { - return; + // NOTE: to be refactored + /* + protected collectAbcFileFromByteCodeHar(): void { + // the abc of the dependent bytecode har needs to be included When compiling hsp/hap + // but it's not required when compiling har + if (this.buildConfig.moduleType === OHOS_MODULE_TYPE.HAR) { + return; + } + for (const [packageName, moduleInfo] of this.moduleInfos) { + if (!(moduleInfo.moduleType === OHOS_MODULE_TYPE.HAR && moduleInfo.byteCodeHar)) { + continue; + } + if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_1_1) { + continue; + } + if (!moduleInfo.abcPath) { + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_ABC_FILE_MISSING_IN_BCHAR, + `abc file not found in bytecode har ${packageName}. ` + ); + this.logger.printError(logData); + continue; + } + if (!fs.existsSync(moduleInfo.abcPath)) { + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_ABC_FILE_NOT_EXIST_IN_BCHAR, + `${moduleInfo.abcPath} does not exist. ` + ); + this.logger.printErrorAndExit(logData); + } + this.abcFiles.add(moduleInfo.abcPath); + } } - this.hasCleanWorker = true; - let arktsGlobal = this.buildConfig.arktsGlobal; - let arkts = this.buildConfig.arkts; - arktsGlobal.es2panda._DestroyGlobalContext(globalContext); - arkts.destroyConfig(config); - arktsGlobal.es2panda._MemFinalize(); + public async generateDeclarationParallell(): Promise { + this.processBuildConfig(); - this.mergeAbcFiles(); - } + const taskManager = new TaskManager(handleCompileWorkerExit); - // CC-OFFNXT(huge_depth) - private async invokeWorkers(jobs: Record, queues: Queues, processingJobs: Set, workers: ThreadWorker[]): Promise { - return new Promise((resolve) => { - const numWorkers = 1; + try { + taskManager.startWorkers(DriverProcess, path.resolve(__dirname, 'declgen_worker.js')); - let files: string[] = []; + const taskPromises = Array.from(this.fileToModule.values()).map(task => + taskManager.submitTask({ + fileInfo: task, + buildConfig: this.getSerializableConfig() as BuildConfig, + moduleInfos: Array.from(this.moduleInfos.entries()) + }) + ); - Object.entries(jobs).forEach(([key, job]) => { - for (let i = 0; i < job.fileList.length; i++) { - files.push(job.fileList[i]); - } - }); + await Promise.all(taskPromises); - let arkts = this.buildConfig.arkts; - let fileInfo = this.compileFiles.values().next().value!; - - let ets2pandaCmd: string[] = [ - '_', - '--extension', - 'ets', - '--arktsconfig', - fileInfo.arktsConfigFile, - '--output', - fileInfo.abcFilePath, - ]; - - if (this.isDebug) { - ets2pandaCmd.push('--debug-info'); - ets2pandaCmd.push('--opt-level=0'); - } - ets2pandaCmd.push(fileInfo.filePath); - - arkts.MemInitialize(); - - let config = arkts.Config.create(ets2pandaCmd).peer; - - let globalContextPtr = arkts.CreateGlobalContext(config, files, files.length, false); - const serializableConfig = this.getSerializableConfig(); - - const workerPool: WorkerInfo[] = []; - for (let i = 0; i < numWorkers; i++) { - const worker = new ThreadWorker( - path.resolve(__dirname, 'compile_thread_worker.js'), - { workerData: { workerId: i } } - ); + this.logger.printInfo('All declaration generation tasks complete.'); + } catch (error) { + this.logger.printError(LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DECLGEN_FAIL, + `Generate declaration files failed.\n${(error as Error)?.message || error}` + )); + } finally { + await taskManager.shutdown(); + } + } - workers.push(worker); - workerPool.push({ worker, isIdle: true }); - this.assignTaskToIdleWorker(workerPool[i], queues, processingJobs, serializableConfig, globalContextPtr); - worker.on('message', (msg) => { - if (msg.type === 'TASK_FINISH') { - const workerInfo = workerPool.find(w => w.worker === worker); - if (workerInfo) { - workerInfo.isIdle = true; - } - const jobId = msg.jobId; - finishedJob.push(jobId); - processingJobs.delete(jobId); - const completedJob = jobs[jobId]; - completedJob.dependants.forEach(depJobId => { - const depJob = jobs[depJobId]; - if (!depJob) { - return; - } - const depIndex = depJob.dependencies.indexOf(jobId); - if (depIndex !== -1) { - depJob.dependencies.splice(depIndex, 1); - if (depJob.dependencies.length === 0) { - this.addJobToQueues(depJob, queues); + private isFileChanged(etsFilePath: string, abcFilePath: string): boolean { + if (fs.existsSync(abcFilePath)) { + const etsFileLastModified: number = fs.statSync(etsFilePath).mtimeMs; + const abcFileLastModified: number = fs.statSync(abcFilePath).mtimeMs; + if (etsFileLastModified < abcFileLastModified) { + const currentHash = getFileHash(etsFilePath); + const cachedHash = this.filesHashCache[etsFilePath]; + if (cachedHash && cachedHash === currentHash) { + return false; } - } - }); - for (let j = 0; j < workerPool.length; j++) { - if (workerPool[j].isIdle) { - this.assignTaskToIdleWorker(workerPool[j], queues, processingJobs, serializableConfig, globalContextPtr); - } } - } - if (this.checkAllTasksDone(queues, workerPool)) { - workers.forEach(worker => worker.postMessage({ type: 'EXIT' })); - this.processAfterCompile(config, globalContextPtr); - resolve(); - } - }); - } - }); - } - - private updateDependantJobs(jobId: string, processingJobs: Set, jobs: Record, queues: Queues): void { - finishedJob.push(jobId); - processingJobs.delete(jobId); - const completedJob = jobs[jobId]; - completedJob.dependants.forEach(depJobId => { - const depJob = jobs[depJobId]; - // During incremental compilation, the dependants task does not necessarily exist - if (!depJob) { - return; - } - const depIndex = depJob.dependencies.indexOf(jobId); - if (depIndex !== -1) { - depJob.dependencies.splice(depIndex, 1); - if (depJob.dependencies.length === 0) { - this.addJobToQueues(depJob, queues); } - } - }); - } - - private assignTaskToIdleWorker( - workerInfo: WorkerInfo, - queues: Queues, - processingJobs: Set, - serializableConfig: Object, - globalContextPtr: KPointer): void { - let job: Job | undefined; - let jobInfo: JobInfo | undefined; - - if (queues.externalProgramQueue.length > 0) { - job = queues.externalProgramQueue.shift()!; - jobInfo = { - id: job.id, - isCompileAbc: false, - compileFileInfo: this.allFiles.get(job.fileList[0])!, - buildConfig: serializableConfig, - globalContextPtr: globalContextPtr - }; + return true; } - else if (queues.abcQueue.length > 0) { - job = queues.abcQueue.shift()!; - jobInfo = { - id: job.id, - isCompileAbc: true, - compileFileInfo: this.allFiles.get(job.fileList[0])!, - buildConfig: serializableConfig, - globalContextPtr: globalContextPtr - }; + + private assignTaskToIdleWorker(workerInfo: WorkerInfo, processingJobs: Set, serializableConfig: Object, globalContextPtr: KPointer): void { + + let jobInfo = this.consumeJob(); + if (!jobInfo) { + return; + } + + processingJobs.add(jobInfo.id); + workerInfo.worker.send('ASSIGN_TASK', jobInfo); + workerInfo.isIdle = false; } - if (job) { - processingJobs.add(job.id); - workerInfo.worker.postMessage({ type: 'ASSIGN_TASK', jobInfo }); - workerInfo.isIdle = false; + private checkAllTasksDone(workerPool: WorkerInfo[]): boolean { + if (this.jobQueue.length === 0) { + for (let i = 0; i < workerPool.length; i++) { + if (!workerPool[i].isIdle) { + return false; + } + } + return true; + } + return false; } - } - public async runConcurrent(): Promise { - this.generateModuleInfos(); - if (this.compileFiles.size === 0) { - return; + private processAfterCompile(config: KPointer, globalContext: KPointer): void { + + let arktsGlobal = this.koalaModule.arktsGlobal; + let arkts = this.koalaModule.arkts; + + arktsGlobal.es2panda._DestroyGlobalContext(globalContext); + arkts.destroyConfig(config); + arktsGlobal.es2panda._MemFinalize(); + + this.mergeAbcFiles(); } - this.generateArkTSConfigForModules(); - - const jobs: Record = {}; - const queues: Queues = { - externalProgramQueue: [], - abcQueue: [], - }; - this.initCompileQueues(jobs, queues); - - const processingJobs = new Set(); - const workers: ThreadWorker[] = []; - await this.invokeWorkers(jobs, queues, processingJobs, workers); - } -} -interface WorkerInfo { - worker: ThreadWorker; - isIdle: boolean; -} + CC-OFFNXT(huge_depth) + private async invokeWorkers(processingJobs: Set, workers: DriverThread[]): Promise { + return new Promise((resolve) => { + const numWorkers = 1; -interface Job { - id: string; - isDeclFile: boolean; - isInCycle?: boolean; - fileList: string[]; - dependencies: string[]; - dependants: string[]; - isAbcJob: boolean; -} + let files: string[] = []; -interface Queues { - externalProgramQueue: Job[]; - abcQueue: Job[]; -} + Object.entries(this.jobs).forEach(([key, job]) => { + for (let i = 0; i < job.fileList.length; i++) { + files.push(job.fileList[i]); + } + }); -function createHash(str: string): string { - const hash = crypto.createHash('sha256'); - hash.update(str); - return hash.digest('hex'); -} + let arkts = this.buildConfig.arkts; + let fileInfo = this.fileToModule.values().next().value!; + + let ets2pandaCmd: string[] = [ + '_', + '--extension', + 'ets', + '--arktsconfig', + fileInfo.arktsConfigFile, + '--output', + fileInfo.inputFilePath, + ]; + + if (this.isDebug) { + ets2pandaCmd.push('--debug-info'); + ets2pandaCmd.push('--opt-level=0'); + } + ets2pandaCmd.push(fileInfo.inputFilePath); + + arkts.MemInitialize(); -// -- runConcurrent code ends -- + let config = arkts.Config.create(ets2pandaCmd).peer; -let finishedJob: string[] = []; + let globalContextPtr = arkts.CreateGlobalContext(config, files, files.length, false); + const serializableConfig = this.getSerializableConfig(); + + const workerPool: WorkerInfo[] = []; + for (let i = 0; i < numWorkers; i++) { + const worker = new DriverThread( + path.resolve(__dirname, 'compile_thread_worker.js'), + { workerData: { workerId: i } } + ); + + workers.push(worker); + workerPool.push({ worker, id: i, isIdle: true, isKilled: false }); + this.assignTaskToIdleWorker(workerPool[i], processingJobs, serializableConfig, globalContextPtr); + worker.on('message', (msg) => { + if (msg.type === 'TASK_FINISH') { + const workerInfo = workerPool.find(w => w.worker === worker); + if (workerInfo) { + workerInfo.isIdle = true; + } + const jobId = msg.jobId; + finishedJob.push(jobId); + processingJobs.delete(jobId); + const completedJob = this.jobs[jobId]; + this.completeJob(completedJob); + for (let j = 0; j < workerPool.length; j++) { + if (workerPool[j].isIdle) { + this.assignTaskToIdleWorker(workerPool[j], processingJobs, serializableConfig, globalContextPtr); + } + } + } + if (this.checkAllTasksDone(workerPool)) { + workers.forEach(worker => worker.send('EXIT')); + this.processAfterCompile(config, globalContextPtr); + resolve(); + } + }); + } + }); + } + */ +} diff --git a/ets2panda/driver/build_system/src/build/build_framework_mode.ts b/ets2panda/driver/build_system/src/build/build_framework_mode.ts index ca40ca6642472fea7782e8bce07147a3032789f2..84fb410117a1b1e5ea7e8e2205da75e68e8c768c 100644 --- a/ets2panda/driver/build_system/src/build/build_framework_mode.ts +++ b/ets2panda/driver/build_system/src/build/build_framework_mode.ts @@ -19,64 +19,56 @@ import { LogData, LogDataFactory } from '../logger'; import { changeFileExtension } from '../util/utils'; import { ABC_SUFFIX } from '../pre_define'; import path from 'path'; -import { ErrorCode } from '../error_code'; +import { ErrorCode } from '../util/error'; export class BuildFrameworkMode extends BaseMode { - frameworkMode: boolean; - useEmptyPackage: boolean; + frameworkMode: boolean; + useEmptyPackage: boolean; - constructor(buildConfig: BuildConfig) { - super(buildConfig); - this.mergedAbcFile = buildConfig.loaderOutPath as string; - this.frameworkMode = buildConfig.frameworkMode ?? false; - this.useEmptyPackage = buildConfig.useEmptyPackage ?? false; - } + constructor(buildConfig: BuildConfig) { + super(buildConfig); + this.mergedAbcFile = buildConfig.loaderOutPath as string; + this.frameworkMode = buildConfig.frameworkMode ?? false; + this.useEmptyPackage = buildConfig.useEmptyPackage ?? false; + } - public async run(): Promise { - super.run(); - } + public async runSimultaneous(): Promise { + // super.runSimultaneous(); + } - protected generateModuleInfos(): void { - this.collectModuleInfos(); - this.generateArkTSConfigForModules(); - this.collectCompileFiles(); - } + protected parseBuildConfig(): void { + this.collectModuleInfos(); + this.generateArkTSConfigForModules(); + this.processEntryFiles(); + } - protected collectCompileFiles(): void { - this.entryFiles.forEach((file: string) => { - for (const [packageName, moduleInfo] of this.moduleInfos) { - if (!file.startsWith(moduleInfo.moduleRootPath)) { - continue; - } - let filePathFromModuleRoot: string = path.relative(moduleInfo.moduleRootPath, file); - let filePathInCache: string = path.join(this.cacheDir, moduleInfo.packageName, filePathFromModuleRoot); - let abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); - this.abcFiles.add(abcFilePath); - let fileInfo: CompileFileInfo = { - filePath: file, - dependentFiles: [], - abcFilePath: abcFilePath, - arktsConfigFile: moduleInfo.arktsConfigFile, - packageName: moduleInfo.packageName - }; - moduleInfo.compileFileInfos.push(fileInfo); - this.compileFiles.set(file, fileInfo); - return; - } - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, - 'File does not belong to any module in moduleInfos.', - '', - file - ); - this.logger.printError(logData); - }); - } + protected processEntryFiles(): void { + this.entryFiles.forEach((file: string) => { + for (const [_, moduleInfo] of this.moduleInfos) { + if (!file.startsWith(moduleInfo.moduleRootPath)) { + continue; + } + let filePathFromModuleRoot: string = path.relative(moduleInfo.moduleRootPath, file); + let filePathInCache: string = path.join(this.cacheDir, moduleInfo.packageName, filePathFromModuleRoot); + let abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); + this.abcFiles.add(abcFilePath); + this.fileToModule.set(file, moduleInfo); + return; + } + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, + 'File does not belong to any module in moduleInfos.', + '', + file + ); + this.logger.printError(logData); + }); + } - protected getMainModuleInfo(): ModuleInfo { - let moduleInfo = super.getMainModuleInfo(); - moduleInfo.frameworkMode = this.frameworkMode; - moduleInfo.useEmptyPackage = this.useEmptyPackage; - return moduleInfo; - } + protected getMainModuleInfo(): ModuleInfo { + let moduleInfo = super.getMainModuleInfo(); + moduleInfo.frameworkMode = this.frameworkMode; + moduleInfo.useEmptyPackage = this.useEmptyPackage; + return moduleInfo; + } } diff --git a/ets2panda/driver/build_system/src/build/build_mode.ts b/ets2panda/driver/build_system/src/build/build_mode.ts index ceb492b3e0f88aa17def45387fe45b5893d5eac3..7086578fbd012bf9255ddf31bdb0bcaa96065291 100644 --- a/ets2panda/driver/build_system/src/build/build_mode.ts +++ b/ets2panda/driver/build_system/src/build/build_mode.ts @@ -14,33 +14,47 @@ */ import { BaseMode } from './base_mode'; -import { - BuildConfig, - ES2PANDA_MODE +import { + BuildConfig, + ES2PANDA_MODE } from '../types'; export class BuildMode extends BaseMode { - constructor(buildConfig: BuildConfig) { - super(buildConfig); - } + constructor(buildConfig: BuildConfig) { + super(buildConfig); - public async generateDeclaration(): Promise { - await super.generateDeclarationParallell(); - } + super.processBuildConfig(); + super.backwardCompatibilityWorkaroundStub() + } + + public async run(): Promise { + if (this.entryFiles.size === 0) { + // Nothing to compile + this.logger.printWarn("Nothing to compile. Exiting...") + return; + } - public async run(): Promise { - if (this.es2pandaMode === ES2PANDA_MODE.RUN_PARALLEL) { - // RUN_PARALLEL: Executes tasks using multiple processes - await super.runParallel(); - } else if (this.es2pandaMode === ES2PANDA_MODE.RUN_CONCURRENT) { - // RUN_CONCURRENT: Executes tasks using multiple threads with astcache - await super.runConcurrent(); - } else if (this.es2pandaMode === ES2PANDA_MODE.RUN) { - // RUN: Executes tasks sequentially in a single process and single thread - await super.run(); - } else { - // Default fallback: Uses parallel execution (same as RUN_PARALLEL) - await super.run(); + let buildMode = this.es2pandaMode + if (buildMode === ES2PANDA_MODE.RUN_PARALLEL) { + this.logger.printInfo("Run parallel") + // RUN_PARALLEL: Executes tasks using multiple processes + await super.runParallel(); + } else if (buildMode === ES2PANDA_MODE.RUN_CONCURRENT) { + this.logger.printInfo("Run concurrent") + // RUN_CONCURRENT: Executes tasks using multiple threads with ast-cache + await super.runConcurrent(); + } else if (buildMode === ES2PANDA_MODE.RUN_SIMULTANEOUS) { + this.logger.printInfo("Run simultaneous") + // RUN_SIMULTANEOUS: Build with specific es2panda mode 'simultaneous' + await super.runSimultaneous(); + } else if (buildMode === ES2PANDA_MODE.RUN) { + this.logger.printInfo("Run ordinary") + // RUN: Executes tasks sequentially in a single process and single thread + await super.run(); + } else { + this.logger.printInfo("Run simultaneous (default)") + // Default fallback: same as RUN_SIMULTANEOUS + await super.runSimultaneous(); + } } - } -} \ No newline at end of file +} diff --git a/ets2panda/driver/build_system/src/build/compile_thread_worker.ts b/ets2panda/driver/build_system/src/build/compile_thread_worker.ts index 3d213b7a166d7833641e89d79a4fc57c8da3d4d6..7fcdaf9ccd8e80dc91c4360a61b768f419bccdfd 100644 --- a/ets2panda/driver/build_system/src/build/compile_thread_worker.ts +++ b/ets2panda/driver/build_system/src/build/compile_thread_worker.ts @@ -14,185 +14,182 @@ */ import { parentPort, workerData } from 'worker_threads'; -import { JobInfo } from '../types'; import * as path from 'path'; import { - changeFileExtension, - ensurePathExists + changeFileExtension, + ensurePathExists } from '../util/utils'; import { - DECL_ETS_SUFFIX, - KOALA_WRAPPER_PATH_FROM_SDK + DECL_ETS_SUFFIX, } from '../pre_define'; import { PluginDriver, PluginHook } from '../plugins/plugins_driver'; import { - BuildConfig, - BUILD_MODE, - OHOS_MODULE_TYPE + BuildConfig, + BUILD_MODE, + OHOS_MODULE_TYPE, + KPointer, + CompileJobInfo } from '../types'; import { - LogData, - LogDataFactory, - Logger + LogData, + LogDataFactory, + Logger, } from '../logger'; -import { ErrorCode } from '../error_code'; +import { ErrorCode } from '../util/error'; import { KitImportTransformer } from '../plugins/KitImportTransformer'; import { initKoalaModules } from '../init/init_koala_modules'; const { workerId } = workerData; -function compileAbc(jobInfo: JobInfo): void { - let config = jobInfo.buildConfig as BuildConfig; - Logger.getInstance(config); - PluginDriver.getInstance().initPlugins(config); - let { arkts, arktsGlobal } = initKoalaModules(config) - const isDebug = config.buildMode === BUILD_MODE.DEBUG; - - let errorStatus = false; - try { - let fileInfo = jobInfo.compileFileInfo; - ensurePathExists(fileInfo.abcFilePath); - - const ets2pandaCmd = [ - '_', '--extension', 'ets', - '--arktsconfig', fileInfo.arktsConfigFile, - '--output', fileInfo.abcFilePath, - ]; - - if (isDebug) { - ets2pandaCmd.push('--debug-info'); - ets2pandaCmd.push('--opt-level=0'); +function compileAbc(jobInfo: CompileJobInfo, globalContextPtr: KPointer, buildConfig: BuildConfig): void { + PluginDriver.getInstance().initPlugins(buildConfig); + let { arkts, arktsGlobal } = initKoalaModules(buildConfig) + const isDebug = buildConfig.buildMode === BUILD_MODE.DEBUG; + + let errorStatus = false; + try { + let fileInfo = jobInfo.compileFileInfo; + ensurePathExists(fileInfo.inputFilePath); + + const ets2pandaCmd = [ + '_', '--extension', 'ets', + '--arktsconfig', fileInfo.arktsConfigFile, + '--output', fileInfo.inputFilePath, + ]; + + if (isDebug) { + ets2pandaCmd.push('--debug-info'); + ets2pandaCmd.push('--opt-level=0'); + } + ets2pandaCmd.push(fileInfo.inputFilePath); + + let arkConfig = arkts.Config.create(ets2pandaCmd).peer; + arktsGlobal.config = arkConfig; + + let context = arkts.Context.createCacheContextFromFile(arkConfig, fileInfo.inputFilePath, globalContextPtr, false).peer; + + PluginDriver.getInstance().getPluginContext().setContextPtr(context); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context); + if (buildConfig.aliasConfig && Object.keys(buildConfig.aliasConfig).length > 0) { + // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin + let ast = arkts.EtsScript.fromContext(); + let transformAst = new KitImportTransformer( + arkts, + arktsGlobal.compilerContext.program, + buildConfig.buildSdkPath, + buildConfig.aliasConfig + ).transform(ast); + PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); + } + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context); + + // if ((buildConfig.byteCodeHar || buildConfig.moduleType === OHOS_MODULE_TYPE.SHARED)) { + { + let filePathFromModuleRoot: string = path.relative(buildConfig.moduleRootPath, fileInfo.inputFilePath); + let declEtsOutputPath: string = changeFileExtension( + path.join(buildConfig.declgenV2OutPath as string, filePathFromModuleRoot), + DECL_ETS_SUFFIX + ); + ensurePathExists(declEtsOutputPath); + + // Generate 1.2 declaration files(a temporary solution while binary import not pushed) + arkts.generateStaticDeclarationsFromContext(declEtsOutputPath); + } + + PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, context); + } catch (error) { + errorStatus = true; + if (error instanceof Error) { + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, + 'Compile abc files failed.', + error.message + ); + Logger.getInstance().printError(logData); + } + } finally { + if (!errorStatus) { + // when error occur,wrapper will destroy context. + arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + } + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + arkts.destroyConfig(arktsGlobal.config); } - ets2pandaCmd.push(fileInfo.filePath); - - let arkConfig = arkts.Config.create(ets2pandaCmd).peer; - arktsGlobal.config = arkConfig; - - let context = arkts.Context.createCacheContextFromFile(arkConfig, fileInfo.filePath, jobInfo.globalContextPtr, false).peer; - - PluginDriver.getInstance().getPluginContext().setContextPtr(context); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context); - if (config.aliasConfig && Object.keys(config.aliasConfig).length > 0) { - // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin - let ast = arkts.EtsScript.fromContext(); - let transformAst = new KitImportTransformer( - arkts, - arktsGlobal.compilerContext.program, - config.buildSdkPath, - config.aliasConfig - ).transform(ast); - PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); - } - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context); - - if (config.hasMainModule && (config.byteCodeHar || config.moduleType === OHOS_MODULE_TYPE.SHARED)) { - let filePathFromModuleRoot: string = path.relative(config.moduleRootPath, fileInfo.filePath); - let declEtsOutputPath: string = changeFileExtension( - path.join(config.declgenV2OutPath as string, filePathFromModuleRoot), - DECL_ETS_SUFFIX - ); - ensurePathExists(declEtsOutputPath); - - // Generate 1.2 declaration files(a temporary solution while binary import not pushed) - arkts.generateStaticDeclarationsFromContext(declEtsOutputPath); - } - - PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, context); - } catch (error) { - errorStatus = true; - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, - 'Compile abc files failed.', - error.message - ); - Logger.getInstance().printError(logData); - } - } finally { - if (!errorStatus) { - // when error occur,wrapper will destroy context. - arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); - } - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - arkts.destroyConfig(arktsGlobal.config); - } } -function compileExternalProgram(jobInfo: JobInfo): void { - let config = jobInfo.buildConfig as BuildConfig; - Logger.getInstance(config); - PluginDriver.getInstance().initPlugins(config); - let { arkts, arktsGlobal } = initKoalaModules(config) - const isDebug = config.buildMode === BUILD_MODE.DEBUG; - - let errorStatus = false; - try { - let fileInfo = jobInfo.compileFileInfo; - const ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', fileInfo.arktsConfigFile]; - - if (isDebug) { - ets2pandaCmd.push('--debug-info'); - ets2pandaCmd.push('--opt-level=0'); +function compileDeclaration(jobInfo: CompileJobInfo, globalContextPtr: KPointer, buildConfig: BuildConfig): void { + PluginDriver.getInstance().initPlugins(buildConfig); + let { arkts, arktsGlobal } = initKoalaModules(buildConfig) + const isDebug = buildConfig.buildMode === BUILD_MODE.DEBUG; + + let errorStatus = false; + try { + let fileInfo = jobInfo.compileFileInfo; + const ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', fileInfo.arktsConfigFile]; + + if (isDebug) { + ets2pandaCmd.push('--debug-info'); + ets2pandaCmd.push('--opt-level=0'); + } + ets2pandaCmd.push(fileInfo.inputFilePath); + + let arkConfig = arkts.Config.create(ets2pandaCmd).peer; + arktsGlobal.config = arkConfig; + + let context = arkts.Context.createCacheContextFromFile(arkConfig, fileInfo.inputFilePath, globalContextPtr, true).peer; + + PluginDriver.getInstance().getPluginContext().setContextPtr(context); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context); + + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context); + + PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_LOWERED, context); + } catch (error) { + errorStatus = true; + if (error instanceof Error) { + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, + 'Compile external program files failed.', + error.message + ); + Logger.getInstance().printError(logData); + } + } finally { + if (!errorStatus) { + // when error occur,wrapper will destroy context. + arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + } + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + arkts.destroyConfig(arktsGlobal.config); } - ets2pandaCmd.push(fileInfo.filePath); - - let arkConfig = arkts.Config.create(ets2pandaCmd).peer; - arktsGlobal.config = arkConfig; - - let context = arkts.Context.createCacheContextFromFile(arkConfig, fileInfo.filePath, jobInfo.globalContextPtr, true).peer; - - PluginDriver.getInstance().getPluginContext().setContextPtr(context); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context); - - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context); - - PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_LOWERED, context); - } catch (error) { - errorStatus = true; - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, - 'Compile external program files failed.', - error.message - ); - Logger.getInstance().printError(logData); - } - } finally { - if (!errorStatus) { - // when error occur,wrapper will destroy context. - arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); - } - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - arkts.destroyConfig(arktsGlobal.config); - } } -parentPort?.on('message', (msg) => { - if (msg.type === 'ASSIGN_TASK') { - const job = msg.jobInfo; - - if (job.isCompileAbc) { - compileAbc(job); - } else { - compileExternalProgram(job); - } - - parentPort?.postMessage({ - type: 'TASK_FINISH', - jobId: job.id, - workerId, - }); - } else if (msg.type === 'EXIT') { - process.exit(0); - } +parentPort!.on('message', (msg: any) => { + if (msg.type === 'ASSIGN_TASK') { + const { job, globalContextPtr, buildConfig } = msg.data; + + if (job.isCompileAbc) { + compileAbc(job, globalContextPtr, buildConfig); + } else { + compileDeclaration(job, globalContextPtr, buildConfig); + } + + parentPort?.postMessage({ + type: 'TASK_FINISH', + jobId: job.id, + workerId, + }); + } else if (msg.type === 'EXIT') { + process.exit(0); + } }); diff --git a/ets2panda/driver/build_system/src/build/compile_worker.ts b/ets2panda/driver/build_system/src/build/compile_worker.ts index fbb0b497c0fc5da6e0184e5495ae3583308cae21..8224516998ceed1ded7146e6d9d4301a21dbc384 100644 --- a/ets2panda/driver/build_system/src/build/compile_worker.ts +++ b/ets2panda/driver/build_system/src/build/compile_worker.ts @@ -13,123 +13,111 @@ * limitations under the License. */ -import { CompileFileInfo, ModuleInfo } from '../types'; import * as fs from 'fs'; import * as path from 'path'; import { - changeFileExtension, - ensurePathExists, - serializeWithIgnore + changeFileExtension, + ensurePathExists, } from '../util/utils'; import { - DECL_ETS_SUFFIX, - KOALA_WRAPPER_PATH_FROM_SDK + DECL_ETS_SUFFIX, } from '../pre_define'; import { PluginDriver, PluginHook } from '../plugins/plugins_driver'; import { - BuildConfig, - BUILD_MODE, - OHOS_MODULE_TYPE + BuildConfig, + CompileJobInfo, + BUILD_MODE, + OHOS_MODULE_TYPE } from '../types'; import { initKoalaModules } from '../init/init_koala_modules'; -import { LogData, LogDataFactory, Logger } from '../logger'; -import { ErrorCode } from '../error_code'; +import { LogDataFactory, Logger, getConsoleLogger } from '../logger'; +import { ErrorCode, DriverError } from '../util/error'; import { KitImportTransformer } from '../plugins/KitImportTransformer' -process.on('message', async (message: { - id: number; - fileInfo: CompileFileInfo; - buildConfig: BuildConfig; +process.on('message', (message: { + job: CompileJobInfo; + buildConfig: BuildConfig; }) => { - const id = message.id; - //@ts-ignore - const { fileInfo, buildConfig } = message.payload; + const { job, buildConfig } = message; - Logger.getInstance(buildConfig); - PluginDriver.getInstance().initPlugins(buildConfig); - let { arkts, arktsGlobal } = initKoalaModules(buildConfig) - let errorStatus = false; - - try { - Logger.getInstance(buildConfig); + Logger.getInstance(getConsoleLogger); PluginDriver.getInstance().initPlugins(buildConfig); + let { arkts, arktsGlobal } = initKoalaModules(buildConfig) + const isDebug = buildConfig.buildMode === BUILD_MODE.DEBUG; + const inputFile = job.compileFileInfo.inputFilePath + const outputFile = job.compileFileInfo.outputFilePath + const arktsConfigFile = job.compileFileInfo.arktsConfigFile; - ensurePathExists(fileInfo.abcFilePath); - const source = fs.readFileSync(fileInfo.filePath).toString(); + const source = fs.readFileSync(inputFile).toString(); - let ets2pandaCmd = [ - '_', '--extension', 'ets', - '--arktsconfig', fileInfo.arktsConfigFile, - '--output', fileInfo.abcFilePath - ]; - if (isDebug) { - ets2pandaCmd.push('--debug-info'); - ets2pandaCmd.push('--opt-level=0'); - } - ets2pandaCmd.push(fileInfo.filePath); + try { + let ets2pandaCmd = [ + '_', + '--extension', + 'ets', + '--arktsconfig', + arktsConfigFile, + '--output', + outputFile + ]; + if (isDebug) { + ets2pandaCmd.push('--debug-info'); + ets2pandaCmd.push('--opt-level=0'); + } + ets2pandaCmd.push(inputFile); - arktsGlobal.filePath = fileInfo.filePath; - arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer; - arktsGlobal.compilerContext = arkts.Context.createFromString(source); + arktsGlobal.filePath = inputFile; + arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer; + arktsGlobal.compilerContext = arkts.Context.createFromString(source); - PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); + PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); - if (buildConfig.aliasConfig && Object.keys(buildConfig.aliasConfig).length > 0) { - // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin - let ast = arkts.EtsScript.fromContext(); - let transformAst = new KitImportTransformer( - arkts, - arktsGlobal.compilerContext.program, - buildConfig.buildSdkPath, - buildConfig.aliasConfig - ).transform(ast); - PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); - } - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); + if (buildConfig.aliasConfig && Object.keys(buildConfig.aliasConfig).length > 0) { + // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin + let ast = arkts.EtsScript.fromContext(); + let transformAst = new KitImportTransformer( + arkts, + arktsGlobal.compilerContext.program, + buildConfig.buildSdkPath, + buildConfig.aliasConfig + ).transform(ast); + PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); + } + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); - if (buildConfig.hasMainModule && (buildConfig.byteCodeHar || buildConfig.moduleType === OHOS_MODULE_TYPE.SHARED)) { - const filePathFromModuleRoot = path.relative(buildConfig.moduleRootPath, fileInfo.filePath); - const declEtsOutputPath = changeFileExtension( - path.join(buildConfig.declgenV2OutPath!, filePathFromModuleRoot), - DECL_ETS_SUFFIX - ); - ensurePathExists(declEtsOutputPath); - arkts.generateStaticDeclarationsFromContext(declEtsOutputPath); - } - PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); + // if ((buildConfig.byteCodeHar || buildConfig.moduleType === OHOS_MODULE_TYPE.SHARED)) { + { + const filePathFromModuleRoot = path.relative(buildConfig.moduleRootPath, inputFile); + const declEtsOutputPath = changeFileExtension( + path.join(buildConfig.declgenV2OutPath!, filePathFromModuleRoot), + DECL_ETS_SUFFIX + ); + ensurePathExists(declEtsOutputPath); + arkts.generateStaticDeclarationsFromContext(declEtsOutputPath); + } + PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer); - if (process.send) { - process.send({ id, success: true, shouldKill: false }); - } - } catch (error) { - errorStatus = true; - if (error instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, - 'Compile abc files failed.', - error.message, - fileInfo.filePath - ); - if (process.send) { - process.send({ - id, - success: false, - shouldKill: true, - error: serializeWithIgnore(logData) - }); - } - } - } finally { - if (!errorStatus) { - arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + process.send!(job); + } catch (error) { + if (error instanceof Error) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, + 'Compile abc files failed.', + error.message, + inputFile + ) + ); + } + } finally { + arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + arkts.destroyConfig(arktsGlobal.config); } - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - arkts.destroyConfig(arktsGlobal.config); - } }); diff --git a/ets2panda/driver/build_system/src/build/declgen_worker.ts b/ets2panda/driver/build_system/src/build/declgen_worker.ts index fcb5ae0adca06c95140e87a8c0e3e75e55158674..3807174fab527a2c3febec57584c9388e73f251d 100644 --- a/ets2panda/driver/build_system/src/build/declgen_worker.ts +++ b/ets2panda/driver/build_system/src/build/declgen_worker.ts @@ -15,133 +15,119 @@ import { CompileFileInfo, ModuleInfo } from '../types'; import { BuildConfig } from '../types'; -import { - Logger, - LogData, - LogDataFactory -} from '../logger'; -import { ErrorCode } from '../error_code'; +import { Logger, getConsoleLogger } from '../logger'; import * as fs from 'fs'; import * as path from 'path'; import { - changeDeclgenFileExtension, - changeFileExtension, - createFileIfNotExists, - serializeWithIgnore, - ensurePathExists + changeDeclgenFileExtension, + changeFileExtension, + createFileIfNotExists, + ensurePathExists } from '../util/utils'; import { - DECL_ETS_SUFFIX, - DECL_TS_SUFFIX, - STATIC_RECORD_FILE, - STATIC_RECORD_FILE_CONTENT, - TS_SUFFIX + DECL_ETS_SUFFIX, + DECL_TS_SUFFIX, + STATIC_RECORD_FILE, + STATIC_RECORD_FILE_CONTENT, + TS_SUFFIX } from '../pre_define'; import { PluginDriver, PluginHook } from '../plugins/plugins_driver'; import { initKoalaModules } from '../init/init_koala_modules'; process.on('message', async (message: { - id: string; - payload: { - fileInfo: CompileFileInfo; - buildConfig: BuildConfig; - moduleInfos: Array<[string, ModuleInfo]>; - }; + id: string; + payload: { + packageName: string, + fileInfo: CompileFileInfo; + buildConfig: BuildConfig; + moduleInfos: Array<[string, ModuleInfo]>; + }; }) => { - if (!process.send) { - throw new Error('process.send is undefined. This worker must be run as a forked process.'); - } - - const { id, payload } = message; - const { fileInfo, buildConfig, moduleInfos } = payload; - const moduleInfosMap = new Map(moduleInfos); - const logger = Logger.getInstance(buildConfig); - const pluginDriver = PluginDriver.getInstance(); - pluginDriver.initPlugins(buildConfig); - - let { arkts, arktsGlobal } = initKoalaModules(buildConfig) - let errorStatus = false; - let continueOnError = buildConfig.continueOnError ?? true; - try { - const source = fs.readFileSync(fileInfo.filePath, 'utf8'); - const moduleInfo = moduleInfosMap.get(fileInfo.packageName)!; - - let filePathFromModuleRoot = path.relative(moduleInfo.moduleRootPath, fileInfo.filePath); - let declEtsOutputPath = path.join(moduleInfo.declgenV1OutPath!, moduleInfo.packageName, filePathFromModuleRoot); - declEtsOutputPath = changeDeclgenFileExtension(declEtsOutputPath, DECL_ETS_SUFFIX); - - let etsOutputPath = path.join(moduleInfo.declgenBridgeCodePath!, moduleInfo.packageName, filePathFromModuleRoot); - etsOutputPath = changeDeclgenFileExtension(etsOutputPath, TS_SUFFIX); - - ensurePathExists(declEtsOutputPath); - ensurePathExists(etsOutputPath); - - const staticRecordPath = path.join(moduleInfo.declgenV1OutPath!, STATIC_RECORD_FILE); - const declEtsOutputDir = path.dirname(declEtsOutputPath); - const staticRecordRelativePath = changeFileExtension( - path.relative(declEtsOutputDir, staticRecordPath).replace(/\\/g, '/'), - '', - DECL_TS_SUFFIX - ); - createFileIfNotExists(staticRecordPath, STATIC_RECORD_FILE_CONTENT); - - arktsGlobal.filePath = fileInfo.filePath; - arktsGlobal.config = arkts.Config.create([ - '_', - '--extension', - 'ets', - '--arktsconfig', - fileInfo.arktsConfigFile, - fileInfo.filePath - ]).peer; - - arktsGlobal.compilerContext = arkts.Context.createFromStringWithHistory(source); - pluginDriver.getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); - const skipDeclCheck = buildConfig?.skipDeclCheck ?? true; - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer, skipDeclCheck); - let ast = arkts.EtsScript.fromContext(); - pluginDriver.getPluginContext().setArkTSAst(ast); - pluginDriver.runPluginHook(PluginHook.PARSED); - - arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer, skipDeclCheck); - ast = arkts.EtsScript.fromContext(); - pluginDriver.getPluginContext().setArkTSAst(ast); - pluginDriver.runPluginHook(PluginHook.CHECKED); - - arkts.generateTsDeclarationsFromContext( - declEtsOutputPath, - etsOutputPath, - false, - false, - staticRecordRelativePath - ); - - logger.printInfo(`[declgen] ${fileInfo.filePath} processed successfully`); - - process.send({ id, success: true, shouldKill: false }); - } catch (err) { - errorStatus = true; - if (err instanceof Error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_DECLGEN_FAIL, - 'Declgen generates declaration files failed.', - err.message, - fileInfo.filePath - ); - process.send({ - id, - success: false, - shouldKill: !continueOnError, - error: serializeWithIgnore(logData) - }); - } - } finally { - if (!errorStatus && arktsGlobal?.compilerContext?.peer) { - arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + if (!process.send) { + throw new Error('process.send is undefined. This worker must be run as a forked process.'); } - if (arktsGlobal?.config) { - arkts.destroyConfig(arktsGlobal.config); + + const { id, payload } = message; + const { packageName, fileInfo, buildConfig, moduleInfos } = payload; + const moduleInfosMap = new Map(moduleInfos); + const logger = Logger.getInstance(getConsoleLogger); + const pluginDriver = PluginDriver.getInstance(); + pluginDriver.initPlugins(buildConfig); + + let { arkts, arktsGlobal } = initKoalaModules(buildConfig) + + try { + const source = fs.readFileSync(fileInfo.inputFilePath, 'utf8'); + const moduleInfo = moduleInfosMap.get(packageName)!; + + let filePathFromModuleRoot = path.relative(moduleInfo.moduleRootPath, fileInfo.inputFilePath); + let declEtsOutputPath = path.join(moduleInfo.declgenV1OutPath!, moduleInfo.packageName, filePathFromModuleRoot); + declEtsOutputPath = changeDeclgenFileExtension(declEtsOutputPath, DECL_ETS_SUFFIX); + + let etsOutputPath = path.join(moduleInfo.declgenBridgeCodePath!, moduleInfo.packageName, filePathFromModuleRoot); + etsOutputPath = changeDeclgenFileExtension(etsOutputPath, TS_SUFFIX); + + ensurePathExists(declEtsOutputPath); + ensurePathExists(etsOutputPath); + + const staticRecordPath = path.join(moduleInfo.declgenV1OutPath!, STATIC_RECORD_FILE); + const declEtsOutputDir = path.dirname(declEtsOutputPath); + const staticRecordRelativePath = changeFileExtension( + path.relative(declEtsOutputDir, staticRecordPath).replace(/\\/g, '/'), + '', + DECL_TS_SUFFIX + ); + createFileIfNotExists(staticRecordPath, STATIC_RECORD_FILE_CONTENT); + + arktsGlobal.filePath = fileInfo.inputFilePath; + arktsGlobal.config = arkts.Config.create([ + '_', + '--extension', + 'ets', + '--arktsconfig', + fileInfo.arktsConfigFile, + fileInfo.inputFilePath + ]).peer; + + arktsGlobal.compilerContext = arkts.Context.createFromStringWithHistory(source); + pluginDriver.getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); + const skipDeclCheck = buildConfig?.skipDeclCheck ?? true; + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer, skipDeclCheck); + let ast = arkts.EtsScript.fromContext(); + pluginDriver.getPluginContext().setArkTSAst(ast); + pluginDriver.runPluginHook(PluginHook.PARSED); + + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer, skipDeclCheck); + ast = arkts.EtsScript.fromContext(); + pluginDriver.getPluginContext().setArkTSAst(ast); + pluginDriver.runPluginHook(PluginHook.CHECKED); + + arkts.generateTsDeclarationsFromContext( + declEtsOutputPath, + etsOutputPath, + false, + false, + staticRecordRelativePath + ); + + logger.printInfo(`[declgen] ${fileInfo.inputFilePath} processed successfully`); + + process.send({ id, success: true }); + } catch (err) { + if (err instanceof Error) { + process.send({ + id, + success: false, + error: `Generate declaration files failed.\n${err?.message || err}` + }); + } + } finally { + if (arktsGlobal?.compilerContext?.peer) { + arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer); + } + if (arktsGlobal?.config) { + arkts.destroyConfig(arktsGlobal.config); + } } - } }); diff --git a/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts b/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts index c9b8145109ab6c7d7a1b05050a5407e2f0494e41..4a24e46a954b4bff0e588aa0c375a3b3a6d93931 100644 --- a/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts +++ b/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts @@ -17,530 +17,530 @@ import * as path from 'path'; import * as fs from 'fs'; import { - Logger, - LogData, - LogDataFactory + Logger, + LogDataFactory } from '../logger'; import { - ErrorCode -} from '../error_code'; + ErrorCode, + DriverError +} from '../util/error'; import { - changeFileExtension, - ensurePathExists, - getInteropFilePathByApi, - getOhmurlByApi, - hasEntry, - isSubPathOf, - readFirstLineSync, - safeRealpath, - toUnixPath + changeFileExtension, + ensurePathExists, + getInteropFilePathByApi, + getOhmurlByApi, + hasEntry, + isSubPathOf, + safeRealpath, + toUnixPath } from '../util/utils'; import { - AliasConfig, - ArkTSConfigObject, - BuildConfig, - DependencyItem, - DynamicFileContext, - ModuleInfo, + AliasConfig, + ArkTSConfigObject, + BuildConfig, + DependencyItem, + DynamicFileContext, + ModuleInfo, } from '../types'; import { - COMPONENT, - DYNAMIC_PREFIX, - KITS, - LANGUAGE_VERSION, - SYSTEM_SDK_PATH_FROM_SDK, - sdkConfigPrefix, + COMPONENT, + DYNAMIC_PREFIX, + KITS, + LANGUAGE_VERSION, + SYSTEM_SDK_PATH_FROM_SDK, + sdkConfigPrefix, } from '../pre_define'; export class ArkTSConfig { - config: ArkTSConfigObject; - - constructor(moduleInfo: ModuleInfo, cacheDir: string) { - this.config = { - compilerOptions: { - package: moduleInfo.packageName, - baseUrl: path.resolve(moduleInfo.moduleRootPath, moduleInfo.sourceRoots[0]), - paths: {}, - dependencies: {}, - rootDir: path.resolve(moduleInfo.moduleRootPath), - cacheDir: path.resolve(cacheDir, moduleInfo.packageName), - } - }; - } - - addPathMappings(mappings: Record): void { - const paths = this.config.compilerOptions.paths; - for (const [key, value] of Object.entries(mappings)) { - if (!paths[key]) { - paths[key] = value; - } else { - paths[key] = [...new Set([...paths[key], ...value])]; - } - } - } - - addDependency({ name, item }: { name: string; item: DependencyItem }): void { - const deps = this.config.compilerOptions.dependencies; - const existing = deps[name]; - - if (existing) { - const mergedAlias = Array.from(new Set([...(existing.alias ?? []), ...(item.alias ?? [])])); - deps[name] = { - ...existing, - ...item, - alias: mergedAlias - }; - } else { - deps[name] = item; - } - } - - addDependencies(deps: Record): void { - Object.entries(deps).forEach(([name, item]) => { - this.addDependency({ name, item }); - }); - } - - getCompilerOptions(): ArkTSConfigObject { - return this.config; - } - - getPackageName(): string { - return this.config.compilerOptions.package; - } - - getDependencies(): Record { - return this.config.compilerOptions.dependencies; - } - - getPathSection(): Record { - return this.config.compilerOptions.paths; - } - - setUseEmptyPackage(value: boolean = false): void { - this.config.compilerOptions.useEmptyPackage = value; - } - - mergeArktsConfig(source: ArkTSConfig | undefined): void { - if (!source) { - return; - } - this.addDependencies(source.getDependencies()); - this.addPathMappings(source.getPathSection()); - } + object: ArkTSConfigObject; + + constructor(moduleInfo: ModuleInfo, cacheDir: string) { + this.object = { + compilerOptions: { + package: moduleInfo.packageName, + baseUrl: path.resolve(moduleInfo.moduleRootPath, moduleInfo.sourceRoots[0]), + paths: {}, + dependencies: {}, + cacheDir: path.resolve(cacheDir, moduleInfo.packageName), + rootDir: path.resolve(moduleInfo.moduleRootPath), + } + }; + } + + addPathMappings(mappings: Record): void { + const paths = this.object.compilerOptions.paths; + for (const [key, value] of Object.entries(mappings)) { + if (!paths[key]) { + paths[key] = value; + } else { + paths[key] = [...new Set([...paths[key], ...value])]; + } + } + } + + addDependency({ name, item }: { name: string; item: DependencyItem }): void { + const deps = this.object.compilerOptions.dependencies; + const existing = deps[name]; + + if (existing) { + const mergedAlias = Array.from(new Set([...(existing.alias ?? []), ...(item.alias ?? [])])); + deps[name] = { + ...existing, + ...item, + alias: mergedAlias + }; + } else { + deps[name] = item; + } + } + + addDependencies(deps: Record): void { + Object.entries(deps).forEach(([name, item]) => { + this.addDependency({ name, item }); + }); + } + + public get compilerOptions() { + return this.object.compilerOptions; + } + + public get packageName(): string { + return this.object.compilerOptions.package; + } + + public get dependencies(): Record { + return this.object.compilerOptions.dependencies; + } + + public get pathSection(): Record { + return this.object.compilerOptions.paths; + } + + public set useEmptyPackage(value: boolean) { + this.object.compilerOptions.useEmptyPackage = value; + } + + mergeArktsConfig(source: ArkTSConfig | undefined): void { + if (!source) { + return; + } + this.addDependencies(source.dependencies); + this.addPathMappings(source.pathSection); + } } export class ArkTSConfigGenerator { - private static instance: ArkTSConfigGenerator | undefined; - private stdlibStdPath: string; - private stdlibEscompatPath: string; - private systemSdkPath: string; - private externalApiPaths: string[]; - - private moduleInfos: Map; - private buildConfig: BuildConfig; - - private logger: Logger; - private aliasConfig: Record>; - private dynamicSDKPaths: Set; - private systemPathSection: Record; - private systemDependenciesSection: Record; - private arktsconfigs: Map; - - private constructor(buildConfig: BuildConfig, moduleInfos: Map) { - this.logger = Logger.getInstance(); - const realPandaSdkPath = safeRealpath(buildConfig.pandaSdkPath!!, this.logger); - const realBuildSdkPath = safeRealpath(buildConfig.buildSdkPath, this.logger); - const realPandaStdlibPath = buildConfig.pandaStdlibPath ?? path.resolve(realPandaSdkPath, 'lib', 'stdlib'); - this.stdlibStdPath = path.resolve(realPandaStdlibPath, 'std'); - this.stdlibEscompatPath = path.resolve(realPandaStdlibPath, 'escompat'); - this.systemSdkPath = path.resolve(realBuildSdkPath, SYSTEM_SDK_PATH_FROM_SDK); - this.externalApiPaths = buildConfig.externalApiPaths; - this.buildConfig = buildConfig; - - this.moduleInfos = moduleInfos; - this.aliasConfig = buildConfig.aliasConfig; - this.dynamicSDKPaths = buildConfig.interopSDKPaths; - - this.systemPathSection = {} - this.systemDependenciesSection = {}; - this.arktsconfigs = new Map(); - - this.initPathInfo(); - } - - public static getInstance(buildConfig?: BuildConfig, moduleInfos?: Map): ArkTSConfigGenerator { - if (!ArkTSConfigGenerator.instance) { - if (!buildConfig || !moduleInfos) { - throw new Error( - 'buildConfig and moduleInfos is required for the first instantiation of ArkTSConfigGenerator.'); - } - ArkTSConfigGenerator.instance = new ArkTSConfigGenerator(buildConfig, moduleInfos); - } - return ArkTSConfigGenerator.instance; - } - - public static destroyInstance(): void { - ArkTSConfigGenerator.instance = undefined; - } - private generateSystemSdkPathSection(pathSection: Record): void { - function traverse(currentDir: string, relativePath: string = '', isExcludedDir: boolean = false, allowedExtensions: string[] = ['.d.ets']): void { - const items = fs.readdirSync(currentDir); - for (const item of items) { - const itemPath = path.join(currentDir, item); - const stat = fs.statSync(itemPath); - const isAllowedFile = allowedExtensions.some(ext => item.endsWith(ext)); - if (stat.isFile() && !isAllowedFile) { - continue; + private static instance: ArkTSConfigGenerator | undefined; + private stdlibStdPath: string; + private stdlibEscompatPath: string; + private systemSdkPath: string; + + private buildConfig: BuildConfig; + + private logger: Logger; + private systemPathSection: Record; + private systemDependenciesSection: Record; + private arktsconfigs: Map; + + private constructor(buildConfig: BuildConfig) { + this.logger = Logger.getInstance(); + const realPandaSdkPath = safeRealpath(buildConfig.pandaSdkPath!!); + const realBuildSdkPath = safeRealpath(buildConfig.buildSdkPath); + const realPandaStdlibPath = buildConfig.pandaStdlibPath ?? path.resolve(realPandaSdkPath, 'lib', 'stdlib'); + this.stdlibStdPath = path.resolve(realPandaStdlibPath, 'std'); + this.stdlibEscompatPath = path.resolve(realPandaStdlibPath, 'escompat'); + this.systemSdkPath = path.resolve(realBuildSdkPath, SYSTEM_SDK_PATH_FROM_SDK); + this.buildConfig = buildConfig; + + this.systemPathSection = {} + this.systemDependenciesSection = {}; + this.arktsconfigs = new Map(); + + this.initPathInfo(); + } + + public get aliasConfig() { + return this.buildConfig.aliasConfig + } + + public get dynamicSDKPaths() { + return this.buildConfig.interopSDKPaths; + } + + public get externalApiPaths() { + return this.buildConfig.externalApiPaths; + } + + public static getInstance(buildConfig?: BuildConfig): ArkTSConfigGenerator { + if (!ArkTSConfigGenerator.instance) { + if (!buildConfig) { + throw new Error( + 'buildConfig and moduleInfos is required for the first instantiation of ArkTSConfigGenerator.'); + } + ArkTSConfigGenerator.instance = new ArkTSConfigGenerator(buildConfig); } + return ArkTSConfigGenerator.instance; + } - if (stat.isFile()) { - const basename = path.basename(item, '.d.ets'); - const key = isExcludedDir ? basename : (relativePath ? `${relativePath}.${basename}` : basename); - pathSection[key] = [changeFileExtension(itemPath, '', '.d.ets')]; + public static destroyInstance(): void { + ArkTSConfigGenerator.instance = undefined; + } + private generateSystemSdkPathSection(pathSection: Record): void { + function traverse(currentDir: string, relativePath: string = '', isExcludedDir: boolean = false, allowedExtensions: string[] = ['.d.ets']): void { + const items = fs.readdirSync(currentDir); + for (const item of items) { + const itemPath = path.join(currentDir, item); + const stat = fs.statSync(itemPath); + const isAllowedFile = allowedExtensions.some(ext => item.endsWith(ext)); + if (stat.isFile() && !isAllowedFile) { + continue; + } + + if (stat.isFile()) { + const basename = path.basename(item, '.d.ets'); + const key = isExcludedDir ? basename : (relativePath ? `${relativePath}.${basename}` : basename); + pathSection[key] = [changeFileExtension(itemPath, '', '.d.ets')]; + } + if (stat.isDirectory()) { + // For files under api dir excluding arkui/runtime-api dir, + // fill path section with `"pathFromApi.subdir.fileName" = [${absolute_path_to_file}]`; + // For @koalaui files under arkui/runtime-api dir, + // fill path section with `"fileName" = [${absolute_path_to_file}]`. + const isCurrentDirExcluded = path.basename(currentDir) === 'arkui' && item === 'runtime-api'; + const newRelativePath = isCurrentDirExcluded ? '' : (relativePath ? `${relativePath}.${item}` : item); + traverse(path.resolve(currentDir, item), newRelativePath, isCurrentDirExcluded || isExcludedDir); + } + } } - if (stat.isDirectory()) { - // For files under api dir excluding arkui/runtime-api dir, - // fill path section with `"pathFromApi.subdir.fileName" = [${absolute_path_to_file}]`; - // For @koalaui files under arkui/runtime-api dir, - // fill path section with `"fileName" = [${absolute_path_to_file}]`. - const isCurrentDirExcluded = path.basename(currentDir) === 'arkui' && item === 'runtime-api'; - const newRelativePath = isCurrentDirExcluded ? '' : (relativePath ? `${relativePath}.${item}` : item); - traverse(path.resolve(currentDir, item), newRelativePath, isCurrentDirExcluded || isExcludedDir); + + if (this.externalApiPaths && this.externalApiPaths.length !== 0) { + this.externalApiPaths.forEach((sdkPath: string) => { + fs.existsSync(sdkPath) ? traverse(sdkPath) : this.logger.printWarn(`sdk path ${sdkPath} not exist.`); + }); + } else { + // NOTE: to be refacotred + // NOTE: should be removed after turn externalApiPaths into mandatory param + // Search openharmony sdk only, we keep them for ci compatibility. + let apiPath: string = path.resolve(this.systemSdkPath, 'api'); + fs.existsSync(apiPath) ? traverse(apiPath) : this.logger.printWarn(`sdk path ${apiPath} not exist.`); + + let arktsPath: string = path.resolve(this.systemSdkPath, 'arkts'); + fs.existsSync(arktsPath) ? traverse(arktsPath) : this.logger.printWarn(`sdk path ${arktsPath} not exist.`); + + let kitsPath: string = path.resolve(this.systemSdkPath, 'kits'); + fs.existsSync(kitsPath) ? traverse(kitsPath) : this.logger.printWarn(`sdk path ${kitsPath} not exist.`); } - } + pathSection.std = [this.stdlibStdPath]; + pathSection.escompat = [this.stdlibEscompatPath]; } - if (this.externalApiPaths && this.externalApiPaths.length !== 0) { - this.externalApiPaths.forEach((sdkPath: string) => { - fs.existsSync(sdkPath) ? traverse(sdkPath) : this.logger.printWarn(`sdk path ${sdkPath} not exist.`); - }); - } else { - // Search openharmony sdk only, we keep them for ci compatibility. - let apiPath: string = path.resolve(this.systemSdkPath, 'api'); - fs.existsSync(apiPath) ? traverse(apiPath) : this.logger.printWarn(`sdk path ${apiPath} not exist.`); + private addPathSection(moduleInfo: ModuleInfo, arktsconfig: ArkTSConfig): void { + arktsconfig.addPathMappings(this.systemPathSection); - let arktsPath: string = path.resolve(this.systemSdkPath, 'arkts'); - fs.existsSync(arktsPath) ? traverse(arktsPath) : this.logger.printWarn(`sdk path ${arktsPath} not exist.`); + // NOTE: workaround + // NOTE:: to be refactored + arktsconfig.addPathMappings({ + [arktsconfig.packageName]: [arktsconfig.compilerOptions.baseUrl] + }) - let kitsPath: string = path.resolve(this.systemSdkPath, 'kits'); - fs.existsSync(kitsPath) ? traverse(kitsPath) : this.logger.printWarn(`sdk path ${kitsPath} not exist.`); + if (!hasEntry(moduleInfo)) { + return; + } + + if (!moduleInfo.entryFile || !fs.existsSync(path.join(moduleInfo.moduleRootPath, moduleInfo.entryFile))) { + return; + } + + if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID) { + this.getAllFilesToPathSection(moduleInfo, arktsconfig); + } + } + + + private getDependencyKey(file: string, moduleInfo: ModuleInfo): string { + let unixFilePath: string = file.replace(/\\/g, '/'); + return moduleInfo.packageName + '/' + unixFilePath; + } + + private addDependenciesSection(moduleInfo: ModuleInfo, arktsconfig: ArkTSConfig): void { + moduleInfo.dynamicDependencyModules.forEach((depModuleInfo: ModuleInfo) => { + if (!depModuleInfo.declFilesPath || !fs.existsSync(depModuleInfo.declFilesPath)) { + console.error(`Module ${moduleInfo.packageName} depends on dynamic module ${depModuleInfo.packageName}, but + decl file not found on path ${depModuleInfo.declFilesPath}`); + return; + } + + const declFilesObject = JSON.parse(fs.readFileSync(depModuleInfo.declFilesPath, 'utf-8')); + const files = declFilesObject.files; + + Object.keys(files).forEach((file: string) => { + const dependencyKey: string = this.getDependencyKey(file, depModuleInfo); + const depItem: DependencyItem = { + language: 'js', + path: files[file].declPath, + ohmUrl: files[file].ohmUrl + }; + + arktsconfig.addDependency({ + name: dependencyKey, + item: depItem + }); + + const absFilePath: string = path.resolve(depModuleInfo.moduleRootPath, file); + const entryFileWithoutExtension: string = changeFileExtension(depModuleInfo.entryFile, ''); + + if (absFilePath === entryFileWithoutExtension) { + arktsconfig.addDependency({ + name: depModuleInfo.packageName, + item: depItem + }); + } + }); + }); + arktsconfig.addDependencies(this.systemDependenciesSection); } - pathSection.std = [this.stdlibStdPath]; - pathSection.escompat = [this.stdlibEscompatPath]; - } - private getPathSection(moduleInfo: ModuleInfo, arktsconfig: ArkTSConfig): void { - arktsconfig.addPathMappings(this.systemPathSection); + public generateArkTSConfigFile( + moduleInfo: ModuleInfo, + enableDeclgenEts2Ts: boolean + ): ArkTSConfig { + if (!moduleInfo.sourceRoots || moduleInfo.sourceRoots.length === 0) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_SOURCEROOTS_NOT_SET_FAIL, + `SourceRoots not set for module ${moduleInfo.packageName}.` + ) + ); + } + let arktsConfig: ArkTSConfig = new ArkTSConfig(moduleInfo, this.buildConfig.cachePath); + this.arktsconfigs.set(moduleInfo.packageName, arktsConfig); + this.addPathSection(moduleInfo, arktsConfig); + + if (!enableDeclgenEts2Ts) { + this.addDependenciesSection(moduleInfo, arktsConfig); + } + + this.processAlias(arktsConfig); + + if (moduleInfo.frameworkMode) { + arktsConfig.useEmptyPackage = moduleInfo.useEmptyPackage ?? false; + } + + ensurePathExists(moduleInfo.arktsConfigFile); - if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID) { - this.getAllFilesToPathSection(moduleInfo, arktsconfig); + this.logger.printInfo(`arktsconfig for ${moduleInfo.packageName}:\n${JSON.stringify(arktsConfig, null, 1)}`) + return arktsConfig; } - if (!hasEntry(moduleInfo)) { - return; + private processAlias(arktsconfigs: ArkTSConfig): void { + const aliasForPkg = this.aliasConfig?.[arktsconfigs.packageName]; + if (!aliasForPkg) { + return; + } + for (const [aliasName, aliasConfig] of Object.entries(aliasForPkg)) { + if (aliasConfig.isStatic) { + continue; + } + if (aliasConfig.originalAPIName.startsWith('@kit')) { + this.processStaticAlias(aliasName, aliasConfig, arktsconfigs); + } else { + this.processDynamicAlias(aliasName, aliasConfig, arktsconfigs); + } + } } - if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_1_1) { - return; + private traverseDependencies( + currentDir: string, + relativePath: string, + isExcludedDir: boolean, + dependencySection: Record, + prefix: string = '' + ): void { + const allowedExtensions = ['.d.ets']; + const items = fs.readdirSync(currentDir); + + for (const item of items) { + const itemPath = path.join(currentDir, item); + const stat = fs.statSync(itemPath); + + if (stat.isFile()) { + if (this.isAllowedExtension(item, allowedExtensions)) { + this.processDynamicFile({ + filePath: itemPath, + fileName: item, + relativePath, + isExcludedDir, + dependencySection, + prefix + }); + } + continue; + } + + if (stat.isDirectory()) { + const isRuntimeAPI = path.basename(currentDir) === 'arkui' && item === 'runtime-api'; + const newRelativePath = isRuntimeAPI + ? '' + : (relativePath ? `${relativePath}/${item}` : item); + + this.traverseDependencies( + path.resolve(currentDir, item), + newRelativePath, + isExcludedDir || isRuntimeAPI, + dependencySection, + prefix + ); + } + } } - if (!moduleInfo.entryFile || !fs.existsSync(moduleInfo.entryFile)) { - return; + private isAllowedExtension(fileName: string, allowedExtensions: string[]): boolean { + return allowedExtensions.some(ext => fileName.endsWith(ext)); } - if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID) { - const firstLine = readFirstLineSync(moduleInfo.entryFile); - if (!firstLine?.includes('use static')) { - return; - } + + private isValidAPIFile(fileName: string): boolean { + const pattern = new RegExp(`^@(${sdkConfigPrefix})\\..+\\.d\\.ets$`, 'i'); + return pattern.test(fileName); } - arktsconfig.addPathMappings({ - [moduleInfo.packageName]: [moduleInfo.moduleRootPath] - }); - } - - - private getDependencyKey(file: string, moduleInfo: ModuleInfo): string { - let unixFilePath: string = file.replace(/\\/g, '/'); - return moduleInfo.packageName + '/' + unixFilePath; - } + private buildDynamicKey( + baseName: string, + relativePath: string, + isExcludedDir: boolean, + separator: string = '.' + ): string { + return isExcludedDir + ? baseName + : (relativePath ? `${relativePath}${separator}${baseName}` : baseName); + } - private getDependenciesSection(moduleInfo: ModuleInfo, arktsconfig: ArkTSConfig): void { - let depModules: Map = moduleInfo.dynamicDepModuleInfos; - depModules.forEach((depModuleInfo: ModuleInfo) => { - if (!depModuleInfo.declFilesPath || !fs.existsSync(depModuleInfo.declFilesPath)) { - console.error(`Module ${moduleInfo.packageName} depends on dynamic module ${depModuleInfo.packageName}, but - decl file not found on path ${depModuleInfo.declFilesPath}`); - return; - } - - const declFilesObject = JSON.parse(fs.readFileSync(depModuleInfo.declFilesPath, 'utf-8')); - const files = declFilesObject.files; - - Object.keys(files).forEach((file: string) => { - const dependencyKey: string = this.getDependencyKey(file, depModuleInfo); - const depItem: DependencyItem = { - language: 'js', - path: files[file].declPath, - ohmUrl: files[file].ohmUrl + private processDynamicFile(ctx: DynamicFileContext): void { + const { + filePath, + fileName, + relativePath, + isExcludedDir, + dependencySection, + prefix = '' + } = ctx; + let separator = '.' + if (!this.isValidAPIFile(fileName)) { + separator = '/' + } + + const baseName = path.basename(fileName, '.d.ets'); + const normalizedRelativePath = relativePath.replace(/\//g, separator); + const key = this.buildDynamicKey(baseName, normalizedRelativePath, isExcludedDir, separator); + + dependencySection[prefix + key] = { + language: 'js', + path: filePath, + ohmUrl: getOhmurlByApi(baseName), + alias: [key] }; + } + + private processStaticAlias( + aliasName: string, + aliasConfig: AliasConfig, + arktsConfig: ArkTSConfig + ): void { + const declPath = getInteropFilePathByApi(aliasConfig.originalAPIName, this.dynamicSDKPaths); + if (!declPath) { + return; + } - arktsconfig.addDependency({ - name: dependencyKey, - item: depItem + arktsConfig.addPathMappings({ + [aliasName]: [declPath] }); + } - const absFilePath: string = path.resolve(depModuleInfo.moduleRootPath, file); - const entryFileWithoutExtension: string = changeFileExtension(depModuleInfo.entryFile, ''); + private processDynamicAlias( + aliasName: string, + aliasConfig: AliasConfig, + arktsConfig: ArkTSConfig + ): void { + const originalName = aliasConfig.originalAPIName; + const declPath = getInteropFilePathByApi(originalName, this.dynamicSDKPaths); - if (absFilePath === entryFileWithoutExtension) { - arktsconfig.addDependency({ - name: depModuleInfo.packageName, - item: depItem - }); + if (declPath === '') { + return; } - }); - }); - arktsconfig.addDependencies(this.systemDependenciesSection); - } - - public generateArkTSConfigFile( - moduleInfo: ModuleInfo, - enableDeclgenEts2Ts: boolean - ): void { - if (!moduleInfo.sourceRoots || moduleInfo.sourceRoots.length === 0) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_SOURCEROOTS_NOT_SET_FAIL, - 'SourceRoots not set from hvigor.' - ); - this.logger.printErrorAndExit(logData); - } - let arktsConfig: ArkTSConfig = new ArkTSConfig(moduleInfo, this.buildConfig.cachePath); - this.arktsconfigs.set(moduleInfo.packageName, arktsConfig); - this.getPathSection(moduleInfo, arktsConfig); - - if (!enableDeclgenEts2Ts) { - this.getDependenciesSection(moduleInfo, arktsConfig); - } - - this.processAlias(arktsConfig); - - if (moduleInfo.frameworkMode) { - arktsConfig.setUseEmptyPackage(moduleInfo.useEmptyPackage); - } - - ensurePathExists(moduleInfo.arktsConfigFile); - } - - private processAlias(arktsconfigs: ArkTSConfig): void { - const aliasForPkg = this.aliasConfig?.[arktsconfigs.getPackageName()]; - if (!aliasForPkg) { - return; - } - for (const [aliasName, aliasConfig] of Object.entries(aliasForPkg)) { - if (aliasConfig.isStatic) { - continue; - } - if (aliasConfig.originalAPIName.startsWith('@kit')) { - this.processStaticAlias(aliasName, aliasConfig, arktsconfigs); - } else { - this.processDynamicAlias(aliasName, aliasConfig, arktsconfigs); - } - } - } - - private traverseDependencies( - currentDir: string, - relativePath: string, - isExcludedDir: boolean, - dependencySection: Record, - prefix: string = '' - ): void { - const allowedExtensions = ['.d.ets']; - const items = fs.readdirSync(currentDir); - - for (const item of items) { - const itemPath = path.join(currentDir, item); - const stat = fs.statSync(itemPath); - - if (stat.isFile()) { - if (this.isAllowedExtension(item, allowedExtensions)) { - this.processDynamicFile({ - filePath: itemPath, - fileName: item, - relativePath, - isExcludedDir, - dependencySection, - prefix - }); + + if (!fs.existsSync(declPath)) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_INTEROP_SDK_NOT_FIND, + `Interop SDK File Not Exist: ${declPath}` + ) + ); } - continue; - } - - if (stat.isDirectory()) { - const isRuntimeAPI = path.basename(currentDir) === 'arkui' && item === 'runtime-api'; - const newRelativePath = isRuntimeAPI - ? '' - : (relativePath ? `${relativePath}/${item}` : item); - - this.traverseDependencies( - path.resolve(currentDir, item), - newRelativePath, - isExcludedDir || isRuntimeAPI, - dependencySection, - prefix - ); - } - } - } - - private isAllowedExtension(fileName: string, allowedExtensions: string[]): boolean { - return allowedExtensions.some(ext => fileName.endsWith(ext)); - } - - private isValidAPIFile(fileName: string): boolean { - const pattern = new RegExp(`^@(${sdkConfigPrefix})\\..+\\.d\\.ets$`, 'i'); - return pattern.test(fileName); - } - - private buildDynamicKey( - baseName: string, - relativePath: string, - isExcludedDir: boolean, - separator: string = '.' - ): string { - return isExcludedDir - ? baseName - : (relativePath ? `${relativePath}${separator}${baseName}` : baseName); - } - - - private processDynamicFile(ctx: DynamicFileContext): void { - const { - filePath, - fileName, - relativePath, - isExcludedDir, - dependencySection, - prefix = '' - } = ctx; - let separator = '.' - if (!this.isValidAPIFile(fileName)) { - separator = '/' - } - - const baseName = path.basename(fileName, '.d.ets'); - const normalizedRelativePath = relativePath.replace(/\//g, separator); - const key = this.buildDynamicKey(baseName, normalizedRelativePath, isExcludedDir, separator); - - dependencySection[prefix + key] = { - language: 'js', - path: filePath, - ohmUrl: getOhmurlByApi(baseName), - alias: [key] - }; - } - - private processStaticAlias( - aliasName: string, - aliasConfig: AliasConfig, - arktsConfig: ArkTSConfig - ): void { - const declPath = getInteropFilePathByApi(aliasConfig.originalAPIName, this.dynamicSDKPaths); - if (!declPath) { - return; - } - - arktsConfig.addPathMappings({ - [aliasName]: [declPath] - }); - } - - private processDynamicAlias( - aliasName: string, - aliasConfig: AliasConfig, - arktsConfig: ArkTSConfig - ): void { - const originalName = aliasConfig.originalAPIName; - const declPath = getInteropFilePathByApi(originalName, this.dynamicSDKPaths); - - if (declPath === '') { - return; - } - - if (!fs.existsSync(declPath)) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_INTEROP_SDK_NOT_FIND, - `Interop SDK File Not Exist: ${declPath}` - ); - this.logger.printErrorAndExit(logData); - } - - arktsConfig.addDependency({ - name: DYNAMIC_PREFIX + originalName, - item: { - language: 'js', - path: declPath, - ohmUrl: getOhmurlByApi(originalName), - alias: [aliasName] - } - }); - } - - private getAllFilesToPathSection( - moduleInfo: ModuleInfo, - arktsConfig: ArkTSConfig - ): void { - const moduleRoot = toUnixPath(moduleInfo.moduleRootPath) + '/'; - - for (const file of this.buildConfig.compileFiles) { - const unixFilePath = toUnixPath(file); - - if (!isSubPathOf(unixFilePath, moduleRoot)) { - continue; - } - - let relativePath = unixFilePath.substring(moduleRoot.length); - const keyWithoutExtension = relativePath.replace(/\.[^/.]+$/, ''); - - const pathKey = `${moduleInfo.packageName}/${keyWithoutExtension}`; - arktsConfig.addPathMappings({ [pathKey]: [file] }); - } - } - - private initPathInfo(): void { - this.generateSystemSdkPathSection(this.systemPathSection); - this.generateSystemSdkDependenciesSection(this.systemDependenciesSection); - if (this.buildConfig.paths) { - Object.entries(this.buildConfig.paths).map(([key, value]) => { - this.systemPathSection[key] = value - }); - } - } - - private generateSystemSdkDependenciesSection(dependenciesSection: Record): void { - this.dynamicSDKPaths.forEach(basePath => { - if(basePath.includes(KITS)){ - return; - } - if (!fs.existsSync(basePath)) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_ALIAS_MODULE_PATH_NOT_EXIST, - `alias module ${basePath} not exist.` - ); - this.logger.printErrorAndExit(logData); - } - if (basePath.includes(COMPONENT)) { - this.traverseDependencies(basePath, '', false, dependenciesSection, 'component/'); - } else { - this.traverseDependencies(basePath, '', false, dependenciesSection, DYNAMIC_PREFIX); - } - }); - } - - public getArktsConfigPackageName(packageName: string): ArkTSConfig | undefined { - return this.arktsconfigs.get(packageName); - } + + arktsConfig.addDependency({ + name: DYNAMIC_PREFIX + originalName, + item: { + language: 'js', + path: declPath, + ohmUrl: getOhmurlByApi(originalName), + alias: [aliasName] + } + }); + } + + private getAllFilesToPathSection( + moduleInfo: ModuleInfo, + arktsConfig: ArkTSConfig + ): void { + const moduleRoot = toUnixPath(moduleInfo.moduleRootPath) + '/'; + + for (const file of this.buildConfig.compileFiles) { + const unixFilePath = toUnixPath(file); + + if (!isSubPathOf(unixFilePath, moduleRoot)) { + continue; + } + + let relativePath = unixFilePath.substring(moduleRoot.length); + const keyWithoutExtension = relativePath.replace(/\.[^/.]+$/, ''); + + const pathKey = `${moduleInfo.packageName}/${keyWithoutExtension}`; + arktsConfig.addPathMappings({ [pathKey]: [file] }); + } + } + + private initPathInfo(): void { + this.generateSystemSdkPathSection(this.systemPathSection); + this.generateSystemSdkDependenciesSection(this.systemDependenciesSection); + if (this.buildConfig.paths) { + Object.entries(this.buildConfig.paths).map(([key, value]) => { + this.systemPathSection[key] = value + }); + } + } + + private generateSystemSdkDependenciesSection(dependenciesSection: Record): void { + this.dynamicSDKPaths.forEach(basePath => { + if (basePath.includes(KITS)) { + return; + } + if (!fs.existsSync(basePath)) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_ALIAS_MODULE_PATH_NOT_EXIST, + `alias module ${basePath} not exist.` + ) + ); + } + if (basePath.includes(COMPONENT)) { + this.traverseDependencies(basePath, '', false, dependenciesSection, 'component/'); + } else { + this.traverseDependencies(basePath, '', false, dependenciesSection, DYNAMIC_PREFIX); + } + }); + } + + public getArktsConfigByPackageName(packageName: string): ArkTSConfig | undefined { + return this.arktsconfigs.get(packageName); + } } diff --git a/ets2panda/driver/build_system/src/dependency_analyzer.ts b/ets2panda/driver/build_system/src/dependency_analyzer.ts new file mode 100644 index 0000000000000000000000000000000000000000..d34f11d4037dd290b17968a75bc28e58fb3bdf52 --- /dev/null +++ b/ets2panda/driver/build_system/src/dependency_analyzer.ts @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as os from 'os'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as child_process from 'child_process'; + +import { + ARKTSCONFIG_JSON_FILE, + DECL_ETS_SUFFIX, + ABC_SUFFIX, + DEP_ANALYZER_DIR, + DEP_ANALYZER_INPUT_FILE, + DEP_ANALYZER_OUTPUT_FILE, +} from './pre_define'; + +import { + changeFileExtension, + ensureDirExists, + isMac +} from './util/utils'; + +import { + BuildConfig, + ModuleInfo, + JobInfo +} from './types' + +import { + Logger, + LogDataFactory +} from './logger'; + +import { ErrorCode, DriverError } from './util/error'; + +import { ArkTSConfigGenerator } from './build/generate_arktsconfig'; + +import { computeHash } from './util/utils' + +export interface DependencyFileMap { + dependants: { + [filePath: string]: string[]; + }; + dependencies: { + [filePath: string]: string[]; + } +} + +export class DependencyAnalyzer { + + readonly logger: Logger; + readonly binPath: string; + readonly outputDir: string; + + constructor(buildConfig: BuildConfig) { + this.logger = Logger.getInstance(); + this.outputDir = path.join(buildConfig.cachePath, DEP_ANALYZER_DIR); + ensureDirExists(this.outputDir); + this.binPath = buildConfig.dependencyAnalyzerPath!; + } + + private generateMergedArktsConfig(modules: Array, outputPath: string): void { + + let mainModule = modules.find((module) => module.isMainModule)! + let resArkTSConfig = ArkTSConfigGenerator.getInstance().getArktsConfigByPackageName(mainModule.packageName)! + modules.forEach((module) => { + if (module.isMainModule) { + return; + } + resArkTSConfig.mergeArktsConfig( + ArkTSConfigGenerator.getInstance().getArktsConfigByPackageName(module.packageName)! + ) + }); + + fs.writeFileSync(outputPath, JSON.stringify(resArkTSConfig.object, null, 2)); + } + + private formExecCmd(input: string, output: string, config: string): string { + let cmd = [this.binPath]; + cmd.push('@' + '"' + input + '"'); + cmd.push('--arktsconfig=' + '"' + config + '"'); + cmd.push('--output=' + '"' + output + '"'); + let res: string = cmd.join(' '); + if (isMac()) { + const loadLibrary = 'DYLD_LIBRARY_PATH=' + '"' + process.env.DYLD_LIBRARY_PATH + '"'; + res = loadLibrary + ' ' + res; + } + return res; + } + + private filterDependencyMap( + dependencyMap: DependencyFileMap, + entryFiles: Set + ): DependencyFileMap { + let resDependencyMap: DependencyFileMap = { + dependants: {}, + dependencies: {} + } + + // Filter files from external api + // We do not consider them in dependency analysis + // Filter files from arkTs 1.1 and arkTs hybrid + Object.entries(dependencyMap.dependencies).forEach(([file, dependencies]: [string, string[]]) => { + if (!entryFiles.has(file)) { + return + } + debugger; + resDependencyMap.dependencies[file] = [...dependencies].filter((dependency: string) => { + return entryFiles.has(dependency) + }) + }) + Object.entries(dependencyMap.dependants).forEach(([file, dependants]: [string, string[]]) => { + if (!entryFiles.has(file)) { + return + } + resDependencyMap.dependants[file] = [...dependants].filter((dependant: string) => { + return entryFiles.has(dependant) + }) + }) + + this.logger.printInfo(`filtered dependency map: ${JSON.stringify(resDependencyMap, null, 1)}`) + return resDependencyMap; + } + + private generateDependencyMap( + entryFiles: Set, + modules: Array + ): DependencyFileMap { + const inputFile: string = path.join(this.outputDir, DEP_ANALYZER_INPUT_FILE); + const outputFile: string = path.join(this.outputDir, DEP_ANALYZER_OUTPUT_FILE); + const arktsConfigPath: string = path.join(this.outputDir, ARKTSCONFIG_JSON_FILE); + + + let depAnalyzerInputFileContent: string = Array.from(entryFiles).join(os.EOL); + fs.writeFileSync(inputFile, depAnalyzerInputFileContent); + + this.generateMergedArktsConfig(modules, arktsConfigPath) + + let execCmd = this.formExecCmd(inputFile, outputFile, arktsConfigPath) + + try { + child_process.execSync(execCmd, { + stdio: 'pipe', + encoding: 'utf-8' + }); + } catch (error) { + if (error instanceof Error) { + const execError = error as child_process.ExecException; + let fullErrorMessage = execError.message; + if (execError.stderr) { + fullErrorMessage += `\nStdErr: ${execError.stderr}`; + } + if (execError.stdout) { + fullErrorMessage += `\nStdOutput: ${execError.stdout}`; + } + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DEPENDENCY_ANALYZE_FAIL, + 'Failed to analyze files dependency.', + fullErrorMessage + ) + ) + } + } + const fullDependencyMap: DependencyFileMap = JSON.parse(fs.readFileSync(outputFile, 'utf-8')); + this.logger.printInfo(`fill dependency map: ${JSON.stringify(fullDependencyMap, null, 1)}`) + + return this.filterDependencyMap(fullDependencyMap, entryFiles); + } + + public findStronglyConnectedComponents(fileMap: DependencyFileMap): Map> { + const adjacencyList: Record = {}; + const reverseAdjacencyList: Record = {}; + const allNodes = new Set(); + + for (const node in fileMap.dependencies) { + allNodes.add(node); + fileMap.dependencies[node].forEach(dep => allNodes.add(dep)); + } + for (const node in fileMap.dependants) { + allNodes.add(node); + fileMap.dependants[node].forEach(dep => allNodes.add(dep)); + } + + allNodes.forEach(node => { + adjacencyList[node] = fileMap.dependencies[node] || []; + reverseAdjacencyList[node] = fileMap.dependants[node] || []; + }); + + const visited = new Set(); + const order: string[] = []; + + function dfs(node: string): void { + visited.add(node); + for (const neighbor of adjacencyList[node]) { + if (!visited.has(neighbor)) { + dfs(neighbor); + } + } + order.push(node); + } + + allNodes.forEach(node => { + if (!visited.has(node)) { + dfs(node); + } + }); + + visited.clear(); + const components = new Map>(); + + function reverseDfs(node: string, component: Set): void { + visited.add(node); + component.add(node); + for (const neighbor of reverseAdjacencyList[node]) { + if (!visited.has(neighbor)) { + reverseDfs(neighbor, component); + } + } + } + + for (let i = order.length - 1; i >= 0; i--) { + const node = order[i]; + if (!visited.has(node)) { + const component = new Set(); + reverseDfs(node, component); + if (component.size > 1) { + const sortedFiles = Array.from(component).sort(); + const componentId = computeHash(sortedFiles.join('|')); + components.set(componentId, component); + } + } + } + + this.logger.printInfo(`Found components: ${JSON.stringify([...components], null, 1)}`) + return components; + } + + private verifyModuleCyclicDependency(files: string[], fileToModule: Map) { + const modules = files.map((file: string) => { + const module = fileToModule.get(file) + if (!module) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DEPENDENCY_ANALYZE_FAIL, + `Failed to find module for file ${file}.`, + ) + ) + } + return module + }) + const set = new Set(modules.map((module: ModuleInfo) => module.packageName)) + if (set.size > 1) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DEPENDENCY_ANALYZE_FAIL, + 'Cyclic dependency between modules found.', + "Module cycle: " + Array.from(set).join(" <---> ") + ) + ) + } + } + + private createCycleJob( + jobs: Record, + cycle: Set, + cycleId: string, + dependencyMap: DependencyFileMap, + fileToCycle: Map + ) { + const cycleFileList = Array.from(cycle) + const cycleDependencies = cycleFileList.map( + (file: string) => this.collectJobDependencies(file, dependencyMap, fileToCycle) + ).reduce((acc: Set, curr: Set) => new Set([...acc, ...curr])) + + const cycleDependants: Set = cycleFileList.map( + (file: string) => this.collectJobDependants(file, dependencyMap, fileToCycle) + ).reduce((acc: Set, curr: Set) => new Set([...acc, ...curr])) + + jobs[cycleId] = { + id: cycleId, + fileList: cycleFileList, + isAbcJob: true, + jobDependencies: Array.from(cycleDependencies), + jobDependants: Array.from(cycleDependants) + } + this.logger.printInfo(`Created job for cycle: ${JSON.stringify(jobs[cycleId], null, 1)}`) + } + + public collectJobs( + entryFiles: Set, + fileToModule: Map, + moduleInfos: Map + ): Record { + let jobs: Record = {}; + + const dependencyMap: DependencyFileMap = + this.generateDependencyMap(entryFiles, Array.from(moduleInfos.values())); + + Object.keys(dependencyMap.dependants).forEach((file: string) => { + if (!(file in dependencyMap.dependencies)) { + dependencyMap.dependencies[file] = []; + } + }); + + const stronglyConnectedComponents: Map> = this.findStronglyConnectedComponents(dependencyMap); + const fileToCycleMap: Map = new Map(); + + // First iterate to check Module Cyclic Dependencies and fill fileToCycleMap + stronglyConnectedComponents.forEach((component: Set, componentId: string) => { + this.verifyModuleCyclicDependency(Array.from(component), fileToModule); + component.forEach((file) => { + fileToCycleMap.set(file, componentId); + }); + }); + this.logger.printInfo(`Found stronglyConnectedComponents: ${JSON.stringify([...stronglyConnectedComponents], null, 1)}`) + this.logger.printInfo(`fileToCycleMap: ${JSON.stringify([...fileToCycleMap], null, 1)}`) + + // Second iterate to create jobs for compiling cycles + stronglyConnectedComponents.forEach((component: Set, componentId: string) => { + this.createCycleJob(jobs, component, componentId, dependencyMap, fileToCycleMap) + }); + + entryFiles.forEach((file: string) => { + const isInCycle: boolean = fileToCycleMap.has(file) + const jobDependencies: Set = this.collectJobDependencies(file, dependencyMap, fileToCycleMap); + const jobDependants: Set = this.collectJobDependants(file, dependencyMap, fileToCycleMap); + + if (isInCycle) { + return; + } + + const declJobId: string = this.getDeclJobId(file) + const abcJobId: string = this.getAbcJobId(file) + + jobs[declJobId] = { + id: declJobId, + fileList: [file], + isAbcJob: false, + jobDependencies: [...jobDependencies], + jobDependants: [...jobDependants, abcJobId] + }; + this.logger.printInfo(`Created Decl job: ${JSON.stringify(jobs[declJobId], null, 1)}`) + + jobs[abcJobId] = { + id: abcJobId, + isAbcJob: true, + fileList: [file], + jobDependencies: [declJobId], + jobDependants: [] + } + this.logger.printInfo(`Created Abc job: ${JSON.stringify(jobs[abcJobId], null, 1)}`) + }); + + this.logger.printInfo(`Collected jobs: ${JSON.stringify(jobs, null, 1)}`) + return jobs; + } + + private collectJobDependencies( + file: string, + dependencyMap: DependencyFileMap, + fileToCycleMap: Map + ): Set { + const fileDependencies = dependencyMap.dependencies[file] + + let dependencySet: Set = new Set(); + fileDependencies.forEach((dependency) => { + if (!fileToCycleMap.has(dependency)) { + dependencySet.add(this.getDeclJobId(dependency)); + return; + } + const dependencyCycle: string = fileToCycleMap.get(dependency)! + + if (fileToCycleMap.has(file)) { + const fileCycle: string = fileToCycleMap.get(file)! + if (fileCycle == dependencyCycle) { + return; + } + } + dependencySet.add(dependencyCycle); + }); + return dependencySet; + } + + private collectJobDependants( + file: string, + dependencyMap: DependencyFileMap, + fileToCycleMap: Map + ): Set { + const fileDependants = dependencyMap.dependants[file] + let dependantSet: Set = new Set(); + + fileDependants.forEach((dependant) => { + if (!fileToCycleMap.has(dependant)) { + dependantSet.add(this.getDeclJobId(dependant)); + return; + } + const dependantCycle: string = fileToCycleMap.get(dependant)! + + if (fileToCycleMap.has(file)) { + const fileCycle: string = fileToCycleMap.get(file)! + if (fileCycle == dependantCycle) { + return; + } + } + dependantSet.add(dependantCycle) + }); + return dependantSet; + } + + private getAbcJobId(file: string): string { + return changeFileExtension(file, ABC_SUFFIX); + } + + private getDeclJobId(file: string): string { + return changeFileExtension(file, DECL_ETS_SUFFIX); + } +} diff --git a/ets2panda/driver/build_system/src/entry.ts b/ets2panda/driver/build_system/src/entry.ts index b590749038c8953a587b3c389edfca42c9c08019..8f974e5bfc7d747e6b620fd5a6ba37626cb78934 100644 --- a/ets2panda/driver/build_system/src/entry.ts +++ b/ets2panda/driver/build_system/src/entry.ts @@ -16,52 +16,82 @@ import * as fs from 'fs'; import * as path from 'path'; -import { processBuildConfig } from './init/process_build_config'; +import { initBuildConfig } from './init/process_build_config'; import { BuildMode } from './build/build_mode'; -import { Logger } from './logger'; +import { Logger, LoggerGetter, getConsoleLogger } from './logger'; +import { DriverError } from './util/error'; import { ArkTSConfigGenerator } from './build/generate_arktsconfig'; import { PluginDriver } from './plugins/plugins_driver'; import { BuildConfig, BUILD_TYPE } from './types'; import { BuildFrameworkMode } from './build/build_framework_mode'; -import { cleanKoalaModule } from './init/init_koala_modules'; +import { initKoalaModules, cleanKoalaModule } from './init/init_koala_modules'; -export async function build(projectConfig: BuildConfig): Promise { - let logger: Logger = Logger.getInstance(projectConfig); - let buildConfig: BuildConfig = processBuildConfig(projectConfig); - - buildConfig.entryFiles = buildConfig.compileFiles; - if (projectConfig.frameworkMode === true) { - let buildframeworkMode: BuildFrameworkMode = new BuildFrameworkMode(buildConfig); - await buildframeworkMode.run(); - if (logger.hasErrors()) { - clean(); - process.exit(1); +// NOTE: to be refactored +function backwardCompatibleBuildConfigStub(projectConfig: BuildConfig, loggerGetter?: LoggerGetter) { + if (projectConfig.dependentModuleList) { + projectConfig.dependencyModuleList = [...projectConfig.dependentModuleList] } - } else if (projectConfig.enableDeclgenEts2Ts === true) { - let buildMode: BuildMode = new BuildMode(buildConfig); - await buildMode.generateDeclaration(); - } else if (projectConfig.buildType === BUILD_TYPE.BUILD) { - let buildMode: BuildMode = new BuildMode(buildConfig); - await buildMode.run(); - } - clean(); + const hvigorLogger = projectConfig.getHvigorConsoleLogger as LoggerGetter + delete projectConfig.getHvigorConsoleLogger + Logger.getInstance(hvigorLogger ?? (loggerGetter ?? getConsoleLogger)); +} + +export async function build(projectConfig: BuildConfig, loggerGetter?: LoggerGetter): Promise { + backwardCompatibleBuildConfigStub(projectConfig, loggerGetter) + + let logger: Logger = Logger.getInstance(); + logger.printInfo(`Project config: ${JSON.stringify(projectConfig, null, 1)}`) + + let buildConfig: BuildConfig = initBuildConfig(projectConfig); + logger.printInfo(`Resulting buildConfig: ${JSON.stringify(buildConfig, null, 1)}`) + + try { + if (projectConfig.frameworkMode === true) { + let buildframeworkMode: BuildFrameworkMode = new BuildFrameworkMode(buildConfig); + await buildframeworkMode.runSimultaneous(); + } else { + let buildMode: BuildMode = new BuildMode(buildConfig); + buildMode.koalaModule = initKoalaModules(buildConfig); + + if (projectConfig.enableDeclgenEts2Ts === true) { + logger.printInfo("generate Declaration") + await buildMode.generateDeclaration(); + } else if (projectConfig.buildType === BUILD_TYPE.BUILD) { + logger.printInfo("just build") + await buildMode.run(); + } + } + } catch(error) { + if (error instanceof DriverError) { + Logger.getInstance().printError((error as DriverError).logData); + return + } else { + Logger.getInstance().printWarn("Error occured") + Logger.getInstance().printWarn("Error is not DriverError") + throw error; + } + } finally { + clean(); + } } function clean(): void { - Logger.destroyInstance(); - ArkTSConfigGenerator.destroyInstance(); - PluginDriver.destroyInstance(); - cleanKoalaModule(); + Logger.destroyInstance(); + ArkTSConfigGenerator.destroyInstance(); + PluginDriver.destroyInstance(); + cleanKoalaModule(); } function main(): void { - const buildConfigPath: string = path.resolve(process.argv[2]); - const projectConfig: BuildConfig = JSON.parse(fs.readFileSync(buildConfigPath, 'utf-8')); + console.log("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV") + debugger; + const buildConfigPath: string = path.resolve(process.argv[2]); + const projectConfig: BuildConfig = JSON.parse(fs.readFileSync(buildConfigPath, 'utf-8')); - build(projectConfig); + build(projectConfig) } if (require.main === module) { - main(); + main(); } diff --git a/ets2panda/driver/build_system/src/init/init_koala_modules.ts b/ets2panda/driver/build_system/src/init/init_koala_modules.ts index 9c3808f3d1a89522958e24da24f3a1795c1543c6..447ae26f33accee25c6cbb84960be1b3cc9dcffa 100644 --- a/ets2panda/driver/build_system/src/init/init_koala_modules.ts +++ b/ets2panda/driver/build_system/src/init/init_koala_modules.ts @@ -22,43 +22,38 @@ import path from "path" let koalaModule: any; export function initKoalaModules(buildConfig: BuildConfig) { - if (!koalaModule) { - const koalaWrapperPath = - process.env.KOALA_WRAPPER_PATH ?? - path.resolve(buildConfig.buildSdkPath, KOALA_WRAPPER_PATH_FROM_SDK); + if (!koalaModule) { + const koalaWrapperPath = + process.env.KOALA_WRAPPER_PATH ?? + path.resolve(buildConfig.buildSdkPath, KOALA_WRAPPER_PATH_FROM_SDK); - koalaModule = require(koalaWrapperPath); - koalaModule.arktsGlobal.es2panda._SetUpSoPath(buildConfig.pandaSdkPath); - } - - Object.assign(buildConfig, { - arkts: koalaModule.arkts, - arktsGlobal: koalaModule.arktsGlobal, - }); + koalaModule = require(koalaWrapperPath); + koalaModule.arktsGlobal.es2panda._SetUpSoPath(buildConfig.pandaSdkPath); + } - return koalaModule; + return koalaModule; } export function initKoalaPlugins(projectConfig: BuildConfig): void { - const uiPluginPath = path.resolve(projectConfig.buildSdkPath, UI_PLUGIN_PATH_FROM_SDK); - const memoPluginPath = path.resolve(projectConfig.buildSdkPath, MEMO_PLUGIN_PATH_FROM_SDK); + const uiPluginPath = path.resolve(projectConfig.buildSdkPath, UI_PLUGIN_PATH_FROM_SDK); + const memoPluginPath = path.resolve(projectConfig.buildSdkPath, MEMO_PLUGIN_PATH_FROM_SDK); // TODO: need change in hvigor if (process.env.USE_KOALA_UI_PLUGIN) { - projectConfig.plugins['ArkUI'] = uiPluginPath + projectConfig.plugins['ArkUI'] = uiPluginPath } if (process.env.USE_KOALA_MEMO_PLUGIN) { - projectConfig.plugins['ArkUI-Memo'] = memoPluginPath + projectConfig.plugins['ArkUI-Memo'] = memoPluginPath } } export function cleanKoalaModule() { - koalaModule = null; + koalaModule = null; } // for ut export function getKoalaModule() { - return koalaModule; -} \ No newline at end of file + return koalaModule; +} diff --git a/ets2panda/driver/build_system/src/init/process_build_config.ts b/ets2panda/driver/build_system/src/init/process_build_config.ts index 09c0d539d836ffb15e3bd0b4ca1d4efe5e16c96d..2ec3c47bb36290ba3ad63cdd22efba5d4a29888c 100644 --- a/ets2panda/driver/build_system/src/init/process_build_config.ts +++ b/ets2panda/driver/build_system/src/init/process_build_config.ts @@ -35,18 +35,17 @@ import { LogDataFactory, Logger } from '../logger'; -import { ErrorCode } from '../error_code'; +import { ErrorCode } from '../util/error'; import { BuildConfig, BUILD_MODE, AliasConfig } from '../types'; -import { initKoalaModules } from './init_koala_modules'; -export function processBuildConfig(projectConfig: BuildConfig): BuildConfig { - let buildConfig: BuildConfig = { +export function initBuildConfig(projectConfig: BuildConfig): BuildConfig { + let buildConfig: BuildConfig = { ...projectConfig, - isBuildConfigModified: false + isBuildConfigModified: false }; let buildSdkPath: string = buildConfig.buildSdkPath as string; buildConfig.pandaSdkPath = buildConfig.pandaSdkPath ?? path.resolve(buildSdkPath, PANDA_SDK_PATH_FROM_SDK); @@ -58,7 +57,6 @@ export function processBuildConfig(projectConfig: BuildConfig): BuildConfig { checkCacheProjectConfig(buildConfig); initPlatformSpecificConfig(buildConfig); initBuildEnv(buildConfig); - initKoalaModules(buildConfig); PluginDriver.getInstance().initPlugins(buildConfig); initAliasConfig(buildConfig); initInteropSDKInfo(buildConfig); @@ -119,7 +117,7 @@ function initPlatformSpecificConfig(buildConfig: BuildConfig): void { if (!buildConfig.frameworkMode && !buildConfig.enableDeclgenEts2Ts && !fs.existsSync(buildConfig.dependencyAnalyzerPath as string)) { const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_Dependency_Analyzer_NOT_FOUND_FAIL, + ErrorCode.BUILDSYSTEM_DEPENDENCY_ANALYZER_NOT_FOUND_FAIL, 'Dependency_analyzer not found in path.', '', buildConfig.dependencyAnalyzerPath as string @@ -235,4 +233,4 @@ export function getEs2pandaPath(buildConfig:BuildConfig):string{ es2pandaPath = path.join(pandaSdkPath, 'bin', 'es2panda'); } return es2pandaPath; -} \ No newline at end of file +} diff --git a/ets2panda/driver/build_system/src/logger.ts b/ets2panda/driver/build_system/src/logger.ts index 77a05dea9174259d13748e41e3a40cf5640a0aad..3051262a4d192267938950e22df7ff3d49be1f7e 100644 --- a/ets2panda/driver/build_system/src/logger.ts +++ b/ets2panda/driver/build_system/src/logger.ts @@ -13,207 +13,202 @@ * limitations under the License. */ -import { BuildConfig } from './types'; -import { - ErrorCode, - SubsystemCode -} from './error_code'; +import { ErrorCode } from './util/error' -export class Logger { - private static instance: Logger | undefined; - private loggerMap: { [key in SubsystemCode]?: ILogger }; - private hasErrorOccurred: boolean = false; - - private constructor(projectConfig: BuildConfig) { - if (typeof projectConfig.getHvigorConsoleLogger !== 'function') { - projectConfig.getHvigorConsoleLogger = getConsoleLogger; - } - let getHvigorConsoleLogger = projectConfig.getHvigorConsoleLogger as Function; - this.loggerMap = {}; - this.loggerMap[SubsystemCode.BUILDSYSTEM] = getHvigorConsoleLogger(SubsystemCode.BUILDSYSTEM); - this.loggerMap[SubsystemCode.ES2PANDA] = getHvigorConsoleLogger(SubsystemCode.ES2PANDA); - } - - public static getInstance(projectConfig?: BuildConfig): Logger { - if (!Logger.instance) { - if (!projectConfig) { - throw new Error('projectConfig is required for the first instantiation.'); - } - Logger.instance = new Logger(projectConfig); - } - return Logger.instance; - } - - public static destroyInstance(): void { - Logger.instance = undefined; - } - - public printInfo(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void { - const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode); - logger.printInfo(message); - } - - public printWarn(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void { - const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode); - logger.printWarn(message); - } - - public printDebug(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void { - const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode); - logger.printDebug(message); - } - - public printError(error: LogData): void { - this.hasErrorOccurred = true; - const logger: ILogger = this.getLoggerFromErrorCode(error.code); - logger.printError(error); - } - - public printErrorAndExit(error: LogData): void { - this.hasErrorOccurred = true; - const logger: ILogger = this.getLoggerFromErrorCode(error.code); - logger.printErrorAndExit(error); - } - - private isValidErrorCode(errorCode: ErrorCode): boolean { - return /^\d{8}$/.test(errorCode); - } - - private getLoggerFromErrorCode(errorCode: ErrorCode): ILogger { - if (!this.isValidErrorCode(errorCode)) { - throw new Error('Invalid errorCode.'); - } - const subsystemCode = errorCode.slice(0, 3) as SubsystemCode; - const logger = this.getLoggerFromSubsystemCode(subsystemCode); - return logger; - } - - private getLoggerFromSubsystemCode(subsystemCode: SubsystemCode): ILogger { - if (!this.loggerMap[subsystemCode]) { - throw new Error('Invalid subsystemCode.'); - } - return this.loggerMap[subsystemCode]; - } - - public hasErrors(): boolean { - return this.hasErrorOccurred; - } - - public resetErrorFlag(): void { - this.hasErrorOccurred = false; - } +export enum SubsystemCode { + BUILDSYSTEM = '114', + ES2PANDA = '115', } -interface ILogger { - printInfo(message: string): void; - printWarn(message: string): void; - printDebug(message: string): void; - printError(error: LogData): void; - printErrorAndExit(error: LogData): void; +export interface ILogger { + printInfo(message: string): void; + printWarn(message: string): void; + printDebug(message: string): void; + printError(error: LogData): void; + printErrorAndExit(error: LogData): void; } -export class LogDataFactory { +export type LoggerGetter = (code: SubsystemCode) => ILogger; + +export class Logger { + private static instance?: Logger; + private loggerMap: { [key in SubsystemCode]?: ILogger }; + private hasErrorOccurred: boolean = false; + + private constructor(loggerGetter: LoggerGetter) { + this.loggerMap = {}; + this.loggerMap[SubsystemCode.BUILDSYSTEM] = loggerGetter(SubsystemCode.BUILDSYSTEM); + this.loggerMap[SubsystemCode.ES2PANDA] = loggerGetter(SubsystemCode.ES2PANDA); + } + + public static getInstance(loggerGetter?: LoggerGetter): Logger { + if (!Logger.instance) { + if (!loggerGetter) { + throw new Error('loggerGetter is required for the first instantiation.'); + } + Logger.instance = new Logger(loggerGetter); + } + return Logger.instance; + } + + public static destroyInstance(): void { + Logger.instance = undefined; + } + + public printInfo(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void { + const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode); + logger.printInfo(message); + } + + public printWarn(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void { + const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode); + logger.printWarn(message); + } + + public printDebug(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void { + const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode); + logger.printDebug(message); + } - static newInstance( - code: ErrorCode, - description: string, - cause: string = '', - position: string = '', - solutions: string[] = [], - moreInfo?: Object - ): LogData { - const data: LogData = new LogData(code, description, cause, position, solutions, moreInfo); - return data; - } + public printError(error: LogData): void { + this.hasErrorOccurred = true; + const logger: ILogger = this.getLoggerFromErrorCode(error.code); + logger.printError(error); + } + + public printErrorAndExit(error: LogData): void { + this.hasErrorOccurred = true; + const logger: ILogger = this.getLoggerFromErrorCode(error.code); + logger.printErrorAndExit(error); + } + + protected isValidErrorCode(errorCode: ErrorCode): boolean { + return /^\d{8}$/.test(errorCode); + } + + protected getLoggerFromErrorCode(errorCode: ErrorCode): ILogger { + if (!this.isValidErrorCode(errorCode)) { + throw new Error('Invalid errorCode.'); + } + const subsystemCode = errorCode.slice(0, 3) as SubsystemCode; + const logger = this.getLoggerFromSubsystemCode(subsystemCode); + return logger; + } + + protected getLoggerFromSubsystemCode(subsystemCode: SubsystemCode): ILogger { + if (!this.loggerMap[subsystemCode]) { + throw new Error('Invalid subsystemCode.'); + } + return this.loggerMap[subsystemCode]; + } + + public hasErrors(): boolean { + return this.hasErrorOccurred; + } + + public resetErrorFlag(): void { + this.hasErrorOccurred = false; + } +} + +export class LogDataFactory { + static newInstance( + code: ErrorCode, + description: string, + cause: string = '', + position: string = '', + solutions: string[] = [], + moreInfo?: Object + ): LogData { + const data: LogData = new LogData(code, description, cause, position, solutions, moreInfo); + return data; + } } export class LogData { + code: ErrorCode; + description: string; + cause: string; + position: string; + solutions: string[]; + moreInfo?: Object; + + constructor( + code: ErrorCode, + description: string, + cause: string, + position: string, + solutions: string[], + moreInfo?: Object + ) { + this.code = code; + this.description = description; + this.cause = cause; + this.position = position; + this.solutions = solutions; + this.moreInfo = moreInfo; + } - code: ErrorCode; - description: string; - cause: string; - position: string; - solutions: string[]; - moreInfo?: Object; - - constructor( - code: ErrorCode, - description: string, - cause: string = '', - position: string = '', - solutions: string[], - moreInfo?: Object - ) { - this.code = code; - this.description = description; - this.cause = cause; - this.position = position; - this.solutions = solutions; - if (moreInfo) { - this.moreInfo = moreInfo; - } - } - - toString(): string { - let errorString = `ERROR Code: ${this.code} ${this.description}\n`; - - if (this.cause || this.position) { - errorString += `Error Message: ${this.cause}`; - if (this.position) { - errorString += ` ${this.position}`; - } - errorString += '\n\n'; - } - - if (this.solutions.length > 0 && this.solutions[0] !== '') { - errorString += `* Try the following: \n${this.solutions.map(str => ` > ${str}`).join('\n')}\n`; - } - - if (this.moreInfo) { - errorString += `\nMore Info:\n`; - for (const [key, value] of Object.entries(this.moreInfo)) { - errorString += ` - ${key.toUpperCase()}: ${value}\n`; - } - } - - return errorString; - } + toString(): string { + let errorString = `ERROR Code: ${this.code} ${this.description}\n`; + + if (this.cause || this.position) { + errorString += `Error Message: ${this.cause}\n`; + if (this.position) { + errorString += `Position: ${this.position}\n`; + } + errorString += '\n\n'; + } + + if (this.solutions.length > 0 && this.solutions[0] !== '') { + errorString += `* Try the following: \n${this.solutions.map(str => ` > ${str}`).join('\n')}\n`; + } + + if (this.moreInfo) { + errorString += `\nMore Info:\n`; + for (const [key, value] of Object.entries(this.moreInfo)) { + errorString += ` - ${key.toUpperCase()}: ${value}\n`; + } + } + + return errorString; + } } class ConsoleLogger { - private static instances: { [key: string]: ConsoleLogger } = {}; + private static instances: { [key: string]: ConsoleLogger } = {}; - private constructor() {} + private constructor() { } - public printInfo(message: string): void { - console.info(message); - } + public printInfo(message: string): void { + console.info("[INFO]", message); + } - public printWarn(message: string): void { - console.warn(message); - } + public printWarn(message: string): void { + console.warn("[WARN]", message); + } - public printDebug(message: string): void { - console.debug(message); - } + public printDebug(message: string): void { + console.debug("[DEBUG]", message); + } - public printError(error: LogData): void { - console.error(error.toString()); - } + public printError(error: LogData): void { + console.error("[ERROR]", error.toString()); + } - public printErrorAndExit(error: LogData): void { - console.error(error.toString()); - process.exit(1); - } + public printErrorAndExit(error: LogData): void { + console.error(error.toString()); + process.exit(1); + } - public static createLogger(subsystemCode: string): ConsoleLogger { - if (!ConsoleLogger.instances[subsystemCode]) { - ConsoleLogger.instances[subsystemCode] = new ConsoleLogger(); + public static createLogger(subsystemCode: string): ConsoleLogger { + if (!ConsoleLogger.instances[subsystemCode]) { + ConsoleLogger.instances[subsystemCode] = new ConsoleLogger(); + } + return ConsoleLogger.instances[subsystemCode]; } - return ConsoleLogger.instances[subsystemCode]; - } } -function getConsoleLogger(subsystemCode: string): ConsoleLogger { - return ConsoleLogger.createLogger(subsystemCode); +export function getConsoleLogger(subsystemCode: string): ConsoleLogger { + return ConsoleLogger.createLogger(subsystemCode); } diff --git a/ets2panda/driver/build_system/src/plugins/FileManager.ts b/ets2panda/driver/build_system/src/plugins/FileManager.ts index 39b93e8f7179d1f661a4e7fa7f7ba394b643e297..9c81e34a61cf956960612566d277fd7d4374d0f1 100644 --- a/ets2panda/driver/build_system/src/plugins/FileManager.ts +++ b/ets2panda/driver/build_system/src/plugins/FileManager.ts @@ -14,7 +14,7 @@ */ import * as path from 'path'; -import { BuildConfig, DependentModuleConfig } from '../types'; +import { BuildConfig, DependencyModuleConfig } from '../types'; import { toUnixPath, readFirstLineSync @@ -23,7 +23,7 @@ import { ETS_1_1, ETS_1_1_INTEROP, LANGUAGE_VERSION } from '../pre_define'; export class FileManager { private static instance: FileManager | undefined = undefined; - static arkTSModuleMap: Map = new Map(); + static arkTSModuleMap: Map = new Map(); static staticApiPath: Set = new Set(); static dynamicApiPath: Set = new Set(); static buildConfig: BuildConfig; @@ -31,7 +31,7 @@ export class FileManager { static init(buildConfig: BuildConfig): void { if (FileManager.instance === undefined) { FileManager.instance = new FileManager(); - FileManager.initLanguageVersionFromDependentModuleMap(buildConfig.dependentModuleList); + FileManager.initLanguageVersionFromDependencyModuleMap(buildConfig.dependencyModuleList); FileManager.initSDK(new Set(buildConfig.externalApiPaths), buildConfig.buildSdkPath); FileManager.buildConfig = buildConfig; } @@ -61,12 +61,12 @@ export class FileManager { FileManager.dynamicApiPath.add(toUnixPath(path.resolve(etsPath, ETS_1_1_INTEROP))); } - private static initLanguageVersionFromDependentModuleMap( - dependentModuleList: DependentModuleConfig[] + private static initLanguageVersionFromDependencyModuleMap( + dependencyModuleList: DependencyModuleConfig[] ): void { - const convertedMap = new Map(); - dependentModuleList.forEach(module => { - const convertedModule: DependentModuleConfig = { + const convertedMap = new Map(); + dependencyModuleList.forEach(module => { + const convertedModule: DependencyModuleConfig = { ...module, modulePath: toUnixPath(module.modulePath), declgenV1OutPath: module.declgenV1OutPath ? toUnixPath(module.declgenV1OutPath) : undefined, @@ -95,7 +95,7 @@ export class FileManager { return LANGUAGE_VERSION.ARKTS_1_1; } } - if (FileManager.buildConfig.compileFiles?.includes(filePath)) { + if (FileManager.buildConfig.compileFiles.includes(filePath)) { return LANGUAGE_VERSION.ARKTS_1_2; } for (const [pkgName, moduleInfo] of FileManager.arkTSModuleMap) { @@ -115,4 +115,4 @@ export class FileManager { } return LANGUAGE_VERSION.ARKTS_1_1; } -} \ No newline at end of file +} diff --git a/ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts b/ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts index dfbf9fd788916883ae21d34e53875524b88b644f..0f31e592329ed64e46ccbb8d6dde416c97cb6355 100644 --- a/ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts +++ b/ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts @@ -21,7 +21,7 @@ import { Logger, LogDataFactory } from '../logger'; -import { ErrorCode } from '../error_code'; +import { ErrorCode } from '../util/error'; import { DYNAMIC_PREFIX, KIT_CONFIGS_PATH_FROM_SDK, diff --git a/ets2panda/driver/build_system/src/plugins/plugins_driver.ts b/ets2panda/driver/build_system/src/plugins/plugins_driver.ts index 4af2ec6aad84af74ab156975ba850ee4dc42de2a..900bd5db31d279fb8ff2f5e8b14ccfbd4f7ed68c 100644 --- a/ets2panda/driver/build_system/src/plugins/plugins_driver.ts +++ b/ets2panda/driver/build_system/src/plugins/plugins_driver.ts @@ -14,242 +14,243 @@ */ import { - Logger, - LogData, - LogDataFactory + Logger, + LogData, + LogDataFactory } from '../logger'; import { BuildConfig } from '../types'; -import { ErrorCode } from '../error_code'; +import { ErrorCode, DriverError } from '../util/error'; import { FileManager } from './FileManager'; import { initKoalaPlugins } from '../init/init_koala_modules'; export enum PluginHook { - NEW = 'afterNew', - PARSED = 'parsed', - SCOPE_INITED = 'scopeInited', - CHECKED = 'checked', - LOWERED = 'lowered', - ASM_GENERATED = 'asmGenerated', - BIN_GENERATED = 'binGenerated', - CLEAN = 'clean', + NEW = 'afterNew', + PARSED = 'parsed', + SCOPE_INITED = 'scopeInited', + CHECKED = 'checked', + LOWERED = 'lowered', + ASM_GENERATED = 'asmGenerated', + BIN_GENERATED = 'binGenerated', + CLEAN = 'clean', }; type PluginHandlerFunction = () => void; type PluginHandlerObject = { - order: 'pre' | 'post' | undefined - handler: PluginHandlerFunction + order: 'pre' | 'post' | undefined + handler: PluginHandlerFunction }; type PluginHandler = PluginHandlerFunction | PluginHandlerObject; interface Plugins { - name: string, - afterNew?: PluginHandler, - parsed?: PluginHandler, - scopeInited?: PluginHandler, - checked?: PluginHandler, - lowered?: PluginHandler, - asmGenerated?: PluginHandler, - binGenerated?: PluginHandler, - clean?: PluginHandler, + name: string, + afterNew?: PluginHandler, + parsed?: PluginHandler, + scopeInited?: PluginHandler, + checked?: PluginHandler, + lowered?: PluginHandler, + asmGenerated?: PluginHandler, + binGenerated?: PluginHandler, + clean?: PluginHandler, } type PluginExecutor = { - name: string - handler: PluginHandler + name: string + handler: PluginHandler }; type PluginInitFunction = () => Plugins; type RawPlugins = { - name: string, - init: PluginInitFunction | undefined + name: string, + init: PluginInitFunction | undefined }; class PluginContext { - private ast: object | undefined; - private program: object | undefined; - private projectConfig: object | undefined; - private fileManager: FileManager | undefined; - private contextPtr: number | undefined; - - constructor() { - this.ast = undefined; - this.program = undefined; - this.projectConfig = undefined; - this.fileManager = undefined; - this.contextPtr = undefined; - } - - public setArkTSAst(ast: object): void { - this.ast = ast; - } - - public getArkTSAst(): object | undefined { - return this.ast; - } - - public setArkTSProgram(program: object): void { - this.program = program; - } - - public getArkTSProgram(): object | undefined { - return this.program; - } - - public setProjectConfig(projectConfig: object): void { - this.projectConfig = projectConfig; - } - - public getProjectConfig(): object | undefined { - return this.projectConfig; - } - - public setFileManager(projectConfig: BuildConfig): void { - if (!this.fileManager) { - FileManager.init(projectConfig); - this.fileManager = FileManager.getInstance(); - } - } - - public getFileManager(): FileManager | undefined{ - return this.fileManager; - } - - public setContextPtr(ptr: number): void { - this.contextPtr = ptr; - } - - public getContextPtr(): number | undefined { - return this.contextPtr; - } + private ast: object | undefined; + private program: object | undefined; + private projectConfig: object | undefined; + private fileManager: FileManager | undefined; + private contextPtr: number | undefined; + + constructor() { + this.ast = undefined; + this.program = undefined; + this.projectConfig = undefined; + this.fileManager = undefined; + this.contextPtr = undefined; + } + + public setArkTSAst(ast: object): void { + this.ast = ast; + } + + public getArkTSAst(): object | undefined { + return this.ast; + } + + public setArkTSProgram(program: object): void { + this.program = program; + } + + public getArkTSProgram(): object | undefined { + return this.program; + } + + public setProjectConfig(projectConfig: object): void { + this.projectConfig = projectConfig; + } + + public getProjectConfig(): object | undefined { + return this.projectConfig; + } + + public setFileManager(projectConfig: BuildConfig): void { + if (!this.fileManager) { + FileManager.init(projectConfig); + this.fileManager = FileManager.getInstance(); + } + } + + public getFileManager(): FileManager | undefined { + return this.fileManager; + } + + public setContextPtr(ptr: number): void { + this.contextPtr = ptr; + } + + public getContextPtr(): number | undefined { + return this.contextPtr; + } } export class PluginDriver { - private static instance: PluginDriver | undefined; - private sortedPlugins: Map; - private allPlugins: Map; - private context: PluginContext; - private logger: Logger = Logger.getInstance(); - - constructor() { - this.sortedPlugins = new Map(); - this.allPlugins = new Map(); - this.context = new PluginContext(); - } - - public static getInstance(): PluginDriver { - if (!this.instance) { - this.instance = new PluginDriver(); - } - return this.instance; - } - - public static destroyInstance(): void { - PluginDriver.instance = undefined; - } - - public initPlugins(projectConfig: BuildConfig): void { - if (!projectConfig || !projectConfig.plugins) { - return; - } - - initKoalaPlugins(projectConfig) - - const pluginResults: RawPlugins[] = Object.entries(projectConfig.plugins).map(([key, value]) => { - try { - let pluginObject = require(value as string); - let initFunction = Object.values(pluginObject)[0] as PluginInitFunction; - if (typeof initFunction !== 'function') { - throw ('Failed to load plugin: plugin in wrong format'); + private static instance: PluginDriver | undefined; + private sortedPlugins: Map; + private allPlugins: Map; + private context: PluginContext; + private logger: Logger = Logger.getInstance(); + + constructor() { + this.sortedPlugins = new Map(); + this.allPlugins = new Map(); + this.context = new PluginContext(); + } + + public static getInstance(): PluginDriver { + if (!this.instance) { + this.instance = new PluginDriver(); + } + return this.instance; + } + + public static destroyInstance(): void { + PluginDriver.instance = undefined; + } + + public initPlugins(projectConfig: BuildConfig): void { + if (!projectConfig || !projectConfig.plugins) { + return; + } + + initKoalaPlugins(projectConfig) + + const pluginResults: RawPlugins[] = [] + + Object.entries(projectConfig.plugins).forEach(([key, value]) => { + try { + let pluginObject = require(value as string); + let initFunction = Object.values(pluginObject)[0] as PluginInitFunction; + if (typeof initFunction !== 'function') { + throw ('Failed to load plugin: plugin in wrong format'); + } + this.logger.printInfo(`Loaded plugin: ', ${key}, ${pluginObject}`); + + pluginResults.push({ + name: key, + init: initFunction + }) + } catch (error) { + if (error instanceof Error) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_LOAD_PLUGIN_FAIL, + 'Failed to load plugin.', + error.message + ) + ) + } + } + }); + + pluginResults.forEach((plugin: RawPlugins) => { + if (plugin.init !== undefined) { + this.allPlugins.set(plugin.name, plugin.init()); + } + }); + + this.context.setProjectConfig(projectConfig); + this.context.setFileManager(projectConfig); + } + + private getPlugins(hook: PluginHook): PluginExecutor[] | undefined { + if (!this.sortedPlugins.has(hook)) { + const sortedPlugins: PluginExecutor[] = this.getSortedPlugins(hook); + if (sortedPlugins.length === 0) { + this.sortedPlugins.set(hook, undefined); + } else { + this.sortedPlugins.set(hook, sortedPlugins); + } } - this.logger.printInfo(`Loaded plugin: ', ${key}, ${pluginObject}`); - - return { - name: key, - init: initFunction - }; - } catch (error) { - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_LOAD_PLUGIN_FAIL, - 'Failed to load plugin.', - error as string - ); - this.logger.printError(logData); - return { - name: key, - init: undefined - }; - } - }); - - pluginResults.forEach((plugin: RawPlugins) => { - if (plugin.init !== undefined) { - this.allPlugins.set(plugin.name, plugin.init()); - } - }); - - this.context.setProjectConfig(projectConfig); - this.context.setFileManager(projectConfig); - } - - private getPlugins(hook: PluginHook) : PluginExecutor[] | undefined { - if (!this.sortedPlugins.has(hook)) { - const sortedPlugins: PluginExecutor[] = this.getSortedPlugins(hook); - if (sortedPlugins.length === 0) { - this.sortedPlugins.set(hook, undefined); - } else { - this.sortedPlugins.set(hook, sortedPlugins); - } - } - - return this.sortedPlugins.get(hook); - } - - private getSortedPlugins(hook: PluginHook): PluginExecutor[] { - let pre: PluginExecutor[] = []; - let normal: PluginExecutor[] = []; - let post: PluginExecutor[] = []; - - this.allPlugins.forEach((pluginObject: Plugins, name: string) => { - if (!(pluginObject[hook])) { - return; - } - - let pluginName: string = pluginObject.name; - let handler: PluginHandler = pluginObject[hook]; - let order: string | undefined = typeof handler === 'object' ? handler.order : undefined; - - let rawPluginHook: PluginExecutor = { - name: pluginName, - handler: typeof handler === 'object' ? handler.handler : handler - }; - - if (order === 'pre') { - pre.push(rawPluginHook); - } else if (order === 'post') { - post.push(rawPluginHook); - } else { - normal.push(rawPluginHook); - } - }); - - return [...pre, ...normal, ...post]; - } - - public runPluginHook(hook: PluginHook): void { - let plugins: PluginExecutor[] | undefined = this.getPlugins(hook); - if (!plugins) { - return; - } - plugins.forEach((executor: PluginExecutor) => { - this.logger.printInfo(`executing plugin: ${executor.name}`); - return (executor.handler as Function).apply(this.context); - }); - } - - public getPluginContext(): PluginContext { - return this.context; - } -} \ No newline at end of file + + return this.sortedPlugins.get(hook); + } + + private getSortedPlugins(hook: PluginHook): PluginExecutor[] { + let pre: PluginExecutor[] = []; + let normal: PluginExecutor[] = []; + let post: PluginExecutor[] = []; + + this.allPlugins.forEach((pluginObject: Plugins, name: string) => { + if (!(pluginObject[hook])) { + return; + } + + let pluginName: string = pluginObject.name; + let handler: PluginHandler = pluginObject[hook]; + let order: string | undefined = typeof handler === 'object' ? handler.order : undefined; + + let rawPluginHook: PluginExecutor = { + name: pluginName, + handler: typeof handler === 'object' ? handler.handler : handler + }; + + if (order === 'pre') { + pre.push(rawPluginHook); + } else if (order === 'post') { + post.push(rawPluginHook); + } else { + normal.push(rawPluginHook); + } + }); + + return [...pre, ...normal, ...post]; + } + + public runPluginHook(hook: PluginHook): void { + let plugins: PluginExecutor[] | undefined = this.getPlugins(hook); + if (!plugins) { + return; + } + plugins.forEach((executor: PluginExecutor) => { + this.logger.printInfo(`executing plugin: ${executor.name}`); + return (executor.handler as Function).apply(this.context); + }); + } + + public getPluginContext(): PluginContext { + return this.context; + } +} diff --git a/ets2panda/driver/build_system/src/pre_define.ts b/ets2panda/driver/build_system/src/pre_define.ts index a434824c5a61c1d7ed9b0320931b98245b884c97..e46067b208e2ebbe3aefd63be73977d791ba8f0e 100644 --- a/ets2panda/driver/build_system/src/pre_define.ts +++ b/ets2panda/driver/build_system/src/pre_define.ts @@ -14,11 +14,12 @@ */ export const ARKTSCONFIG_JSON_FILE: string = 'arktsconfig.json'; -export const MERGED_INTERMEDIATE_FILE: string = 'modules_intermediate.abc'; +export const MERGED_CYCLE_FILE: string = 'cycle.abc'; export const MERGED_ABC_FILE: string = 'modules_static.abc'; export const LINKER_INPUT_FILE: string = 'fileInfo.txt'; -export const DEPENDENCY_INPUT_FILE: string = 'dependencyFileInfo.txt'; -export const DEPENDENCY_JSON_FILE: string = 'dependency.json'; +export const DEP_ANALYZER_INPUT_FILE: string = 'dependencyFileInfo.txt'; +export const DEP_ANALYZER_OUTPUT_FILE: string = 'dependency.json'; +export const DEP_ANALYZER_DIR: string = 'dep_analyzer'; export const PROJECT_BUILD_CONFIG_FILE: string = 'projectionConfig.json'; export const STATIC_RECORD_FILE: string = 'static.Record.d.ts'; @@ -38,7 +39,7 @@ export const PANDA_SDK_PATH_FROM_SDK: string = './build-tools/ets2panda'; export const SYSTEM_SDK_PATH_FROM_SDK: string = './'; export const KIT_CONFIGS_PATH_FROM_SDK: string = '../ets1.1/build-tools/ets-loader/kit_configs'; -export const DEFAULT_WOKER_NUMS: number = 6; +export const DEFAULT_WORKER_NUMS: number = 6; export const ETS_1_1 = 'ets1.1'; export const ETS_1_1_INTEROP = 'ets1.1interop'; diff --git a/ets2panda/driver/build_system/src/types.ts b/ets2panda/driver/build_system/src/types.ts index fe66126e8354e446a373f24c4ba497bc96c91d4a..9c05f24d02c8260b14952dee9bbd7c53bb3ebbf5 100644 --- a/ets2panda/driver/build_system/src/types.ts +++ b/ets2panda/driver/build_system/src/types.ts @@ -14,100 +14,97 @@ */ export enum RECORD_TYPE { - DEFAULT_TYPE = 'OFF', - ON_TYPE = 'ON', + DEFAULT_TYPE = 'OFF', + ON_TYPE = 'ON', } export enum BUILD_MODE { - DEBUG = 'Debug', - RELEASE = 'Release' + DEBUG = 'Debug', + RELEASE = 'Release' }; export enum BUILD_TYPE { - BUILD = 'build', - PREVIEW = 'preview' + BUILD = 'build', + PREVIEW = 'preview' } export enum OHOS_MODULE_TYPE { - HAP = 'hap', - FEATURE = 'feature', - SHARED = 'shared', - HAR = 'har', + HAP = 'hap', + FEATURE = 'feature', + SHARED = 'shared', + HAR = 'har', } // ProjectConfig begins export interface PluginsConfig { - [pluginName: string]: string; + [pluginName: string]: string; } export interface PathsConfig { - [pathName: string]: string[]; + [pathName: string]: string[]; } export interface BuildBaseConfig { - buildType: BUILD_TYPE; - buildMode: BUILD_MODE; - es2pandaMode: ES2PANDA_MODE; - hasMainModule: boolean; - arkts: ArkTS; - arktsGlobal: ArkTSGlobal; - maxWorkers?: number; - isBuildConfigModified?: boolean; - recordType?: RECORD_TYPE; + buildType: BUILD_TYPE; + buildMode: BUILD_MODE; + es2pandaMode: ES2PANDA_MODE; + isBuildConfigModified?: boolean; + recordType?: RECORD_TYPE; } export interface ArkTSGlobal { - filePath: string; - config: object; - compilerContext: { - program: object; - peer: object - }; - es2panda: { - _DestroyContext: Function; - _MemInitialize: Function; - _MemFinalize: Function; - _CreateGlobalContext: Function; - _DestroyGlobalContext: Function; - _SetUpSoPath: Function; - } + filePath: string; + config: object; + compilerContext: { + program: object; + peer: object + }; + es2panda: { + _DestroyContext: Function; + _MemInitialize: Function; + _MemFinalize: Function; + _CreateGlobalContext: Function; + _DestroyGlobalContext: Function; + _SetUpSoPath: Function; + } } export interface ArkTS { - Config: { - create: Function; - createContextGenerateAbcForExternalSourceFiles: Function; - }; - Context: { - createFromString: Function; - createFromStringWithHistory: Function; - }; - EtsScript: { - fromContext: Function; - }; - proceedToState: Function; - generateTsDeclarationsFromContext: Function; - generateStaticDeclarationsFromContext: Function; - destroyConfig: Function; - Es2pandaContextState: typeof Es2pandaContextState; - MemInitialize: Function; - CreateGlobalContext: Function; - AstNode: AstNode; - ETSImportDeclaration: ETSImportDeclaration; - isEtsScript: Function; - isImportSpecifier: Function; - isETSImportDeclaration: Function; - factory: { - createEtsScript: Function; - createImportDeclaration: Function; - createImportSpecifier: Function; - createLiteral: Function; - createIdentifier: Function; - updateEtsScript: Function; - createStringLiteral: Function; - }; - Es2pandaImportKinds: typeof Es2pandaImportKinds; - Es2pandaImportFlags: typeof Es2pandaImportFlags; + Config: { + create: Function; + createContextGenerateAbcForExternalSourceFiles: Function; + }; + Context: { + createFromString: Function; + createFromStringWithHistory: Function; + }; + EtsScript: { + fromContext: Function; + }; + proceedToState: Function; + generateTsDeclarationsFromContext: Function; + generateStaticDeclarationsFromContext: Function; + destroyConfig: Function; + Es2pandaContextState: typeof Es2pandaContextState; + MemInitialize: Function; + MemFinalize: Function; + CreateGlobalContext: Function; + AstNode: AstNode; + ETSImportDeclaration: ETSImportDeclaration; + isEtsScript: Function; + isImportSpecifier: Function; + isETSImportDeclaration: Function; + factory: { + createEtsScript: Function; + createImportDeclaration: Function; + createImportSpecifier: Function; + createLiteral: Function; + createIdentifier: Function; + updateEtsScript: Function; + createStringLiteral: Function; + }; + Es2pandaImportKinds: typeof Es2pandaImportKinds; + Es2pandaImportFlags: typeof Es2pandaImportFlags; } export enum Es2pandaContextState { @@ -122,224 +119,215 @@ export enum Es2pandaContextState { } export interface ModuleConfig { - packageName: string; - moduleType: OHOS_MODULE_TYPE; - moduleRootPath: string; - sourceRoots: string[]; - byteCodeHar: boolean; + packageName: string; + moduleType: OHOS_MODULE_TYPE; + moduleRootPath: string; + sourceRoots: string[]; + byteCodeHar: boolean; + entryFile: string; } export interface PathConfig { - loaderOutPath: string; - cachePath: string; - buildSdkPath: string; - pandaSdkPath?: string; // path to panda sdk lib/bin, for local test - pandaStdlibPath?: string; // path to panda sdk stdlib, for local test - externalApiPaths: string[]; - abcLinkerPath?: string; - dependencyAnalyzerPath?: string; - sdkAliasConfigPaths?: string[]; - sdkAliasMap: Map; - interopSDKPaths: Set; - interopApiPaths:string[]; - projectRootPath: string; + loaderOutPath: string; + cachePath: string; + buildSdkPath: string; + pandaSdkPath?: string; // path to panda sdk lib/bin, for local test + pandaStdlibPath?: string; // path to panda sdk stdlib, for local test + externalApiPaths: string[]; + abcLinkerPath?: string; + dependencyAnalyzerPath?: string; + sdkAliasConfigPaths?: string[]; + sdkAliasMap: Map; + interopSDKPaths: Set; + interopApiPaths: string[]; + projectRootPath: string; } /** * Configuration for framework mode compilation using generate_static_abc gni. - * + * * In framework mode, the compiler generates static ABC files from framework SDK ETS files. * This mode requires additional arktsconfig.json parameters for proper operation. */ export interface FrameworkConfig { - /** - * Enables or disables framework compilation mode. - * When enabled (true), activates special processing rules for framework-level - * compilation, including different output locations and packaging requirements. - */ - frameworkMode?: boolean; - - /** - * Determines whether an empty package name should be used. - * Must be set to true when compiling framework components without a package name. - */ - useEmptyPackage?: boolean; + /** + * Enables or disables framework compilation mode. + * When enabled (true), activates special processing rules for framework-level + * compilation, including different output locations and packaging requirements. + */ + frameworkMode?: boolean; + + /** + * Determines whether an empty package name should be used. + * Must be set to true when compiling framework components without a package name. + */ + useEmptyPackage?: boolean; } export interface DeclgenConfig { - enableDeclgenEts2Ts: boolean; - declgenV1OutPath?: string; - declgenV2OutPath?: string; - declgenBridgeCodePath?: string; - skipDeclCheck?: boolean; - continueOnError?: boolean; + enableDeclgenEts2Ts: boolean; + declgenV1OutPath?: string; + declgenV2OutPath?: string; + declgenBridgeCodePath?: string; + skipDeclCheck?: boolean; + continueOnError?: boolean; } export interface LoggerConfig { - getHvigorConsoleLogger?: Function; + getHvigorConsoleLogger?: Function; } -export interface DependentModuleConfig { - packageName: string; - moduleName: string; - moduleType: string; - modulePath: string; - sourceRoots: string[]; - entryFile: string; - language: string; - declFilesPath?: string; - dependencies?: string[]; - abcPath?: string; - declgenV1OutPath?: string; - declgenV2OutPath?: string; - declgenBridgeCodePath?: string; - byteCodeHar: boolean; +export interface DependencyModuleConfig { + packageName: string; + moduleName: string; + moduleType: string; + modulePath: string; + sourceRoots: string[]; + entryFile: string; + language: string; + declFilesPath?: string; + dependencies?: string[]; + abcPath?: string; + declgenV1OutPath?: string; + declgenV2OutPath?: string; + declgenBridgeCodePath?: string; + byteCodeHar?: boolean; } export interface BuildConfig extends BuildBaseConfig, DeclgenConfig, LoggerConfig, ModuleConfig, PathConfig, FrameworkConfig { - plugins: PluginsConfig; - paths: PathsConfig; // paths config passed from template to generate arktsconfig.json "paths" configs. - compileFiles: string[]; - entryFiles?: string[]; - dependentModuleList: DependentModuleConfig[]; - aliasConfig: Record>; + plugins: PluginsConfig; + paths: PathsConfig; // paths config passed from template to generate arktsconfig.json "paths" configs. + compileFiles: string[]; + dependencyModuleList: DependencyModuleConfig[]; + aliasConfig: Record>; + // NOTE: left to be backward compatible with old version of build config + // TO BE REMOVED!! + dependentModuleList: DependencyModuleConfig[]; } // ProjectConfig ends export interface CompileFileInfo { - filePath: string; - dependentFiles: string[]; - abcFilePath: string; - arktsConfigFile: string; - packageName: string; + inputFilePath: string; + outputFilePath: string; + arktsConfigFile: string; }; export interface ModuleInfo { - isMainModule: boolean; - packageName: string; - moduleRootPath: string; - moduleType: string; - sourceRoots: string[]; - entryFile: string; - arktsConfigFile: string; - compileFileInfos: CompileFileInfo[]; - declgenV1OutPath: string | undefined; - declgenV2OutPath: string | undefined; - declgenBridgeCodePath: string | undefined; - dependencies?: string[]; - staticDepModuleInfos: Map; - dynamicDepModuleInfos: Map; - language?: string; - declFilesPath?: string; - abcPath?: string; - frameworkMode?: boolean; - useEmptyPackage?: boolean; - byteCodeHar: boolean; - //for topological order merging - dependenciesSet:Set; - dependentSet:Set; + isMainModule: boolean; + packageName: string; + moduleRootPath: string; + moduleType: string; + sourceRoots: string[]; + entryFile: string; + arktsConfigFile: string; + declgenV1OutPath?: string; + declgenV2OutPath?: string; + declgenBridgeCodePath?: string; + dependencies: string[]; + staticDependencyModules: Map; + dynamicDependencyModules: Map; + language?: string; + declFilesPath?: string; + abcPath?: string; + frameworkMode?: boolean; + useEmptyPackage?: boolean; + byteCodeHar?: boolean; } export type SetupClusterOptions = { - clearExitListeners?: boolean; - execPath?: string; - execArgs?: string[]; + clearExitListeners?: boolean; + execPath?: string; + execArgs?: string[]; }; -export interface DependencyFileConfig { - dependants: { - [filePath: string]: string[]; - }; - dependencies: { - [filePath: string]: string[]; - } -} - -export interface JobInfo { - id: string; - isCompileAbc: boolean; - compileFileInfo: CompileFileInfo; - buildConfig: Object; - globalContextPtr?: KPointer; -} - export type KPointer = number | bigint; export interface AliasConfig { - originalAPIName: string; - isStatic: boolean; + originalAPIName: string; + isStatic: boolean; } export interface AstNode { - kind: string; - statements: AstNode[]; - source: LiteralNode; - specifiers: ImportSpecifierNode[]; + kind: string; + statements: AstNode[]; + source: LiteralNode; + specifiers: ImportSpecifierNode[]; } export interface LiteralNode { - str: string; - clone: Function; + str: string; + clone: Function; } export interface IdentifierNode { - name: string; + name: string; } export interface ImportSpecifierNode { - imported?: IdentifierNode; + imported?: IdentifierNode; } export interface ETSImportDeclaration extends AstNode { - specifiers: ImportSpecifierNode[]; - source: LiteralNode; + specifiers: ImportSpecifierNode[]; + source: LiteralNode; } export enum Es2pandaImportKinds { - IMPORT_KINDS_VALUE = 0, + IMPORT_KINDS_VALUE = 0, } export enum Es2pandaImportFlags { - IMPORT_FLAGS_NONE, + IMPORT_FLAGS_NONE, } export enum ES2PANDA_MODE { - RUN_PARALLEL = 0, - RUN_CONCURRENT = 1, - RUN = 2, - RUN_WITH_MUTIL = 3 + RUN_PARALLEL = "parallel", + RUN_CONCURRENT = "concurrent", + RUN_SIMULTANEOUS = "simultaneous", + RUN = "sequential" }; export interface DynamicFileContext { - filePath: string; - fileName: string; - relativePath: string; - isExcludedDir: boolean; - dependencySection: Record; - prefix?: string; + filePath: string; + fileName: string; + relativePath: string; + isExcludedDir: boolean; + dependencySection: Record; + prefix?: string; } export interface DependencyItem { - language: string, - path: string, - ohmUrl: string, - alias?:string[] + language: string, + path: string, + ohmUrl: string, + alias?: string[] } export interface ArkTSConfigObject { - compilerOptions: { - package: string, - baseUrl: string, - paths: Record; - dependencies: Record; - useEmptyPackage?: boolean; - rootDir?: string, - cacheDir?: string, - } + compilerOptions: { + package: string, + baseUrl: string, + paths: Record; + dependencies: Record; + useEmptyPackage?: boolean; + rootDir?: string, + cacheDir?: string, + } }; -export interface CompilePayload { - fileInfo: CompileFileInfo; - buildConfig: BuildConfig; - moduleInfos: [string, ModuleInfo][]; -} \ No newline at end of file +export interface CompileTask { + job: CompileJobInfo; + buildConfig: BuildConfig; +} + +export interface JobInfo { + id: string; + isAbcJob: boolean; + fileList: string[]; + jobDependencies: string[]; + jobDependants: string[]; +} + +export interface CompileJobInfo extends JobInfo { + compileFileInfo: CompileFileInfo +} diff --git a/ets2panda/driver/build_system/src/util/TaskManager.ts b/ets2panda/driver/build_system/src/util/TaskManager.ts index 7d9eec679ada4ad073e51f188524e1a25da457d2..92908c6f173c696465e5cfc38bcd5dbfdf5593a9 100644 --- a/ets2panda/driver/build_system/src/util/TaskManager.ts +++ b/ets2panda/driver/build_system/src/util/TaskManager.ts @@ -13,238 +13,323 @@ * limitations under the License. */ -import { fork, ChildProcess } from 'child_process'; +import * as Process from 'child_process'; import * as os from 'os'; -import { DEFAULT_WOKER_NUMS } from '../pre_define'; -import { createTaskId } from './utils'; -import { LogData, Logger } from '../logger'; - -export interface Task { - id: string; - payload: T; - resolve: (result: true) => void; - reject: (error: Object) => void; - timeoutTimer?: NodeJS.Timeout; +import { DEFAULT_WORKER_NUMS } from '../pre_define'; +import { Logger, LogData } from '../logger'; +import { Worker as JSThreadWorker } from 'worker_threads'; +import { CompileJobInfo } from '../types'; +import { DriverError } from './error' + +export interface Task { + id: string; + payload: PayloadT; + resolve: (result: true) => void; + reject: (error: Object) => void; + timeoutTimer?: NodeJS.Timeout; } export interface WorkerInfo { - worker: ChildProcess; - id: number; - currentTaskId?: string; - isKilled: boolean; + worker: DriverWorker; + id: number; + currentTaskId?: string; + isIdle: boolean; + isKilled: boolean; } -type OnWorkerExitCallback = ( - workerInfo: WorkerInfo, - code: number | null, - signal: NodeJS.Signals | null, - runningTasks: Map> +type OnWorkerExitCallback = ( + workerInfo: WorkerInfo, + code: number | null, + signal: NodeJS.Signals | null, + runningTasks: Map> ) => void; interface WorkerMessage { - id: string; - success: boolean; - shouldKill: boolean; - error?: LogData; + job: CompileJobInfo; + success: boolean; + shouldKill: boolean; + error?: LogData; +} + +interface DriverWorker { + on(msg: string, listener: (...args: any) => void): DriverWorker; + send(msgType: string, data?: any): boolean; + stop(...args: any): number; + getId(): number; + getWorkerPath(): string; + createNewInstance(workerPath: string, ...args: any): DriverWorker; } -export class TaskManager { - private workers: WorkerInfo[] = []; - private idleWorkers: WorkerInfo[] = []; - private taskQueue: Task[] = []; - private runningTasks = new Map>(); - private maxWorkers = DEFAULT_WOKER_NUMS; - private workerPath: string; - private onWorkerExit: OnWorkerExitCallback; - private taskTimeoutMs: number; +export class DriverThread implements DriverWorker { + private thread: JSThreadWorker; + private path: string; + + constructor(workerPath: string, ...args: any) { + this.path = workerPath; + this.thread = new JSThreadWorker(workerPath, ...args); + } + on(msg: string, listener: (...args: any) => void): DriverThread { + this.thread.on(msg, listener); + return this; + } + send(msgType: string, data?: any): boolean { + this.thread.postMessage({ type: msgType, data: data }); + return true + } + stop(): number { + let res = 0; + (async () => { + await this.thread.terminate().then((value: number) => { res = value; }) + })(); + return res; + } + getId(): number { + return this.thread.threadId; + } + getWorkerPath(): string { + return this.path + } + createNewInstance(workerPath: string, ...args: any): DriverThread { + return new DriverThread(workerPath, ...args) + } +} + +export class DriverProcess implements DriverWorker { + private process: Process.ChildProcess; + private path: string; + + constructor(workerPath: string, ...args: any) { + this.path = workerPath + this.process = Process.fork(workerPath, ...args); + // { + // stdio: ['inherit', 'inherit', 'inherit', 'ipc'] + // }); + } + on(msg: string, listener: (...args: any) => void): DriverProcess { + this.process.on(msg, listener); + return this; + } + send(msgType: string, data?: any): boolean { + return this.process.send({ type: msgType, data: data }); + } + stop(): number { + this.process.kill(); + return 0; + } + getId(): number { + return this.process.pid!; + } + getWorkerPath(): string { + return this.path + } + createNewInstance(workerPath: string, ...args: any): DriverProcess { + return new DriverProcess(workerPath, ...args) + } +} - constructor(workerPath: string, onWorkerExit: OnWorkerExitCallback, - maxWorkers?: number, taskTimeoutMs: number = 180000) { - const cpuCount = Math.max(os.cpus().length - 1, 1); +class WorkerFactory { + static spawnWorker(type: { new(...args: any): WorkerT; }, ...args: any): WorkerT { + return new type(...args); + } +} - this.workerPath = workerPath; - this.onWorkerExit = onWorkerExit; - this.taskTimeoutMs = taskTimeoutMs; +export class TaskManager { + private workers: WorkerInfo[] = []; + private idleWorkers: WorkerInfo[] = []; + private taskQueue: Task[] = []; + private runningTasks = new Map>(); + private maxWorkers = DEFAULT_WORKER_NUMS; + private onWorkerExit: OnWorkerExitCallback; + private taskTimeoutMs: number; + + constructor(onWorkerExit: OnWorkerExitCallback, + maxWorkers?: number, taskTimeoutMs: number = 180000) { + const cpuCount = Math.max(os.cpus().length - 1, 1); + + this.onWorkerExit = onWorkerExit; + this.taskTimeoutMs = taskTimeoutMs; + + if (maxWorkers) { + this.maxWorkers = Math.min(maxWorkers, cpuCount); + } + } - if (maxWorkers !== undefined) { - this.maxWorkers = Math.min(maxWorkers, cpuCount); - } else { - this.maxWorkers = DEFAULT_WOKER_NUMS; - } - } - - public startWorkers(): void { - for (let i = 0; i < this.maxWorkers; i++) { - const worker = fork(this.workerPath, [], { - stdio: ['inherit', 'inherit', 'inherit', 'ipc'] - }); - - const workerInfo: WorkerInfo = { worker, id: i, isKilled: false }; - - worker.on('message', (message: WorkerMessage) => { - this.handleWorkerMessage(workerInfo, message); - }); - - worker.on('exit', (code, signal) => { - this.handleWorkerExit(workerInfo, code, signal); - }); - - this.workers.push(workerInfo); - this.idleWorkers.push(workerInfo); - } - - this.dispatchNext(); - } - - - private settleTask(taskId: string, success: boolean, error?: string) { - const task = this.runningTasks.get(taskId); - if (!task) { - return; - } - if (task.timeoutTimer) { - clearTimeout(task.timeoutTimer); - task.timeoutTimer = undefined; - } - if (success) { - task.resolve(true); - } - else { - task.reject(error ?? new Error(error)); - } - this.runningTasks.delete(taskId); - } - - private handleSignals(workerInfo: WorkerInfo, signal: NodeJS.Signals | null) { - if (!signal) { - return; - } - switch (signal) { - case "SIGTERM": - break; - case "SIGSEGV": - this.reconfigureWorker(workerInfo); - break; - default: - break; - } - } - - private reconfigureWorker(workerInfo: WorkerInfo) { - const worker = fork(this.workerPath, [], { - stdio: ['inherit', 'inherit', 'inherit', 'ipc'] - }); - workerInfo.currentTaskId = undefined; - workerInfo.worker = worker; - worker.on('message', (message: WorkerMessage) => { - this.handleWorkerMessage(workerInfo, message); - }); - worker.on('exit', (code, signal) => { - this.handleWorkerExit(workerInfo, code, signal); - }); - this.idleWorkers.push(workerInfo); - } - - private handleWorkerExit(workerInfo: WorkerInfo, code: number | null, signal: NodeJS.Signals | null) { - const taskId = workerInfo.currentTaskId; - if (taskId) { - const success = code === 0 && !signal; - const reason = this.getWorkerExitReason(code, signal); - this.settleTask(taskId, success, reason); - } - - this.handleSignals(workerInfo, signal); - - if (this.onWorkerExit) { - this.onWorkerExit(workerInfo, code, signal, this.runningTasks); - } - } - - private logErrorMessage(message: WorkerMessage): void { - const err = message.error; - if (!err) { - return; - } - const logData = new LogData( - err.code, - err.description, - err.cause, - err.position, - err.solutions, - err.moreInfo - ); - if (message.shouldKill) { - this.shutdown(); - Logger.getInstance().printErrorAndExit(logData); - } else { - Logger.getInstance().printError(logData); - } - } - - private handleWorkerMessage(workerInfo: WorkerInfo, message: WorkerMessage) { - const { id, success } = message; - if (!success) { - this.logErrorMessage(message); - } - this.settleTask(id, success); - workerInfo.currentTaskId = undefined; - this.idleWorkers.push(workerInfo); - this.dispatchNext(); - } - - private getWorkerExitReason(code: number | null, signal: NodeJS.Signals | null): - string | undefined { - if (signal && signal !== 'SIGKILL') { - return `Worker killed by signal ${signal}`; - } - return code !== 0 ? `Worker exited with code ${code}` : undefined; - } - - private dispatchNext(): void { - while (this.taskQueue.length > 0 && this.idleWorkers.length > 0) { - const task = this.taskQueue.shift()!; - const workerInfo = this.idleWorkers.shift()!; - - this.runningTasks.set(task.id, task); - workerInfo.currentTaskId = task.id; - - task.timeoutTimer = setTimeout(() => { - this.taskQueue.push(task); + private handleWorkerMessage(workerInfo: WorkerInfo, message: WorkerMessage) { + const { job, success } = message; + if (!success) { + this.logErrorMessage(message); + } + this.settleTask(job.id, success); workerInfo.currentTaskId = undefined; - workerInfo.worker.kill(); - this.reconfigureWorker(workerInfo); + this.idleWorkers.push(workerInfo); + this.dispatchNext(); + } + + private handleWorkerExit(workerInfo: WorkerInfo, code: number | null, signal: NodeJS.Signals | null) { + const taskId = workerInfo.currentTaskId; + if (taskId) { + const success = code === 0 && !signal; + const reason = this.getWorkerExitReason(code, signal); + this.settleTask(taskId, success, reason); + } + + this.handleSignals(workerInfo, signal); + + if (this.onWorkerExit) { + this.onWorkerExit(workerInfo, code, signal, this.runningTasks); + } + } + + public startWorkers(type: { new(...args: any): WorkerT; }, ...args: any): void { + for (let i = 0; i < this.maxWorkers; i++) { + const worker: WorkerT = WorkerFactory.spawnWorker(type, ...args); + + const workerInfo: WorkerInfo = { worker, id: worker.getId(), isKilled: false, isIdle: true }; + + worker.on('message', (message: WorkerMessage) => { + this.handleWorkerMessage(workerInfo, message); + }); + + worker.on('exit', (code: number, signal) => { + this.handleWorkerExit(workerInfo, code, signal); + }); + + worker.on('error', (error: DriverError) => { + this.shutdownWorkers(); + Logger.getInstance().printErrorAndExit(error.logData); + }); + + this.workers.push(workerInfo); + this.idleWorkers.push(workerInfo); + } + this.dispatchNext(); - }, this.taskTimeoutMs); - - workerInfo.worker.send({ id: task.id, payload: task.payload }); - } - } - - public submitTask(payload: T): Promise { - return new Promise((resolve, reject) => { - const task: Task = { - id: createTaskId(), - payload, - resolve, - reject, - }; - this.taskQueue.push(task); - this.dispatchNext(); - }); - } - - public async shutdown(): Promise { - await Promise.all(this.workers.map((workerInfo) => - new Promise((res) => { - workerInfo.isKilled = true; - workerInfo.worker.kill(); - res(); - }) - )); - this.workers = []; - this.idleWorkers = []; - this.runningTasks.clear(); - this.taskQueue = []; - } + } + + + private dispatchNext(): void { + while (this.taskQueue.length > 0 && this.idleWorkers.length > 0) { + const task = this.taskQueue.shift()!; + const workerInfo = this.idleWorkers.shift()!; + + this.runningTasks.set(task.id, task); + workerInfo.currentTaskId = task.id; + + task.timeoutTimer = setTimeout(() => { + this.taskQueue.push(task); + workerInfo.currentTaskId = undefined; + workerInfo.worker.stop(); + this.reconfigureWorker(workerInfo); + this.dispatchNext(); + }, this.taskTimeoutMs); + + workerInfo.worker.send('ASSIGN_TASK', { id: task.id, payload: task.payload }); + } + } + + private settleTask(taskId: string, success: boolean, error?: string) { + const task = this.runningTasks.get(taskId); + if (!task) { + return; + } + if (task.timeoutTimer) { + clearTimeout(task.timeoutTimer); + task.timeoutTimer = undefined; + } + if (success) { + task.resolve(true); + } + else { + task.reject(error ?? new Error(error)); + } + this.runningTasks.delete(taskId); + } + + private handleSignals(workerInfo: WorkerInfo, signal: NodeJS.Signals | null) { + if (!signal) { + return; + } + switch (signal) { + case "SIGTERM": + break; + case "SIGSEGV": + this.reconfigureWorker(workerInfo); + break; + default: + break; + } + } + + private reconfigureWorker(workerInfo: WorkerInfo) { + const worker = workerInfo.worker + worker.createNewInstance(worker.getWorkerPath(), { + stdio: ['inherit', 'inherit', 'inherit', 'ipc'] + }); + workerInfo.currentTaskId = undefined; + workerInfo.worker = worker; + worker.on('message', (message: WorkerMessage) => { + this.handleWorkerMessage(workerInfo, message); + }); + worker.on('exit', (code, signal) => { + this.handleWorkerExit(workerInfo, code, signal); + }); + this.idleWorkers.push(workerInfo); + } + + private logErrorMessage(message: WorkerMessage): void { + const err = message.error; + if (!err) { + return; + } + const logData = new LogData( + err.code, + err.description, + err.cause, + err.position, + err.solutions, + err.moreInfo + ); + if (message.shouldKill) { + this.shutdownWorkers(); + Logger.getInstance().printErrorAndExit(logData); + } else { + Logger.getInstance().printError(logData); + } + } + + private getWorkerExitReason(code: number | null, signal: NodeJS.Signals | null): + string | undefined { + if (signal && signal !== 'SIGKILL') { + return `Worker killed by signal ${signal}`; + } + return code !== 0 ? `Worker exited with code ${code}` : undefined; + } + + public submitJob(id: string, payload: PayloadT): Promise { + return new Promise((resolve, reject) => { + const task: Task = { + id, + payload, + resolve, + reject, + }; + this.taskQueue.push(task); + this.dispatchNext(); + }); + } + + public async shutdownWorkers(): Promise { + await Promise.all(this.workers.map((workerInfo) => + new Promise((res) => { + workerInfo.isKilled = true; + workerInfo.worker.stop(); + res(); + }) + )); + this.workers = []; + this.idleWorkers = []; + this.runningTasks.clear(); + this.taskQueue = []; + } } diff --git a/ets2panda/driver/build_system/src/util/error.js b/ets2panda/driver/build_system/src/util/error.js new file mode 100644 index 0000000000000000000000000000000000000000..9917c31b3d568ff048f954d2dbb872d9504b4e29 --- /dev/null +++ b/ets2panda/driver/build_system/src/util/error.js @@ -0,0 +1,74 @@ +"use strict"; +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DriverError = exports.ErrorCode = void 0; +var ErrorCode; +(function (ErrorCode) { + ErrorCode["BUILDSYSTEM_COMPILE_ABC_FAIL"] = "11410001"; + ErrorCode["BUILDSYSTEM_LINK_ABC_FAIL"] = "11410002"; + ErrorCode["BUILDSYSTEM_SOURCEROOTS_NOT_SET_FAIL"] = "11410003"; + ErrorCode["BUILDSYSTEM_UNRECOGNIZED_MODULEROOTPATH"] = "11410004"; + ErrorCode["BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL"] = "11410005"; + ErrorCode["BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_CORRECT_FAIL"] = "11410006"; + ErrorCode["BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL"] = "11410007"; + ErrorCode["BUILDSYSTEM_LOAD_PLUGIN_FAIL"] = "11410008"; + ErrorCode["BUILDSYSTEM_ARK_LINK_NOT_FOUND_FAIL"] = "11410009"; + ErrorCode["BUILDSYSTEM_SDK_NOT_EXIST_FAIL"] = "11410010"; + ErrorCode["BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND"] = "11410011"; + ErrorCode["BUILDSYSTEM_PLUGIN_CONTEXT_RESET_PROJECT_CONFIG"] = "11410012"; + ErrorCode["BUILDSYSTEM_DECLGEN_FAIL"] = "11410013"; + ErrorCode["BUILDSYSTEM_LOAD_HASH_CACHE_FAIL"] = "11410014"; + ErrorCode["BUILDSYSTEM_DEPENDENCY_ANALYZE_FAIL"] = "11410015"; + ErrorCode["BUILDSYSTEM_DEPENDENCY_ANALYZER_NOT_FOUND_FAIL"] = "11410016"; + ErrorCode["BUILDSYSTEM_ABC_FILE_MISSING_IN_BCHAR"] = "11410017"; + ErrorCode["BUILDSYSTEM_HANDLE_ENTRY_FILE"] = "11410018"; + ErrorCode["BUILDSYSTEM_PATH_RESOLVE_FAIL"] = "11410019"; + ErrorCode["BUILDSYSTEM_INTEROP_SDK_NOT_FIND"] = "11410020"; + ErrorCode["BUILDSYSTEM_INIT_ALIAS_CONFIG_FAILED"] = "11410021"; + ErrorCode["BUILDSYSTEM_PLUGIN_ALIAS_CONFIG_PARSING_FAIL"] = "11410022"; + ErrorCode["BUILDSYSTEM_ABC_FILE_NOT_EXIST_IN_BCHAR"] = "11410023"; + ErrorCode["BUILDSYSTEM_ALIAS_MODULE_PATH_NOT_EXIST"] = "11410024"; + ErrorCode["BUILDSYSTEM_ENTRY_FILE_NOT_EXIST"] = "11410025"; + ErrorCode["BUILDSYSTEM_COMPILE_FAILED_IN_WORKER"] = "11410026"; +})(ErrorCode || (exports.ErrorCode = ErrorCode = {})); +var DriverError = /** @class */ (function (_super) { + __extends(DriverError, _super); + function DriverError(logData) { + var _this = _super.call(this) || this; + _this.logData = logData; + return _this; + } + DriverError.prototype.toString = function () { + return this.logData.toString(); + }; + return DriverError; +}(Error)); +exports.DriverError = DriverError; diff --git a/ets2panda/driver/build_system/src/error_code.ts b/ets2panda/driver/build_system/src/util/error.ts similarity index 84% rename from ets2panda/driver/build_system/src/error_code.ts rename to ets2panda/driver/build_system/src/util/error.ts index 529679ab833da155637873d5a8436ed7965ed581..108e184e450133cc30d7334dde0b0734d560b3f9 100644 --- a/ets2panda/driver/build_system/src/error_code.ts +++ b/ets2panda/driver/build_system/src/util/error.ts @@ -13,10 +13,9 @@ * limitations under the License. */ -export enum SubsystemCode { - BUILDSYSTEM = '114', - ES2PANDA = '115', -} +import { + LogData +} from '../logger' export enum ErrorCode { BUILDSYSTEM_COMPILE_ABC_FAIL = '11410001', @@ -33,8 +32,8 @@ export enum ErrorCode { BUILDSYSTEM_PLUGIN_CONTEXT_RESET_PROJECT_CONFIG = '11410012', BUILDSYSTEM_DECLGEN_FAIL = '11410013', BUILDSYSTEM_LOAD_HASH_CACHE_FAIL = '11410014', - BUILDSYSTEM_Dependency_Analyze_FAIL = '11410015', - BUILDSYSTEM_Dependency_Analyzer_NOT_FOUND_FAIL = '11410016', + BUILDSYSTEM_DEPENDENCY_ANALYZE_FAIL = '11410015', + BUILDSYSTEM_DEPENDENCY_ANALYZER_NOT_FOUND_FAIL = '11410016', BUILDSYSTEM_ABC_FILE_MISSING_IN_BCHAR = '11410017', BUILDSYSTEM_HANDLE_ENTRY_FILE = '11410018', BUILDSYSTEM_PATH_RESOLVE_FAIL = '11410019', @@ -47,3 +46,16 @@ export enum ErrorCode { BUILDSYSTEM_COMPILE_FAILED_IN_WORKER = "11410026", BUILDSYSTEM_DECLGEN_FAILED_IN_WORKER = '11410027', } + +export class DriverError extends Error { + logData: LogData + + constructor(logData: LogData) { + super() + this.logData = logData + } + + toString() { + return this.logData.toString() + } +} diff --git a/ets2panda/driver/build_system/src/util/utils.ts b/ets2panda/driver/build_system/src/util/utils.ts index 738d8d7b6422e30645fda22cef91d9c00d72628b..c886f7d83d403e7de104a3a95d0395ec4f6d9d3e 100644 --- a/ets2panda/driver/build_system/src/util/utils.ts +++ b/ets2panda/driver/build_system/src/util/utils.ts @@ -19,22 +19,21 @@ import * as os from 'os'; import * as path from 'path'; import { - ARKTS_MODULE_NAME, - DECL_ETS_SUFFIX, - LANGUAGE_VERSION, - NATIVE_MODULE, - sdkConfigPrefix + ARKTS_MODULE_NAME, + DECL_ETS_SUFFIX, + LANGUAGE_VERSION, + NATIVE_MODULE, + sdkConfigPrefix } from '../pre_define'; import { - Logger, - LogData, - LogDataFactory + LogDataFactory } from '../logger'; -import { ErrorCode } from '../error_code'; +import { ErrorCode, DriverError } from '../util/error'; import { - ModuleInfo, - OHOS_MODULE_TYPE, - BuildConfig + ModuleInfo, + OHOS_MODULE_TYPE, + BuildConfig, + DependencyModuleConfig } from '../types'; const WINDOWS: string = 'Windows_NT'; @@ -42,88 +41,89 @@ const LINUX: string = 'Linux'; const MAC: string = 'Darwin'; export function isWindows(): boolean { - return os.type() === WINDOWS; + return os.type() === WINDOWS; } export function isLinux(): boolean { - return os.type() === LINUX; + return os.type() === LINUX; } export function isMac(): boolean { - return os.type() === MAC; + return os.type() === MAC; } export function changeFileExtension(file: string, targetExt: string, originExt = ''): string { - let currentExt = originExt.length === 0 ? getFileExtension(file) : originExt; - let fileWithoutExt = file.substring(0, file.lastIndexOf(currentExt)); - return fileWithoutExt + targetExt; + let currentExt = originExt.length === 0 ? getFileExtension(file) : originExt; + let fileWithoutExt = file.substring(0, file.lastIndexOf(currentExt)); + return fileWithoutExt + targetExt; } export function changeDeclgenFileExtension(file: string, targetExt: string): string { - if (file.endsWith(DECL_ETS_SUFFIX)) { - return changeFileExtension(file, targetExt, DECL_ETS_SUFFIX); - } - return changeFileExtension(file, targetExt); + if (file.endsWith(DECL_ETS_SUFFIX)) { + return changeFileExtension(file, targetExt, DECL_ETS_SUFFIX); + } + return changeFileExtension(file, targetExt); } export function ensurePathExists(filePath: string): void { - try { const dirPath: string = path.dirname(filePath); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - } - } catch (error) { - if (error instanceof Error) { - console.error(`Error: ${error.message}`); + ensureDirExists(dirPath); +} + +export function ensureDirExists(dirPath: string): void { + try { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } + } catch (error) { + if (error instanceof Error) { + console.error(`Error: ${error.message}`); + } } - } -} - -export function getFileHash(filePath: string): string { - const content = fs.readFileSync(filePath, 'utf8'); - return crypto.createHash('sha256').update(content).digest('hex'); } export function toUnixPath(path: string): string { - return path.replace(/\\/g, '/'); + return path.replace(/\\/g, '/'); } export function readFirstLineSync(filePath: string): string | null { - const fd = fs.openSync(filePath, 'r'); - const buffer = Buffer.alloc(256); - const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0); - fs.closeSync(fd); - - const content = buffer.toString('utf-8', 0, bytesRead); - const firstLine = content.split(/\r?\n/, 1)[0].trim(); - - return firstLine; -} - -export function safeRealpath(path: string, logger: Logger): string { - try { - return fs.realpathSync(path); - } catch(error) { - const msg = error instanceof Error ? error.message : String(error); - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_PATH_RESOLVE_FAIL, - `Error resolving path "${path}".`, - msg - ); - logger.printError(logData); - throw logData; - } + const fd = fs.openSync(filePath, 'r'); + const buffer = Buffer.alloc(256); + const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0); + fs.closeSync(fd); + + const content = buffer.toString('utf-8', 0, bytesRead); + const firstLine = content.split(/\r?\n/, 1)[0].trim(); + + return firstLine; +} + +export function safeRealpath(path: string): string { + try { + return fs.realpathSync(path); + } catch (error) { + if (error instanceof Error) { + throw new DriverError( + LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_PATH_RESOLVE_FAIL, + `Error resolving path "${path}".`, + error.message + ) + ); + } + throw error + } } export function getInteropFilePathByApi(apiName: string, interopSDKPath: Set): string { - for (const sdkPath of interopSDKPath) { - const modulePath = path.resolve(sdkPath, apiName + DECL_ETS_SUFFIX); - if (fs.existsSync(modulePath)) { - return modulePath; + for (const sdkPath of interopSDKPath) { + const modulePath = path.resolve(sdkPath, apiName + DECL_ETS_SUFFIX); + if (fs.existsSync(modulePath)) { + return modulePath; + } } - } - return ''; + return ''; } /** @@ -131,28 +131,28 @@ export function getInteropFilePathByApi(apiName: string, interopSDKPath: Set { - const systemModule: string = `${moduleType}.${systemKey}`; - if (NATIVE_MODULE.has(systemModule)) { - return `@native:${systemModule}`; - } else if (moduleType === ARKTS_MODULE_NAME) { - // @arkts.xxx -> @ohos:arkts.xxx - return `@ohos:${systemModule}`; - } else { - return `@ohos:${systemKey}`; - }; - }); - } - return ''; + const REG_SYSTEM_MODULE: RegExp = new RegExp(`@(${sdkConfigPrefix})\\.(\\S+)`); + + if (REG_SYSTEM_MODULE.test(api.trim())) { + return api.replace(REG_SYSTEM_MODULE, (_, moduleType, systemKey) => { + const systemModule: string = `${moduleType}.${systemKey}`; + if (NATIVE_MODULE.has(systemModule)) { + return `@native:${systemModule}`; + } else if (moduleType === ARKTS_MODULE_NAME) { + // @arkts.xxx -> @ohos:arkts.xxx + return `@ohos:${systemModule}`; + } else { + return `@ohos:${systemKey}`; + }; + }); + } + return ''; } export function isSubPathOf(targetPath: string, parentDir: string): boolean { - const resolvedParent = toUnixPath(path.resolve(parentDir)); - const resolvedTarget = toUnixPath(path.resolve(targetPath)); - return resolvedTarget === resolvedParent || resolvedTarget.startsWith(resolvedParent + '/'); + const resolvedParent = toUnixPath(path.resolve(parentDir)); + const resolvedTarget = toUnixPath(path.resolve(targetPath)); + return resolvedTarget === resolvedParent || resolvedTarget.startsWith(resolvedParent + '/'); } /** @@ -162,78 +162,70 @@ export function isSubPathOf(targetPath: string, parentDir: string): boolean { * @returns The full extension (e.g., '.d.ts'). Returns an empty string if no extension is found. */ export function getFileExtension( - filePath: string, - knownCompositeExts: string[] = ['.d.ts', '.test.ts', '.d.ets'] + filePath: string, + knownCompositeExts: string[] = ['.d.ts', '.test.ts', '.d.ets'] ): string { - const baseName = path.basename(filePath); + const baseName = path.basename(filePath); - // Match known composite extensions first - for (const ext of knownCompositeExts) { - if (baseName.endsWith(ext)) { - return ext; + // Match known composite extensions first + for (const ext of knownCompositeExts) { + if (baseName.endsWith(ext)) { + return ext; + } } - } - // Fallback to default behavior: return the last segment after the final dot - return path.extname(baseName); + // Fallback to default behavior: return the last segment after the final dot + return path.extname(baseName); } export function hasEntry(moduleInfo: ModuleInfo): boolean { - switch (moduleInfo.moduleType) { - case OHOS_MODULE_TYPE.SHARED: - case OHOS_MODULE_TYPE.HAR: - return true; - default: - return false; - } + switch (moduleInfo.moduleType) { + case OHOS_MODULE_TYPE.SHARED: + case OHOS_MODULE_TYPE.HAR: + return true; + default: + return false; + } } export function createFileIfNotExists(filePath: string, content: string): boolean { - try { - const normalizedPath = path.normalize(filePath); - if (fs.existsSync(normalizedPath)) { - return false; + try { + const normalizedPath = path.normalize(filePath); + if (fs.existsSync(normalizedPath)) { + return false; + } + + ensurePathExists(filePath); + + fs.writeFileSync(normalizedPath, content, { encoding: 'utf-8' }); + return true; + } catch (error) { + return false; } +} - const dir = path.dirname(normalizedPath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); +export function isMixCompileProject(buildConfig: BuildConfig): boolean { + for (const moduleInfo of buildConfig.dependencyModuleList) { + if ( + moduleInfo.language === LANGUAGE_VERSION.ARKTS_1_1 || + moduleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID + ) { + return true; + } } - - fs.writeFileSync(normalizedPath, content, { encoding: 'utf-8' }); - return true; - } catch (error) { return false; - } } -export function isMixCompileProject(buildConfig: BuildConfig): boolean { - for (const moduleInfo of buildConfig.dependentModuleList) { - if ( - moduleInfo.language === LANGUAGE_VERSION.ARKTS_1_1 || - moduleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID - ) { - return true; - } - } - return false; +export function checkDependencyModuleInfoCorrectness(module: DependencyModuleConfig): boolean { + return (module.packageName && module.modulePath && module.sourceRoots && module.entryFile) != ""; } -export function createTaskId(): string { - const timestamp = Date.now().toString(36); - const randomPart = Math.random().toString(36).slice(2, 6); - return `task-${timestamp}-${randomPart}`; +export function computeHash(str: string): string { + const hash = crypto.createHash('sha256'); + return hash.update(str).digest('hex'); } -export function serializeWithIgnore(obj: any, ignoreKeys: string[] = []): any { - const jsonStr = JSON.stringify(obj, (key, value) => { - if (typeof value === 'bigint') { - return undefined; - } - if (ignoreKeys.includes(key)) { - return undefined; - } - return value; - }); - return JSON.parse(jsonStr); +export function getFileHash(filePath: string): string { + return computeHash(fs.readFileSync(filePath, 'utf8')); } + diff --git a/ets2panda/driver/build_system/src/util/worker_exit_handler.ts b/ets2panda/driver/build_system/src/util/worker_exit_handler.ts index 13d798cf472ac6a4b523ab2b28b588c36fafc3c3..efae43d1373bb4ad617921095f96068e3a43b242 100644 --- a/ets2panda/driver/build_system/src/util/worker_exit_handler.ts +++ b/ets2panda/driver/build_system/src/util/worker_exit_handler.ts @@ -13,67 +13,66 @@ * limitations under the License. */ -import { ErrorCode } from "../error_code"; +import { ErrorCode } from "../util/error"; import { getEs2pandaPath } from "../init/process_build_config"; import { LogData, LogDataFactory, Logger } from "../logger"; -import { CompilePayload } from "../types"; +import { CompileTask } from "../types"; import { Task, WorkerInfo } from "./TaskManager"; export function handleCompileWorkerExit( - workerInfo: WorkerInfo, - code: number | null, - signal: NodeJS.Signals | null, - runningTasks: Map> + workerInfo: WorkerInfo, + code: number | null, + signal: NodeJS.Signals | null, + runningTasks: Map> ): void { - if (!code || code === 0) { - return - } - const taskId = workerInfo.currentTaskId; - const payload = runningTasks.get(taskId!)?.payload; - if (!payload) { - return; - } - const es2pandPath = getEs2pandaPath(payload.buildConfig); - const cmd = [ - es2pandPath, - '--arktsconfig', payload.fileInfo.arktsConfigFile, - '--output', payload.fileInfo.abcFilePath, - payload.fileInfo.filePath - ]; + if (!code || code === 0) { + return + } + const taskId = workerInfo.currentTaskId; + const payload = runningTasks.get(taskId!)?.payload; + if (!payload) { + return; + } + const es2pandPath = getEs2pandaPath(payload.buildConfig); + const cmd = [ + es2pandPath, + '--arktsconfig', payload.job.compileFileInfo.arktsConfigFile, + '--output', payload.job.compileFileInfo.outputFilePath, + payload.job.compileFileInfo.inputFilePath + ]; - const logData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_COMPILE_FAILED_IN_WORKER, - `Compile file ${payload.fileInfo.filePath} crashed (exit code ${code})`, - "", - "", - [`Please try to run command locally : ${cmd.join(' ')}`] - ); + const logData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_COMPILE_FAILED_IN_WORKER, + `Compile file ${payload.job.compileFileInfo.inputFilePath} crashed (exit code ${code})`, + "", + "", + [`Please try to run command locally : ${cmd.join(' ')}`] + ); - Logger.getInstance().printErrorAndExit(logData); + Logger.getInstance().printErrorAndExit(logData); } export function handleDeclgenWorkerExit( - workerInfo: WorkerInfo, - code: number | null, - signal: NodeJS.Signals | null, - runningTasks: Map> + workerInfo: WorkerInfo, + code: number | null, + signal: NodeJS.Signals | null, ): void { - if (code && code !== 0) { - let logExitCodeData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_DECLGEN_FAILED_IN_WORKER, - `Declgen crashed (exit code ${code})`, - "This error is likely caused internally from compiler.", - ); - Logger.getInstance().printError(logExitCodeData); - return; - } - if (signal && signal !== "SIGTERM") { - let logSignalData: LogData = LogDataFactory.newInstance( - ErrorCode.BUILDSYSTEM_DECLGEN_FAILED_IN_WORKER, - `Declgen crashed (exit signal ${signal})`, - "This error is likely caused internally from compiler.", - ); - Logger.getInstance().printError(logSignalData); - } + if (code && code !== 0) { + let logExitCodeData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DECLGEN_FAILED_IN_WORKER, + `Declgen crashed (exit code ${code})`, + "This error is likely caused internally from compiler.", + ); + Logger.getInstance().printError(logExitCodeData); + return; + } + if (signal && signal !== "SIGTERM") { + let logSignalData: LogData = LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_DECLGEN_FAILED_IN_WORKER, + `Declgen crashed (exit signal ${signal})`, + "This error is likely caused internally from compiler.", + ); + Logger.getInstance().printError(logSignalData); + } } diff --git a/ets2panda/driver/build_system/test/demo_hap/build_config.json b/ets2panda/driver/build_system/test/demo_hap/build_config.json index 59193da7a0c69939c08da835cda3ca67aba92c5f..f991918b62084fd833a59f93adefa5829f690eb8 100644 --- a/ets2panda/driver/build_system/test/demo_hap/build_config.json +++ b/ets2panda/driver/build_system/test/demo_hap/build_config.json @@ -4,7 +4,7 @@ "${absolute_path_to_build_system}/test/demo_hap/entry/c.ets", "${absolute_path_to_build_system}/test/demo_hap/entry/d.ets", "${absolute_path_to_build_system}/test/demo_hap/harA/index.ets", - "${absolute_path_to_build_system}/test/demo_hap/harB/indexB.ets" + "${absolute_path_to_build_system}/test/demo_hap/harB/index.ets" ], "packageName": "entry", @@ -14,6 +14,7 @@ "buildMode": "Debug", "moduleRootPath": "${absolute_path_to_build_system}/test/demo_hap/entry/", "sourceRoots": ["./", "src/main1/ets"], + "es2pandaMode": "sequential", "loaderOutPath": "./dist", "cachePath": "./dist/cache", @@ -24,7 +25,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", @@ -32,7 +33,10 @@ "modulePath": "${absolute_path_to_build_system}/test/demo_hap/harA", "sourceRoots": ["./"], "entryFile": "index.ets", - "language": "1.2" + "language": "1.2", + "dependencies": [ + "harB" + ] }, { "packageName": "harB", @@ -40,8 +44,8 @@ "moduleType": "har", "modulePath": "${absolute_path_to_build_system}/test/demo_hap/harB", "sourceRoots": ["./"], - "entryFile": "indexB.ets", + "entryFile": "index.ets", "language": "1.2" } ] -} \ No newline at end of file +} diff --git a/ets2panda/driver/build_system/test/demo_hap/entry/a.ets b/ets2panda/driver/build_system/test/demo_hap/entry/a.ets index 03e10fb47a6de6ecbe014b8d1d67cb7008d8f24a..55c0ac5e6e51c8ac9dc9aa152a21292208fa028d 100644 --- a/ets2panda/driver/build_system/test/demo_hap/entry/a.ets +++ b/ets2panda/driver/build_system/test/demo_hap/entry/a.ets @@ -31,4 +31,4 @@ function main() { } let stra = "hello world from a" -export {stra} \ No newline at end of file +export {stra} diff --git a/ets2panda/driver/build_system/test/demo_hap/entry/b.ets b/ets2panda/driver/build_system/test/demo_hap/entry/b.ets index cc677f2f571d961d6978d102705cdc2778b00054..a13c72fdef80b8850a5c59ea616931ea3104cb18 100644 --- a/ets2panda/driver/build_system/test/demo_hap/entry/b.ets +++ b/ets2panda/driver/build_system/test/demo_hap/entry/b.ets @@ -30,4 +30,4 @@ function main() { console.log(strA); console.log(strB); -} \ No newline at end of file +} diff --git a/ets2panda/driver/build_system/test/demo_hap/harA/index.ets b/ets2panda/driver/build_system/test/demo_hap/harA/index.ets index 9c9b7000e75894a8b3b9c78ad5ce4d07fac87cdd..f0ea1e7ded23779f92ef0b64fec5ff13b029b1e6 100644 --- a/ets2panda/driver/build_system/test/demo_hap/harA/index.ets +++ b/ets2panda/driver/build_system/test/demo_hap/harA/index.ets @@ -13,4 +13,6 @@ * limitations under the License. */ -export let strA = "hello world from harA!"; \ No newline at end of file +import {strB} from 'harB' + +export let strA = "hello world from harA!"; diff --git a/ets2panda/driver/build_system/test/demo_hap/harB/indexB.ets b/ets2panda/driver/build_system/test/demo_hap/harB/index.ets similarity index 89% rename from ets2panda/driver/build_system/test/demo_hap/harB/indexB.ets rename to ets2panda/driver/build_system/test/demo_hap/harB/index.ets index 7ef14f38911e1520308935b2a2f53f0847b07a59..1709cf4d9bb22b2be7e7a1325269019586799a99 100644 --- a/ets2panda/driver/build_system/test/demo_hap/harB/indexB.ets +++ b/ets2panda/driver/build_system/test/demo_hap/harB/index.ets @@ -13,4 +13,6 @@ * limitations under the License. */ -export let strB = "hello world from harB!"; \ No newline at end of file +// import {strA} from 'harA' + +export let strB = "hello world from harB!"; diff --git a/ets2panda/driver/build_system/test/demo_mix_hap/build_config.json b/ets2panda/driver/build_system/test/demo_mix_hap/build_config.json index 3ca824dcfa20aa2243eaba806a204f3f4128d584..36b8719367e5710f4af31fb720503dbf16852aa9 100644 --- a/ets2panda/driver/build_system/test/demo_mix_hap/build_config.json +++ b/ets2panda/driver/build_system/test/demo_mix_hap/build_config.json @@ -10,6 +10,7 @@ "buildMode": "Debug", "moduleRootPath": "${absolute_path_to_build_system}/test/demo_mix_hap/entry/", "sourceRoots": ["./"], + "es2pandaMode": "sequential", "loaderOutPath": "./dist/cache/demo_mix_hap", "cachePath": "./dist/cache/demo_mix_hap", @@ -20,7 +21,7 @@ "declgenDtsOutPath": "./dist/cache/demo_mix_hap/decl", "declgenTsOutPath": "./dist/cache/demo_mix_hap/ts", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "static_har", "moduleName": "static_har", diff --git a/ets2panda/driver/build_system/test/demo_mix_hap/build_config_decl.json b/ets2panda/driver/build_system/test/demo_mix_hap/build_config_decl.json index b562b33aa8bfe62494f37e79fd027f5ad80a1aab..fecdaf64d9d822004e5fb873f0ba98d6619be880 100644 --- a/ets2panda/driver/build_system/test/demo_mix_hap/build_config_decl.json +++ b/ets2panda/driver/build_system/test/demo_mix_hap/build_config_decl.json @@ -23,5 +23,5 @@ "declgenDtsOutPath": "./dist/cache/demo_mix_hap/decl", "declgenTsOutPath": "./dist/cache/demo_mix_hap/ts", - "dependentModuleList": [] + "dependencyModuleList": [] } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/IncrementDemo/build_config.json b/ets2panda/driver/build_system/test/e2e/IncrementDemo/build_config.json index a43e58bf8ddf8d495d1479bf49ce130be96979e2..04228e17a066105932d54c310fa03fa958b0fd05 100755 --- a/ets2panda/driver/build_system/test/e2e/IncrementDemo/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/IncrementDemo/build_config.json @@ -24,7 +24,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/checkHash.test.ts b/ets2panda/driver/build_system/test/e2e/checkHash.test.ts index dca9540b97bab0038adac9f81326f7a1594938ec..c8720bf39ba5394d38ce52875cddf14f1e7a9b2a 100755 --- a/ets2panda/driver/build_system/test/e2e/checkHash.test.ts +++ b/ets2panda/driver/build_system/test/e2e/checkHash.test.ts @@ -23,7 +23,7 @@ import crypto from 'crypto'; function getAllModules(config: any) { return [ { packageName: config.packageName, modulePath: config.moduleRootPath, sourceRoots: config.sourceRoots || ['./'] }, - ...(config.dependentModuleList || []) + ...(config.dependencyModuleList || []) ]; } diff --git a/ets2panda/driver/build_system/test/e2e/compile.test.ts b/ets2panda/driver/build_system/test/e2e/compile.test.ts index a2e6d8f9b9bd6944386d76bc37967d2e6825d25a..30298532110327006b6172b77daf8e92b3844bd1 100755 --- a/ets2panda/driver/build_system/test/e2e/compile.test.ts +++ b/ets2panda/driver/build_system/test/e2e/compile.test.ts @@ -69,7 +69,7 @@ function getConfigAndPaths(testScriptName: string) { function getExpectedOutputs(config: any, cachePath: string) { const allModules = [ { packageName: config.packageName, modulePath: config.moduleRootPath }, - ...(config.dependentModuleList || []).map((m: any) => ({ + ...(config.dependencyModuleList || []).map((m: any) => ({ packageName: m.packageName, modulePath: m.modulePath })) diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config1.json index 540ae17e589a7a9a2796244c61319b7a406cac40..8475a4e531ac03828c57608755b7d1ddcda69a86 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config1.json @@ -22,7 +22,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config2.json index d7a47e61c8b7b3cbd74d3418e53119269ba4df5f..e88373108a5146574a6d8e834fde2f60761c4e20 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.1_hsp1.2/build_config2.json @@ -19,7 +19,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "hspA", "moduleName": "hspA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config1.json index d3170e03ef91b57d24572fe0eb68d3f960815c11..4a22b6e96353390beee70f0905cee4a48c5966da 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config1.json @@ -19,6 +19,6 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ] } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config2.json index d1ba6641731478dd9cae087d515a03985c07777d..803fea33653692a78ec0c075f265ec53f3c14cf8 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.1/build_config2.json @@ -24,7 +24,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", "useNormalizedOHMUrl": true, - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config1.json index a06617898011aa33e0ac4cd4b092a04e8bce0514..6240cf12458350ea7b230f1d0be20cbbd7a45e83 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config1.json @@ -23,6 +23,6 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ] } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config2.json index 5c8ba385fe2db402a307e305f22ce1e5981edf1e..adda7585366edcf9d872f232174d75a3c09c4b9a 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_har1.2_hsp1.2/build_config2.json @@ -23,7 +23,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config1.json index 21ffecc50df6ede6f5f71b5a6e8f6da1fb834673..029bd190e38f008af766e18e5be17193e1150b84 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config1.json @@ -22,7 +22,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config2.json index ca2a934d3088117255c7c5c9e8aef26214736c3e..0fc6e9f07c9604541867528efcacca53f665dd91 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.1_hsp1.2/build_config2.json @@ -20,7 +20,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "hsp", "moduleName": "hsp", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config1.json index c8b751563ac7f441aacae2524d4e2650d8cc8104..ba290d85e42c7e552371ff824bef63971e3422b9 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config1.json @@ -18,6 +18,6 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ] } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config2.json index 5edb23b2534de11f2f40a239ac256eaeac0f52aa..b301a3cdf2f862ee3e4d6f0bbde012689bdde156 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config2.json @@ -19,6 +19,6 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ] } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config3.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config3.json index 7b0d5e819e7063fb74d243198d8955831e7b4126..8f9f45375ce77af61c5c93c2200e7040d302c070 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config3.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.1/build_config3.json @@ -22,7 +22,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config1.json index 1f60eac75d58c4ad7c3b9a9c02c3e918bccc20a6..fa79d4fef872ef96fb9ff7f19f887498aa46635a 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config1.json @@ -23,6 +23,6 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ] } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config2.json index fab627cabc7518c96bdfc70818570f24855f472c..29325051799e353ef21b8a82c09796c72c3e169f 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.1_hsp1.2/build_config2.json @@ -22,7 +22,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config1.json index 9944bc1b5c6ae90d7e0b3070de09b9ac6319e116..5cdf361443cf8f62ba7778a50d8d63a441ba18c4 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config1.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config2.json index 220fd2e7f2a77507ea636c09be0f216f3646a132..00a3659982a9b216e1f6815e1e88d558a580db38 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config2.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config3.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config3.json index 3c4bf1c7d2fe66fe6b5f43de12e5f89559b5e7e7..dea3ba5d51e7864a24bd8c3a9dddae3393f2e6a0 100644 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config3.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2/build_config3.json @@ -27,7 +27,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.1/build_config.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.1/build_config.json index b46713de0f2313c04eafaab3bfd48bdbde07d95a..ba684f7b002b5e9199c959bbb3704d128589f851 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.1/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.1/build_config.json @@ -25,7 +25,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", "useNormalizedOHMUrl": true, - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config1.json index cec7a7664dc2c940be641610dc25b8d0a4afcf81..e75dc6213c7786a39350b0d5ef0017bb70056367 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config1.json @@ -22,7 +22,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config2.json index af1bc8a9805859bc2d1acced8e114b34e4e2d007..e020f0eaae944d7392cee48802219a1bd5ad720b 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_har1.2_hsp1.2/build_config2.json @@ -23,7 +23,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "harA", "moduleName": "harA", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.1/build_config.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.1/build_config.json index 5c1a110fc87374a2e821b5f64a0e9a17e699ab86..5b2a6b92dc686d42101257a630895d65e4d4e2f4 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.1/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.1/build_config.json @@ -21,7 +21,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "hsp", "moduleName": "hsp", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config1.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config1.json index 6f88b5d9850482e08ee01b98d4d7f87442cf7bc8..6044c192487723384eff193b4493b5c6fec516ad 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config1.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config1.json @@ -22,6 +22,6 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config2.json b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config2.json index 42c0caa1dab27eed9e9fbdfd9dc2f47b02f2671c..b301d7f46e008f0c826ab80e98d3fc437537a74c 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config2.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1.2_hsp1.2/build_config2.json @@ -21,7 +21,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "hsp", "moduleName": "hsp", diff --git a/ets2panda/driver/build_system/test/e2e/demo_entry1_2/build_config.json b/ets2panda/driver/build_system/test/e2e/demo_entry1_2/build_config.json index a6673efea3887c47f11f56f1fdee8578d9080c79..b164982b1d7dc693fd6570c20f776d685b46d20e 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_entry1_2/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/demo_entry1_2/build_config.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_har1_2/build_config_decl.json b/ets2panda/driver/build_system/test/e2e/demo_har1_2/build_config_decl.json index 6d808ef1d6da3070b8115b83100882747b342070..42466aea7c02f6bb3cdddb0e61af62543a47a171 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_har1_2/build_config_decl.json +++ b/ets2panda/driver/build_system/test/e2e/demo_har1_2/build_config_decl.json @@ -17,6 +17,6 @@ "declgenV1OutPath": "./dist/declgen/decl_ets", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_hsp1_2/build_config_decl.json b/ets2panda/driver/build_system/test/e2e/demo_hsp1_2/build_config_decl.json index 96a16c44d40aa7954f546062d136226191d4041b..9968d3b672e668da8791d335746c4ad8c54d24d6 100755 --- a/ets2panda/driver/build_system/test/e2e/demo_hsp1_2/build_config_decl.json +++ b/ets2panda/driver/build_system/test/e2e/demo_hsp1_2/build_config_decl.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/demo_interop_sdk/build_config.json b/ets2panda/driver/build_system/test/e2e/demo_interop_sdk/build_config.json index a28ca043baa7ae7453d9e5eaab1dcc8abd5b700b..3b31d3a3c48fa65cda023399536f05c12bf30d0b 100644 --- a/ets2panda/driver/build_system/test/e2e/demo_interop_sdk/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/demo_interop_sdk/build_config.json @@ -18,7 +18,7 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true, "interopApiPaths":[ "${absolute_path_to_build_system}/test/mock_interop_sdk" diff --git a/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config.json b/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config.json index 47fcd7ad54e8ef90f91982ee716bcdcfb922b492..7d263fdf99f0ce7a20eb53cbbab39c8be9a4cc2a 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config.json @@ -20,7 +20,7 @@ "declgenBridgeCodePath": "./dist/declgen/ets", "hasMainModule": true, "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "har1_2", "moduleName": "har1_2", diff --git a/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config_decl.json b/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config_decl.json index cdfbd1366ebaad4df7b04a6be4463ab89edf07aa..d9cbdefee00696e34f0695b79b013a2411467a3e 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config_decl.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_1_external_har1_2/build_config_decl.json @@ -22,7 +22,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ ], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config.json b/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config.json index 8e9d8491551ec63b9212a91e969643a25569ab21..a5ce4f61cfee66ad0b536c74fec51caf74ed0695 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config.json @@ -17,7 +17,7 @@ "declgenBridgeCodePath": "./dist/declgen/ets", "hasMainModule": true, "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "hsp1_2", "moduleName": "hsp1_2", diff --git a/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config_decl.json b/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config_decl.json index eff1e4b05fa72a3c52d34cf0fc044ee7024c72d7..d4bd0443cad39b61745026ddc5c372318edce8f8 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config_decl.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_1_external_hsp1_2/build_config_decl.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_1/build_config.json b/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_1/build_config.json index 9f1508fd0e172016e9d7404684cdd25c1b2cb32f..bfcdd1f411964c341b2da46c46a9dd2588255925 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_1/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_1/build_config.json @@ -19,7 +19,7 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "hasMainModule": true, "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "har1_1", "moduleName": "har1_1", diff --git a/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_2/build_config.json b/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_2/build_config.json index fadb8532edad64040e4c98b0239c9536bde78578..80fa261ce42d5bb68641f0c83d11652618c80412 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_2/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_2_external_har1_2/build_config.json @@ -21,7 +21,7 @@ "hasMainModule": true, "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "har1_2", "moduleName": "har1_2", diff --git a/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_1/build_config.json b/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_1/build_config.json index cf47e9786431e9cc64ad3e365acef53f1393ade8..edb687a37ca3ba278114c6a39dd7faf92b720b75 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_1/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_1/build_config.json @@ -22,7 +22,7 @@ "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "hsp1_1", "moduleName": "hsp1_1", diff --git a/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config.json b/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config.json index 4e77dda86bef8418f9a58bd09092fe549e3270ea..f3c1f592bffd5dcacde9a2326f84075a9d560652 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config.json @@ -19,7 +19,7 @@ "hasMainModule": true, "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [ + "dependencyModuleList": [ { "packageName": "hsp1_2", "moduleName": "hsp1_2", diff --git a/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config_decl.json b/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config_decl.json index c41f6bb97f789b7db80709307333c3b28647ac63..4bdedd294e3dead39d17efb65423ec0ceaa5fce8 100755 --- a/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config_decl.json +++ b/ets2panda/driver/build_system/test/e2e/entry1_2_external_hsp1_2/build_config_decl.json @@ -19,5 +19,5 @@ "hasMainModule": true, "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [] + "dependencyModuleList": [] } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_1.json b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_1.json index 1ac8e12ca9f586b09645a7cabf58966b04407805..cd9db5abf37fbe32901ac444c1755a4f27f77504 100755 --- a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_1.json +++ b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_1.json @@ -17,6 +17,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_2.json b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_2.json index 5c5a833f71ef4b3c84665bffe48a2874de49acc9..5f466f0259269f1224bb5891486b6cf74ef24038 100755 --- a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_2.json +++ b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_har1_2.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_1.json b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_1.json index 8d2fc7de1a0246ef9990d09e4da0ac92751e42d4..fae07de1bbea171b8a3c50b691a9178b506c1734 100755 --- a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_1.json +++ b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_1.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_2.json b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_2.json index be6d80e0cf78eaff8f95044d244ee6fed34bfbd0..0ce1d6455766d95b5089258c0be02422d3fc82cd 100755 --- a/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_2.json +++ b/ets2panda/driver/build_system/test/e2e/externalProject/build_config_hsp1_2.json @@ -18,6 +18,6 @@ "declgenV2OutPath": "./dist/declgen/decl_ets2", "declgenBridgeCodePath": "./dist/declgen/ets", "buildSdkPath": "${absolute_path_to_build_system}/test/mock_sdk/", - "dependentModuleList": [], + "dependencyModuleList": [], "hasMainModule": true } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/mock_sdk/kits/@kit.AbilityKit.d.ets b/ets2panda/driver/build_system/test/mock_sdk/kits/@kit.AbilityKit.d.ets index 1d9441699408ae12ebadbbf8b0b41ca46f5118ea..2354a4f128a24de6e6b65378375ed4fb1d81232f 100644 --- a/ets2panda/driver/build_system/test/mock_sdk/kits/@kit.AbilityKit.d.ets +++ b/ets2panda/driver/build_system/test/mock_sdk/kits/@kit.AbilityKit.d.ets @@ -17,4 +17,4 @@ import {UIAbility} from '@ohos.app.ability.UIAbility' import {Lang} from '@arkts.lang' import {Hilog} from '@ohos.hilog' -export {UIAbility, Lang} +export {UIAbility, Lang, Hilog} diff --git a/ets2panda/driver/build_system/test/ut/base_modeTest/base_mode.test.ts b/ets2panda/driver/build_system/test/ut/base_modeTest/base_mode.test.ts index a8344603bd899377ca7c89a413cdd203ae7bda3b..3eca7a5f2b5e154b8a12c802179a2409547e2d33 100755 --- a/ets2panda/driver/build_system/test/ut/base_modeTest/base_mode.test.ts +++ b/ets2panda/driver/build_system/test/ut/base_modeTest/base_mode.test.ts @@ -17,2616 +17,2389 @@ import fs from 'fs'; import path from 'path'; import { BaseMode } from '../../../src/build/base_mode'; -import { BuildConfig, BUILD_TYPE, BUILD_MODE, OHOS_MODULE_TYPE, ModuleInfo, ES2PANDA_MODE } from '../../../src/types'; -import { BuildMode } from '../../../src/build/build_mode'; import { - ErrorCode, -} from '../../../src/error_code'; -import cluster, { - Cluster, -} from 'cluster'; - -interface Job { - id: string; - type?: string; - dependencies: string[]; - dependants: string[]; - fileList?: string[]; - isDeclFile?: boolean; - isAbcJob?: boolean; - isInCycle?: boolean; - result?: any; -} + BuildConfig, + BUILD_TYPE, + BUILD_MODE, + OHOS_MODULE_TYPE, + ModuleInfo, + JobInfo +} from '../../../src/types'; +import { + DependencyFileMap +} from '../../../src/dependency_analyzer' +import { BuildMode } from '../../../src/build/build_mode'; +import { ErrorCode } from '../../../src/util/error'; +import { Logger } from '../../../src/logger' +import * as mock from '../mock/data' +import { LANGUAGE_VERSION } from '../../../src/pre_define' -interface WorkerInfo { - worker: ThreadWorker; - isIdle: boolean; +interface LogDataFactory { + newInstance: jest.Mock; } -interface ThreadWorker { - postMessage: (message: any) => void; -} +function getMockMainModuleInfo(): ModuleInfo { + return { + isMainModule: true, + packageName: "test", + moduleRootPath: "/test/path", + moduleType: OHOS_MODULE_TYPE.HAR, + sourceRoots: ["./"], + entryFile: "index.ets", -interface Queues { - externalProgramQueue: Job[]; - abcQueue: Job[]; + arktsConfigFile: "/dist/cache/test/arktsconfig.json", + dependencies: [], + staticDependencyModules: new Map(), + dynamicDependencyModules: new Map(), + language: LANGUAGE_VERSION.ARKTS_1_2, + } } -interface DependencyFileConfig { - dependencies: Record; - dependants: Record; +interface ThreadWorker { + postMessage: (message: any) => void; } -jest.mock('os', () => ({ - ...jest.requireActual('os'), - type: jest.fn().mockReturnValue('Darwin') -})); - beforeEach(() => { - jest.clearAllMocks(); - process.exit = jest.fn() as any; + jest.clearAllMocks(); + process.exit = jest.fn() as any; }); beforeAll(() => { - const { execSync } = require('child_process'); - execSync('rimraf test/ut/mock/dist', { stdio: 'pipe' }); - const dir = path.resolve('dist/cache'); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } + const { execSync } = require('child_process'); + execSync('rimraf test/ut/mock/dist', { stdio: 'pipe' }); + const dir = path.resolve('dist/cache'); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + Logger.getInstance(mock.getMockLoggerGetter()) + + const PluginDriver = require('../../../src/plugins/plugins_driver').PluginDriver; + PluginDriver.getInstance = jest.fn().mockReturnValue({ + runPluginHook: jest.fn() + }); + }); // Test the functions of the base_mode.ts file. describe('test base_mode.ts file api', () => { - test('test collectModuleInfos', () => { - test_collectModuleInfos(); - }); - - test('test collectDependentCompileFiles002', () => { - test_collectDependentCompileFiles002(); - }); - - test('test shouldSkipFile', () => { - test_shouldSkipFile(); - }); - - test('test collectCompileFiles when test declaration files skip branch', () => { - test_collectCompileFiles_decl_ets_skip(); - }); - - test('test collectCompileFiles when test bytecode HAR branch', () => { - test_collectCompileFiles_bytecode_har(); - }); - - test('test collectCompileFiles when test file not in module path branch', () => { - test_collectCompileFiles_file_not_in_module(); - }); - - test('test createExternalProgramJob method branches', () => { - test_createExternalProgramJob_branches(); - }); - - test('test findStronglyConnectedComponents method branches', () => { - test_findStronglyConnectedComponents_branches(); - }); - - test('test assignTaskToIdleWorker abcQueue branch without job', () => { - test_assignTaskToIdleWorker_abcQueue_no_job(); - }); - - test('test assignTaskToIdleWorker with empty queues', () => { - test_assignTaskToIdleWorker_empty_queues(); - }); - - test('test generateDeclaration method', () => { - return test_generateDeclaration(); - }); - - test('test run method', () => { - test_runMethod(); - }); - - test('test declgen method', () => { - test_declgen_method(); - }); - - test('test getDependentModules with missing module', () => { - test_getDependentModules_missing_module(); - }); - - test('test collectDependencyModules language branches', () => { - test_collectDependencyModules_language_branches(); - }); - - test('test runConcurrent method', () => { - test_runConcurrent(); - }); - - test('test processAfterCompile method', () => { - test_processAfterCompile(); - }); - - test('test checkAllTasksDone method', () => { - test_checkAllTasksDone(); - }); - - test('test initCompileQueues method', () => { - test_initCompileQueues(); - }); - - test('test addJobToQueues method', () => { - test_addJobToQueues(); - }); - - test('test dealWithDependants method', () => { - test_dealWithDependants(); - }); - - test('test collectCompileJobs method', () => { - test_collectCompileJobs(); - }); - - test('test getJobDependants method', () => { - test_getJobDependants(); - }); - - test('test getJobDependencies method', () => { - test_getJobDependencies(); - }); - - test('test getSerializableConfig handles bigint values', () => { - test_getSerializableConfig(); - }); - - test('test collectDependentCompileFiles', () => { - test_collectDependentCompileFiles(); - }); - - test('test isFileChanged method branches', () => { - test_isFileChanged(); - }); + test('test collectModuleInfos', () => { + test_collectModuleInfos(); + }); - test('test loadHashCache method branches', () => { - test_loadHashCache(); - }); + test('test collectDependentCompileFiles002', () => { + test_collectDependentCompileFiles002(); + }); - test('test updateDependantJobs method', () => { - test_updateDependantJobs(); - }); + test('test processEntryFiles when test file not in module path branch', () => { + test_processEntryFiles_file_not_in_module(); + }); - test('test collectModuleInfos branches001', () => { - test_collectModuleInfos001(); - }); + test('test createExternalProgramJob method branches', () => { + test_createExternalProgramJob_branches(); + }); - test('test collectCompileFiles enableDeclgenEts2Ts false branch', () => { - test_collectCompileFiles_enableDeclgenEts2Ts_false(); - }); + test('test findStronglyConnectedComponents method branches', () => { + test_findStronglyConnectedComponents_branches(); + }); - test('test collectAbcFileFromByteCodeHar_missing_abc_path', () => { - test_collectAbcFileFromByteCodeHar_missing_abc_path(); - }); + test('test run method', () => { + test_runMethod(); + }); - test('test collectDependentCompileFiles isFileChanged branch', () => { - test_collectDependentCompileFiles_isFileChanged_branch(); - }); + test('test getDependentModules with missing module', () => { + test_getDependentModules_missing_module(); + }); - test('collectCompileJobs should skip entry files not in compileFiles', () => { - test_collectCompileJobs_should_skip_entry_files_not_in_compileFiles(); - }); + test('test collectDependencyModules language branches', () => { + test_collectDependencyModules_language_branches(); + }); -}); + test('test initCompileQueues method', () => { + test_initCompileQueues(); + }); -function test_collectCompileJobs_should_skip_entry_files_not_in_compileFiles() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } - } + test('test addJobToQueues method', () => { + test_addJobToQueues(); + }); - const baseMode = new TestBaseMode(mockConfig as any); + test('test dealWithDependants method', () => { + test_dealWithDependants(); + }); - const entryFile = '/path/to/entry.ets'; - const includedFile = '/path/to/included.ets'; + test('test getJobDependants method', () => { + test_getJobDependants(); + }); - (baseMode as any).entryFiles = new Set([entryFile]); - (baseMode as any).compileFiles = new Set(); + test('test getJobDependencies method', () => { + test_getJobDependencies(); + }); - const dependencyFileMap: { - dependencies: { [key: string]: string[] }, - dependants: { [key: string]: string[] } - } = { - dependencies: { - [entryFile]: ['dependency1.ets'] - }, - dependants: { - [entryFile]: ['dependant1.ets'] - } - }; - (baseMode as any).dependencyFileMap = dependencyFileMap; + test('test getSerializableConfig handles bigint values', () => { + test_getSerializableConfig(); + }); - const cycleGroups = new Map(); - jest.spyOn(baseMode as any, 'findStronglyConnectedComponents').mockReturnValue(cycleGroups); + test('test collectDependentCompileFiles', () => { + test_collectDependentCompileFiles(); + }); - jest.spyOn(baseMode as any, 'getJobDependencies').mockReturnValue(new Set()); - jest.spyOn(baseMode as any, 'getJobDependants').mockReturnValue(new Set()); - jest.spyOn(baseMode as any, 'dealWithDependants').mockImplementation(() => { }); - jest.spyOn(baseMode as any, 'createExternalProgramJob').mockImplementation(() => { }); - jest.spyOn(baseMode as any, 'getAbcJobId').mockImplementation((file) => `abc_${file}`); - jest.spyOn(baseMode as any, 'getExternalProgramJobId').mockImplementation((file) => `external_${file}`); + test('test isFileChanged method branches', () => { + test_isFileChanged(); + }); - const jobs = {}; + test('test loadHashCache method branches', () => { + test_loadHashCache(); + }); - (baseMode as any).collectCompileJobs(jobs); + test('test updateDependantJobs method', () => { + test_updateDependantJobs(); + }); - expect(Object.keys(jobs).length).toBe(0); - expect((baseMode as any).getJobDependencies).not.toHaveBeenCalledWith(['dependency1.ets'], expect.anything()); - expect((baseMode as any).getJobDependants).not.toHaveBeenCalledWith(['dependant1.ets'], expect.anything()); + test('test collectModuleInfos branches001', () => { + test_collectModuleInfos001(); + }); - (baseMode as any).entryFiles.add(includedFile); - (baseMode as any).compileFiles.add(includedFile); + test('test collectCompileFiles enableDeclgenEts2Ts false branch', () => { + test_collectCompileFiles_enableDeclgenEts2Ts_false(); + }); - dependencyFileMap.dependencies = { - ...dependencyFileMap.dependencies, - [includedFile]: [] - }; + test('test collectDependentCompileFiles isFileChanged branch', () => { + test_collectDependentCompileFiles_isFileChanged_branch(); + }); - dependencyFileMap.dependants = { - ...dependencyFileMap.dependants, - [includedFile]: [] - }; + test('collectCompileJobs should skip entry files not in compileFiles', () => { + test_collectCompileJobs_should_skip_entry_files_not_in_compileFiles(); + }); - jest.clearAllMocks(); -} + // NOTE: to be defined later + // test('test checkAllTasksDone method', () => { + // test_checkAllTasksDone(); + // }); + // + // test('test assignTaskToIdleWorker abcQueue branch without job', () => { + // test_assignTaskToIdleWorker_abcQueue_no_job(); + // }); + // + // test('test assignTaskToIdleWorker with empty queues', () => { + // test_assignTaskToIdleWorker_empty_queues(); + // }); + // + // test('test generateDeclaration method', () => { + // return test_generateDeclaration(); + // }); + // + // test('test shouldSkipFile', () => { + // test_shouldSkipFile(); + // }); + // + // test('test collectCompileFiles when test declaration files skip branch', () => { + // test_processEntryFiles_decl_ets_skip(); + // }); + // + // test('test collectCompileFiles when test bytecode HAR branch', () => { + // test_collectCompileFiles_bytecode_har(); + // }); + // + // test('test declgen method', () => { + // test_declgen_method(); + // }); + // + // test('test runConcurrent method', () => { + // test_runConcurrent(); + // }); + // + // test('test processAfterCompile method', () => { + // test_processAfterCompile(); + // }); + // + // test('test collectAbcFileFromByteCodeHar_missing_abc_path', () => { + // test_collectAbcFileFromByteCodeHar_missing_abc_path(); + // }); + // + // NOTE: to be moved to Dependency Analyzer ut + // test('test collectCompileJobs method', () => { + // test_collectCompileJobs(); + // }); -function test_collectDependentCompileFiles_isFileChanged_branch() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockConfig = { - packageName: "test", - moduleType: "har", - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } +}); - public testCollectDependentCompileFiles(): void { - (this as any).collectDependentCompileFiles(); - } +function test_collectCompileJobs_should_skip_entry_files_not_in_compileFiles() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; - public setIsFileChanged(fn: (file: string, abcFile: string) => boolean): void { - (this as any).isFileChanged = fn; - } - } - - const fs = require('fs'); - jest.spyOn(fs, 'statSync').mockReturnValue({ mtimeMs: Date.now() }); - jest.spyOn(fs, 'readFileSync').mockReturnValue('mocked file content'); - - const utils = require('../../../src/util/utils'); - jest.spyOn(utils, 'getFileHash').mockReturnValue("test-hash-123"); - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - - const baseMode = new TestBaseMode(mockConfig as any); - - const testFile1 = "/test/path/file1.ets"; - const testFile2 = "/test/path/file2.ets"; - - (baseMode as any).entryFiles = new Set([testFile1, testFile2]); - (baseMode as any).cacheDir = "./dist/cache"; - (baseMode as any).hashCache = {}; - (baseMode as any).abcFiles = new Set(); - (baseMode as any).compileFiles = new Map(); - (baseMode as any).allFiles = new Map(); - - (baseMode as any).moduleInfos = new Map(); - (baseMode as any).moduleInfos.set("test", { - packageName: "test", - moduleType: "har", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - arktsConfigFile: "./dist/cache/test/config.json", - compileFileInfos: [] - }); - - (baseMode as any).dependencyFileMap = { - dependencies: { - [testFile1]: [], - [testFile2]: [testFile1] - }, - dependants: { - [testFile1]: [testFile2], - [testFile2]: [] + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } } - }; - baseMode.setIsFileChanged(() => true); - (baseMode as any).isBuildConfigModified = false; + const baseMode = new TestBaseMode(mockConfig as any); - baseMode.testCollectDependentCompileFiles(); + const entryFile = '/path/to/entry.ets'; + const includedFile = '/path/to/included.ets'; - expect((baseMode as any).compileFiles.size).toBe(2); - expect((baseMode as any).compileFiles.has(testFile1)).toBe(true); - expect((baseMode as any).compileFiles.has(testFile2)).toBe(true); + (baseMode as any).entryFiles = new Set([entryFile]); + (baseMode as any).compileFiles = new Set(); - (baseMode as any).compileFiles.clear(); - (baseMode as any).abcFiles.clear(); - jest.restoreAllMocks(); -} + const dependencyFileMap: { + dependencies: { [key: string]: string[] }, + dependants: { [key: string]: string[] } + } = { + dependencies: { + [entryFile]: ['dependency1.ets'] + }, + dependants: { + [entryFile]: ['dependant1.ets'] + } + }; + (baseMode as any).dependencyFileMap = dependencyFileMap; -function test_collectAbcFileFromByteCodeHar_missing_abc_path() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn(), - printErrorAndExit: jest.fn() - }; - - const LogDataFactory = { - newInstance: jest.fn().mockReturnValue({ - code: "11410101", - description: "abc file not found in bytecode har test-module." - }) - }; - - const ErrorCode = { - BUILDSYSTEM_ABC_FILE_MISSING_IN_BCHAR: '11410101' - }; - - const mockConfig = { - packageName: "main-package", - moduleType: OHOS_MODULE_TYPE.SHARED, - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } + const cycleGroups = new Map(); + jest.spyOn(baseMode as any, 'findStronglyConnectedComponents').mockReturnValue(cycleGroups); - public testCollectAbcFileFromByteCodeHar(): void { - this.collectAbcFileFromByteCodeHar(); - } - } - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - - (global as any).LogDataFactory = LogDataFactory; - (global as any).ErrorCode = ErrorCode; - - const baseMode = new TestBaseMode(mockConfig as any); - (baseMode as any).abcFiles = new Set(); - - (baseMode as any).moduleInfos = new Map(); - (baseMode as any).moduleInfos.set("test-module", { - packageName: "test-module", - moduleType: OHOS_MODULE_TYPE.HAR, - byteCodeHar: true, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - arktsConfigFile: "./dist/cache/test/config.json", - compileFileInfos: [] - }); - - (baseMode as any).moduleInfos.set("test-module-2", { - packageName: "test-module-2", - moduleType: OHOS_MODULE_TYPE.HAR, - byteCodeHar: true, - abcPath: "/test/path/module2.abc", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - arktsConfigFile: "./dist/cache/test/config.json", - compileFileInfos: [] - }); - - baseMode.testCollectAbcFileFromByteCodeHar(); - expect((baseMode as any).abcFiles.has("/test/path/module2.abc")).toBe(true); - expect((baseMode as any).abcFiles.size).toBe(1); - - delete (global as any).LogDataFactory; - delete (global as any).ErrorCode; -} + jest.spyOn(baseMode as any, 'getJobDependencies').mockReturnValue(new Set()); + jest.spyOn(baseMode as any, 'getJobDependants').mockReturnValue(new Set()); + jest.spyOn(baseMode as any, 'dealWithDependants').mockImplementation(() => { }); + jest.spyOn(baseMode as any, 'createExternalProgramJob').mockImplementation(() => { }); + jest.spyOn(baseMode as any, 'getAbcJobId').mockImplementation((file) => `abc_${file}`); + jest.spyOn(baseMode as any, 'getExternalProgramJobId').mockImplementation((file) => `external_${file}`); -function test_collectCompileFiles_enableDeclgenEts2Ts_false() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockConfig = { - packageName: "test", - moduleType: OHOS_MODULE_TYPE.HAR, - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - enableDeclgenEts2Ts: false, - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } + const jobs = {}; - public testCollectCompileFiles(): void { - this.collectCompileFiles(); - } - } + (baseMode as any).collectCompileJobs(jobs); - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); + expect(Object.keys(jobs).length).toBe(0); + expect((baseMode as any).getJobDependencies).not.toHaveBeenCalledWith(['dependency1.ets'], expect.anything()); + expect((baseMode as any).getJobDependants).not.toHaveBeenCalledWith(['dependant1.ets'], expect.anything()); - const baseMode = new TestBaseMode(mockConfig as any); + (baseMode as any).entryFiles.add(includedFile); + (baseMode as any).compileFiles.add(includedFile); - (baseMode as any).entryFiles = new Set(['/test/path/file1.ets']); - (baseMode as any).moduleInfos = new Map(); - (baseMode as any).abcFiles = new Set(); - (baseMode as any).hashCache = {}; - (baseMode as any).compileFiles = new Map(); + dependencyFileMap.dependencies = { + ...dependencyFileMap.dependencies, + [includedFile]: [] + }; - baseMode.testCollectCompileFiles(); -} + dependencyFileMap.dependants = { + ...dependencyFileMap.dependants, + [includedFile]: [] + }; -class TestBaseModeMock extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } - public getMainModuleInfo(): ModuleInfo { - const path = require('path'); - const ARKTSCONFIG_JSON_FILE = 'arktsconfig.json'; - return { - isMainModule: true, - packageName: this.packageName, - moduleRootPath: this.moduleRootPath, - sourceRoots: this.sourceRoots, - arktsConfigFile: path.resolve(this.cacheDir, this.packageName, ARKTSCONFIG_JSON_FILE), - compileFileInfos: [], - dynamicDepModuleInfos: new Map(), - staticDepModuleInfos: new Map(), - dependenciesSet: new Set(), - dependentSet: new Set(), - moduleType: OHOS_MODULE_TYPE.HAR, - entryFile: "index.ets", - byteCodeHar: false, - declgenV1OutPath: path.resolve(this.cacheDir, "declgen"), - declgenV2OutPath: path.resolve(this.cacheDir, "declgen/v2"), - declgenBridgeCodePath: path.resolve(this.cacheDir, "bridge") - }; - } - public testCollectModuleInfos(): void { - return (this as any).collectModuleInfos(); - } + jest.clearAllMocks(); } -function test_collectModuleInfos1(mockLogger: any, LogDataFactory: any) { - const mockConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["test.ets"], - packageName: "", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - hasMainModule: true, - dependentModuleList: [], - }; - const baseMode = new TestBaseModeMock(mockConfig as any); - (baseMode as any).logger = mockLogger; - (baseMode as any).cacheDir = "./dist/cache"; - baseMode.testCollectModuleInfos(); - LogDataFactory.newInstance.mockClear(); - mockLogger.printError.mockClear(); -} +function test_collectDependentCompileFiles_isFileChanged_branch() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn() + }; -function test_collectModuleInfos2(mockLogger: any, LogDataFactory: any) { - const mockConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["test.ets"], - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - hasMainModule: true, - dependentModuleList: [ - { - packageName: "dep1", + const mockConfig = { + packageName: "test", + moduleType: "har", + buildMode: BUILD_MODE.DEBUG, + moduleRootPath: "/test/path", sourceRoots: ["./"], - entryFile: "index.ets" - } - ] - }; + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependencyModuleList: [] + }; - const baseMode = new TestBaseModeMock(mockConfig as any); - (baseMode as any).logger = mockLogger; - (baseMode as any).cacheDir = "./dist/cache"; + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } - baseMode.testCollectModuleInfos(); - LogDataFactory.newInstance.mockClear(); - mockLogger.printError.mockClear(); + public testCollectDependentCompileFiles(): void { + (this as any).collectDependentCompileFiles(); + } -} + public setIsFileChanged(fn: (file: string, abcFile: string) => boolean): void { + (this as any).isFileChanged = fn; + } + } -function test_collectModuleInfos3(mockLogger: any, LogDataFactory: any) { - const mockConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["test.ets"], - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - hasMainModule: true, - dependentModuleList: [ - { - packageName: "dep2", - modulePath: "/test/dep2", - entryFile: "index.ets" - } - ] - }; - - const baseMode = new TestBaseModeMock(mockConfig as any); - (baseMode as any).logger = mockLogger; - (baseMode as any).cacheDir = "./dist/cache"; - - baseMode.testCollectModuleInfos(); - LogDataFactory.newInstance.mockClear(); - mockLogger.printError.mockClear(); -} + const fs = require('fs'); + jest.spyOn(fs, 'statSync').mockReturnValue({ mtimeMs: Date.now() }); + jest.spyOn(fs, 'readFileSync').mockReturnValue('mocked file content'); -function test_collectModuleInfos4(mockLogger: any, LogDataFactory: any) { - const mockConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["test.ets"], - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - hasMainModule: true, - dependentModuleList: [ - { - packageName: "dep3", - modulePath: "/test/dep3", - sourceRoots: ["./"] - } - ] - }; - - const baseMode = new TestBaseModeMock(mockConfig as any); - (baseMode as any).logger = mockLogger; - (baseMode as any).cacheDir = "./dist/cache"; - baseMode.testCollectModuleInfos(); - LogDataFactory.newInstance.mockClear(); - mockLogger.printError.mockClear(); -} + const utils = require('../../../src/util/utils'); + jest.spyOn(utils, 'getFileHash').mockReturnValue("test-hash-123"); -function test_collectModuleInfos001() { - const mockLogger = { printError: jest.fn(), printInfo: jest.fn() }; - const LogDataFactory = { newInstance: jest.fn().mockReturnValue({ code: "123", message: "Test error" }) }; - const ErrorCode = { - BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL: '11410003', - BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_CORRECT_FAIL: '11410004' - }; - const path = require('path'); - const ARKTSCONFIG_JSON_FILE = 'arktsconfig.json'; - (global as any).LogDataFactory = LogDataFactory; - (global as any).ErrorCode = ErrorCode; - - test_collectModuleInfos1(mockLogger as any, LogDataFactory as any); - test_collectModuleInfos2(mockLogger as any, LogDataFactory as any); - test_collectModuleInfos3(mockLogger as any, LogDataFactory as any); - test_collectModuleInfos4(mockLogger as any, LogDataFactory as any); - - delete (global as any).LogDataFactory; - delete (global as any).ErrorCode; -} + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance = jest.fn().mockReturnValue(mockLogger); -function test_updateDependantJobs1(baseMode: any) { - const jobId = "job1"; - const processingJobs = new Set([jobId, "job2"]); - const jobs: Record = { - "job1": { - id: "job1", - dependencies: [], - dependants: ["job2", "job3"], - fileList: ["/test/file1.ets"], - isAbcJob: true - }, - "job2": { - id: "job2", - dependencies: ["job1", "job4"], - dependants: [], - fileList: ["/test/file2.ets"], - isAbcJob: true - }, - "job3": { - id: "job3", - dependencies: ["job1"], - dependants: [], - fileList: ["/test/file3.ets"], - isAbcJob: true - } - }; + const baseMode = new TestBaseMode(mockConfig as any); - const queues: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; + const testFile1 = "/test/path/file1.ets"; + const testFile2 = "/test/path/file2.ets"; - baseMode.testUpdateDependantJobs(jobId, processingJobs, jobs, queues); + (baseMode as any).entryFiles = new Set([testFile1, testFile2]); + (baseMode as any).cacheDir = "./dist/cache"; + (baseMode as any).hashCache = {}; + (baseMode as any).abcFiles = new Set(); + (baseMode as any).compileFiles = new Map(); + (baseMode as any).allFiles = new Map(); - expect(processingJobs.has(jobId)).toBe(false); - expect(jobs["job2"].dependencies).not.toContain("job1"); - expect(jobs["job2"].dependencies).toContain("job4"); - expect(jobs["job3"].dependencies.length).toBe(0); - expect((baseMode as any).addJobToQueues).toHaveBeenCalledWith(jobs["job3"], queues); -} + (baseMode as any).moduleInfos = new Map(); + (baseMode as any).moduleInfos.set("test", { + packageName: "test", + moduleType: "har", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + arktsConfigFile: "./dist/cache/test/config.json", + compileFileInfos: [] + }); -function test_updateDependantJobs2(global: any, baseMode: any) { - (global as any).finishedJob = []; - (baseMode as any).addJobToQueues.mockClear(); - - const jobId = "job5"; - const processingJobs = new Set([jobId]); - const jobs: Record = { - "job5": { - id: "job5", - dependencies: [], - dependants: ["job6", "nonExistingJob"], - fileList: ["/test/file5.ets"], - isAbcJob: true - }, - "job6": { - id: "job6", - dependencies: ["job5"], - dependants: [], - fileList: ["/test/file6.ets"], - isAbcJob: true - } - }; + (baseMode as any).dependencyFileMap = { + dependencies: { + [testFile1]: [], + [testFile2]: [testFile1] + }, + dependants: { + [testFile1]: [testFile2], + [testFile2]: [] + } + }; - const queues: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; + baseMode.setIsFileChanged(() => true); + (baseMode as any).isBuildConfigModified = false; + + baseMode.testCollectDependentCompileFiles(); - baseMode.testUpdateDependantJobs(jobId, processingJobs, jobs, queues); + expect((baseMode as any).compileFiles.size).toBe(2); + expect((baseMode as any).compileFiles.has(testFile1)).toBe(true); + expect((baseMode as any).compileFiles.has(testFile2)).toBe(true); - expect(processingJobs.has(jobId)).toBe(false); - expect((baseMode as any).addJobToQueues).toHaveBeenCalledWith(jobs["job6"], queues); + (baseMode as any).compileFiles.clear(); + (baseMode as any).abcFiles.clear(); + jest.restoreAllMocks(); } -function test_updateDependantJobs3(global: any, baseMode: any) { - (global as any).finishedJob = []; - (baseMode as any).addJobToQueues.mockClear(); - - const jobId = "job7"; - const processingJobs = new Set([jobId]); - const jobs: Record = { - "job7": { - id: "job7", - dependencies: [], - dependants: ["job8"], - fileList: ["/test/file7.ets"], - isAbcJob: true - }, - "job8": { - id: "job8", - dependencies: ["job9"], - dependants: [], - fileList: ["/test/file8.ets"], - isAbcJob: true - } - }; +// NOTE: to be defined later +/* +function test_collectAbcFileFromByteCodeHar_missing_abc_path() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn(), + printErrorAndExit: jest.fn() + }; - const queues: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; + const LogDataFactory = { + newInstance: jest.fn().mockReturnValue({ + code: "11410101", + description: "abc file not found in bytecode har test-module." + }) + }; - baseMode.testUpdateDependantJobs(jobId, processingJobs, jobs, queues); + const ErrorCode = { + BUILDSYSTEM_ABC_FILE_MISSING_IN_BCHAR: '11410101' + }; - expect(jobs["job8"].dependencies).toEqual(["job9"]); - expect((baseMode as any).addJobToQueues).not.toHaveBeenCalled(); -} + const mockConfig = { + packageName: "main-package", + moduleType: OHOS_MODULE_TYPE.SHARED, + buildMode: BUILD_MODE.DEBUG, + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependencyModuleList: [] + }; -function test_updateDependantJobs() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - (global as any).finishedJob = []; - class TestBuildMode extends BuildMode { - public testUpdateDependantJobs(jobId: string, processingJobs: Set, jobs: Record, queues: Queues): void { - return (this as any).updateDependantJobs(jobId, processingJobs, jobs, queues); - } - } - const baseMode = new TestBuildMode(mockConfig as any); - (baseMode as any).addJobToQueues = jest.fn(); - test_updateDependantJobs1(baseMode as any); - test_updateDependantJobs2(global as any, baseMode as any); - test_updateDependantJobs3(global as any, baseMode as any); - - delete (global as any).finishedJob; -} + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } -function test_loadHashCache() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - const fs = require('fs'); - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockLogData = { code: "123", message: "Test error" }; - - const LogDataFactory = { - newInstance: jest.fn().mockReturnValue(mockLogData) - }; - - const ErrorCode = { - BUILDSYSTEM_LOAD_HASH_CACHE_FAIL: '11410100' - }; - - class TestBuildMode extends BuildMode { - public testLoadHashCache(): Record { - return (this as any).loadHashCache(); + public testCollectAbcFileFromByteCodeHar(): void { + this.collectAbcFileFromByteCodeHar(); + } } - } - - const baseMode = new TestBuildMode(mockConfig as any); - (baseMode as any).logger = mockLogger; - (baseMode as any).hashCacheFile = "/test/cache/hash_cache.json"; - - (global as any).LogDataFactory = LogDataFactory; - (global as any).ErrorCode = ErrorCode; - - jest.spyOn(fs, 'existsSync').mockReturnValueOnce(false); - let result = baseMode.testLoadHashCache(); - expect(result).toEqual({}); - - (baseMode as any).entryFiles = new Set(['file1.ets', 'file2.ets']); - jest.spyOn(fs, 'existsSync').mockReturnValueOnce(true); - jest.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"file1.ets":"hash1","file2.ets":"hash2"}'); - result = baseMode.testLoadHashCache(); - expect(result).toEqual({ - "file1.ets": "hash1", - "file2.ets": "hash2" - }); - - jest.spyOn(fs, 'existsSync').mockReturnValueOnce(true); - jest.spyOn(fs, 'readFileSync').mockImplementationOnce(() => { - throw new Error("File read error"); - }); - result = baseMode.testLoadHashCache(); - expect(result).toEqual({}); - - delete (global as any).LogDataFactory; - delete (global as any).ErrorCode; - jest.restoreAllMocks(); -} -function test_isFileChanged() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBuildMode extends BuildMode { - public testIsFileChanged(etsFilePath: string, abcFilePath: string): boolean { - return (this as any).isFileChanged(etsFilePath, abcFilePath); - } - } + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - const fs = require('fs'); - const existsSyncSpy = jest.spyOn(fs, 'existsSync'); - const statSyncSpy = jest.spyOn(fs, 'statSync'); + (global as any).LogDataFactory = LogDataFactory; + (global as any).ErrorCode = ErrorCode; - (global as any).getFileHash = jest.fn(); + const baseMode = new TestBaseMode(mockConfig as any); + (baseMode as any).abcFiles = new Set(); - const baseMode = new TestBuildMode(mockConfig as any); - (baseMode as any).hashCache = {}; + (baseMode as any).moduleInfos = new Map(); + (baseMode as any).moduleInfos.set("test-module", { + packageName: "test-module", + moduleType: OHOS_MODULE_TYPE.HAR, + byteCodeHar: true, + moduleRootPath: "/test/path", + sourceRoots: ["./"], + arktsConfigFile: "./dist/cache/test/config.json", + compileFileInfos: [] + }); - existsSyncSpy.mockReturnValueOnce(false); - let result = baseMode.testIsFileChanged('/test/file1.ets', '/test/file1.abc'); - expect(result).toBe(true); - expect(existsSyncSpy).toHaveBeenCalledWith('/test/file1.abc'); - expect(statSyncSpy).not.toHaveBeenCalled(); + (baseMode as any).moduleInfos.set("test-module-2", { + packageName: "test-module-2", + moduleType: OHOS_MODULE_TYPE.HAR, + byteCodeHar: true, + abcPath: "/test/path/module2.abc", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + arktsConfigFile: "./dist/cache/test/config.json", + compileFileInfos: [] + }); - existsSyncSpy.mockReturnValueOnce(true); - statSyncSpy.mockReturnValueOnce({ mtimeMs: 200 }); - statSyncSpy.mockReturnValueOnce({ mtimeMs: 100 }); - result = baseMode.testIsFileChanged('/test/file2.ets', '/test/file2.abc'); - expect(result).toBe(true); - expect(statSyncSpy).toHaveBeenCalledWith('/test/file2.ets'); - expect(statSyncSpy).toHaveBeenCalledWith('/test/file2.abc'); + baseMode.testCollectAbcFileFromByteCodeHar(); + expect((baseMode as any).abcFiles.has("/test/path/module2.abc")).toBe(true); + expect((baseMode as any).abcFiles.size).toBe(1); - jest.restoreAllMocks(); - delete (global as any).getFileHash; + delete (global as any).LogDataFactory; + delete (global as any).ErrorCode; } +*/ -function test_collectDependentCompileFiles() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const LogDataFactory = { - newInstance: jest.fn().mockReturnValue({ code: "123", message: "Test error" }) - }; - - const ErrorCode = { - BUILDSYSTEM_Dependency_Analyze_FAIL: '11410001', - BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL: '11410002' - }; - - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBuildMode extends BuildMode { - public testCollectDependentCompileFiles(): void { - return (this as any).collectDependentCompileFiles(); +function test_collectCompileFiles_enableDeclgenEts2Ts_false() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn() + }; + + const mockConfig = { + packageName: "test", + moduleType: OHOS_MODULE_TYPE.HAR, + buildMode: BUILD_MODE.DEBUG, + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + enableDeclgenEts2Ts: false, + dependencyModuleList: [] + }; + + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + + public testCollectCompileFiles(): void { + super.processEntryFiles(); + } } - } - (global as any).LogDataFactory = LogDataFactory; - (global as any).ErrorCode = ErrorCode; - (global as any).getFileHash = jest.fn().mockReturnValue("hash123"); + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - const baseMode = new TestBuildMode(mockConfig as any); - (baseMode as any).logger = mockLogger; - (baseMode as any).cacheDir = "/test/cache"; - (baseMode as any).hashCache = {}; - (baseMode as any).abcFiles = new Set(); - (baseMode as any).allFiles = new Map(); - (baseMode as any).compileFiles = new Map(); + const baseMode = new TestBaseMode(mockConfig as any); - { - (baseMode as any).dependencyFileMap = null; + baseMode.entryFiles = new Set(['/test/path/file1.ets']); + baseMode.moduleInfos = new Map(); + baseMode.abcFiles = new Set(); + baseMode.filesHashCache = {}; - baseMode.testCollectDependentCompileFiles(); + baseMode.testCollectCompileFiles(); +} + +class TestBaseModeMock extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + public getMainModuleInfo(): ModuleInfo { + const ARKTSCONFIG_JSON_FILE = 'arktsconfig.json'; + return { + isMainModule: true, + packageName: "entry", + moduleRootPath: "./", + moduleType: OHOS_MODULE_TYPE.HAR, + sourceRoots: ["./"], + entryFile: "index.ets", + arktsConfigFile: path.resolve(this.cacheDir, "entry", ARKTSCONFIG_JSON_FILE), + declgenV1OutPath: path.resolve(this.cacheDir, "declgen"), + declgenV2OutPath: path.resolve(this.cacheDir, "declgen/v2"), + declgenBridgeCodePath: path.resolve(this.cacheDir, "bridge"), + dependencies: [], + dynamicDependencyModules: new Map(), + staticDependencyModules: new Map(), + byteCodeHar: false, + }; + } + public testCollectModuleInfos(): void { + return this.collectModuleInfos(); + } +} - mockLogger.printError.mockClear(); +function test_collectModuleInfos1(LogDataFactory: LogDataFactory) { + const mockConfig = mock.getMockedBuildConfig() + const baseMode = new TestBaseModeMock(mockConfig); + baseMode.testCollectModuleInfos(); LogDataFactory.newInstance.mockClear(); - } +} - { - (baseMode as any).dependencyFileMap = { - dependants: { - "/test/other/path/file.ets": [] - } +function test_collectModuleInfos2(LogDataFactory: LogDataFactory) { + const mockConfig = { + buildMode: BUILD_MODE.DEBUG, + compileFiles: ["test.ets"], + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + hasMainModule: true, + dependencyModuleList: [ + { + packageName: "dep1", + sourceRoots: ["./"], + entryFile: "index.ets" + } + ] }; - (baseMode as any).entryFiles = new Set(["/test/other/path/file.ets"]); - (baseMode as any).moduleInfos = new Map([ - ["test", { + + const baseMode = new TestBaseModeMock(mockConfig as any); + baseMode.testCollectModuleInfos(); + LogDataFactory.newInstance.mockClear(); +} + +function test_collectModuleInfos3(LogDataFactory: LogDataFactory) { + const mockConfig = { + buildMode: BUILD_MODE.DEBUG, + compileFiles: ["test.ets"], packageName: "test", moduleRootPath: "/test/path", - sourceRoots: ["./"] - }] - ]); + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + hasMainModule: true, + dependencyModuleList: [ + { + packageName: "dep2", + modulePath: "/test/dep2", + entryFile: "index.ets" + } + ] + }; - baseMode.testCollectDependentCompileFiles(); + const baseMode = new TestBaseModeMock(mockConfig as any); - mockLogger.printError.mockClear(); + baseMode.testCollectModuleInfos(); LogDataFactory.newInstance.mockClear(); - } - - delete (global as any).LogDataFactory; - delete (global as any).ErrorCode; - delete (global as any).getFileHash; - jest.restoreAllMocks(); } -function test_getSerializableConfig() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG, - arkts: { - someFunction: () => { } - }, - bigIntValue: BigInt(9007199254740991) - }; - - class TestBuildMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } +function test_collectModuleInfos4(LogDataFactory: LogDataFactory) { + const mockConfig = { + buildMode: BUILD_MODE.DEBUG, + compileFiles: ["test.ets"], + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + hasMainModule: true, + dependencyModuleList: [ + { + packageName: "dep3", + modulePath: "/test/dep3", + sourceRoots: ["./"] + } + ] + }; - public testGetSerializableConfig(): Object { - return (this as any).getSerializableConfig(); - } - } + const baseMode = new TestBaseModeMock(mockConfig as any); + baseMode.testCollectModuleInfos(); + LogDataFactory.newInstance.mockClear(); +} - const baseMode = new TestBuildMode(mockConfig as any); +function test_collectModuleInfos001() { + const LogDataFactory = { newInstance: jest.fn().mockReturnValue({ code: "123", message: "Test error" }) }; - const result = baseMode.testGetSerializableConfig(); + test_collectModuleInfos1(LogDataFactory); + test_collectModuleInfos2(LogDataFactory); + test_collectModuleInfos3(LogDataFactory); + test_collectModuleInfos4(LogDataFactory); - expect(result).not.toHaveProperty('arkts'); +} - expect(result).not.toHaveProperty('bigIntValue'); +function test_updateDependantJobs1(baseMode: TestBuildMode) { + const jobId = "job1"; + const processingJobs = new Set([jobId, "job2"]); + const jobs: Record = { + "job1": { + id: "job1", + jobDependencies: [], + jobDependants: ["job2", "job3"], + fileList: ["/test/file1.ets"], + isAbcJob: true + }, + "job2": { + id: "job2", + jobDependencies: ["job1", "job4"], + jobDependants: [], + fileList: ["/test/file2.ets"], + isAbcJob: true + }, + "job3": { + id: "job3", + jobDependencies: ["job1"], + jobDependants: [], + fileList: ["/test/file3.ets"], + isAbcJob: true + } + }; - expect(result).toHaveProperty('packageName', 'test'); - expect(result).toHaveProperty('moduleRootPath', '/test/path'); - expect(result).toHaveProperty('sourceRoots'); -} + baseMode.testUpdateDependantJobs(jobId, processingJobs, jobs); -function test_getJobDependencies() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBuildMode extends BuildMode { - public testGetJobDependencies(fileDeps: string[], cycleFiles: Map): Set { - return (this as any).getJobDependencies(fileDeps, cycleFiles); - } - } - - const baseMode = new TestBuildMode(mockConfig as any); - { - const fileDeps = ['/test/path/file1.ets', '/test/path/file2.ets']; - const cycleFiles = new Map(); - const result = baseMode.testGetJobDependencies(fileDeps, cycleFiles); - expect(result.size).toBe(2); - expect(result.has('0/test/path/file1.ets')).toBe(true); - expect(result.has('0/test/path/file2.ets')).toBe(true); - } - - { - const fileDeps = ['/test/path/file1.ets', '/test/path/cycle1.ets']; - const cycleFiles = new Map(); - cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1', 'cycle-group-2']); - const result = baseMode.testGetJobDependencies(fileDeps, cycleFiles); - expect(result.size).toBe(3); - expect(result.has('0/test/path/file1.ets')).toBe(true); - expect(result.has('cycle-group-1')).toBe(true); - expect(result.has('cycle-group-2')).toBe(true); - expect(result.has('0/test/path/cycle1.ets')).toBe(false); - } - - { - const fileDeps = ['/test/path/cycle1.ets', '/test/path/cycle2.ets']; - const cycleFiles = new Map(); - cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1']); - cycleFiles.set('/test/path/cycle2.ets', ['cycle-group-2']); - const result = baseMode.testGetJobDependencies(fileDeps, cycleFiles); - expect(result.size).toBe(2); - expect(result.has('cycle-group-1')).toBe(true); - expect(result.has('cycle-group-2')).toBe(true); - expect(result.has('0/test/path/cycle1.ets')).toBe(false); - expect(result.has('0/test/path/cycle2.ets')).toBe(false); - } + expect(processingJobs.has(jobId)).toBe(false); + expect(jobs["job2"].jobDependencies).not.toContain("job1"); + expect(jobs["job2"].jobDependencies).toContain("job4"); + expect(jobs["job3"].jobDependencies.length).toBe(0); + expect(baseMode.addJobToQueues).toHaveBeenCalledWith(jobs["job3"]); } -function test_getJobDependants() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - class TestBuildMode extends BuildMode { - public testGetJobDependants(fileDeps: string[], cycleFiles: Map): Set { - return (this as any).getJobDependants(fileDeps, cycleFiles); - } - } - const baseMode = new TestBuildMode(mockConfig as any); - { - const fileDeps = ['/test/path/file1.ets', '/test/path/file2.ets']; - const cycleFiles = new Map(); - const result = baseMode.testGetJobDependants(fileDeps, cycleFiles); - expect(result.size).toBe(4); - expect(result.has('1/test/path/file1.ets')).toBe(true); - expect(result.has('0/test/path/file1.ets')).toBe(true); - expect(result.has('1/test/path/file2.ets')).toBe(true); - expect(result.has('0/test/path/file2.ets')).toBe(true); - } - - { - const fileDeps = ['/test/path/file1.d.ets', '/test/path/file2.ets']; - const cycleFiles = new Map(); - const result = baseMode.testGetJobDependants(fileDeps, cycleFiles); - expect(result.size).toBe(3); - expect(result.has('1/test/path/file1.d.ets')).toBe(false); - expect(result.has('0/test/path/file1.d.ets')).toBe(true); - expect(result.has('1/test/path/file2.ets')).toBe(true); - expect(result.has('0/test/path/file2.ets')).toBe(true); - } - - { - const fileDeps = ['/test/path/file1.ets', '/test/path/cycle1.ets']; - const cycleFiles = new Map(); - cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1', 'cycle-group-2']); - const result = baseMode.testGetJobDependants(fileDeps, cycleFiles); - expect(result.size).toBe(5); - expect(result.has('1/test/path/file1.ets')).toBe(true); - expect(result.has('0/test/path/file1.ets')).toBe(true); - expect(result.has('cycle-group-1')).toBe(true); - expect(result.has('cycle-group-2')).toBe(true); - expect(result.has('0/test/path/cycle1.ets')).toBe(false); - } -} +function test_updateDependantJobs2(global: any, baseMode: any) { + (global as any).finishedJob = []; + (baseMode as any).addJobToQueues.mockClear(); + + const jobId = "job5"; + const processingJobs = new Set([jobId]); + const jobs: Record = { + "job5": { + id: "job5", + jobDependencies: [], + jobDependants: ["job6", "nonExistingJob"], + fileList: ["/test/file5.ets"], + isAbcJob: true + }, + "job6": { + id: "job6", + jobDependencies: ["job5"], + jobDependants: [], + fileList: ["/test/file6.ets"], + isAbcJob: true + } + }; -function test_collectCompileJobs() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBuildMode extends BuildMode { - public testCollectCompileJobs(jobs: Record): void { - return (this as any).collectCompileJobs(jobs); - } + baseMode.testUpdateDependantJobs(jobId, processingJobs, jobs); - constructor(buildConfig: any) { - super(buildConfig); - (this as any).dependencyFileMap = { - dependencies: { - '/test/path/file1.ets': ['/test/path/file2.ets'], - '/test/path/file3.ets': ['/test/path/file4.ets'], - '/test/path/file5.d.ets': [] + expect(processingJobs.has(jobId)).toBe(false); + expect((baseMode as any).addJobToQueues).toHaveBeenCalledWith(jobs["job6"]); +} + +function test_updateDependantJobs3(global: any, baseMode: any) { + (global as any).finishedJob = []; + (baseMode as any).addJobToQueues.mockClear(); + + const jobId = "job7"; + const processingJobs = new Set([jobId]); + const jobs: Record = { + "job7": { + id: "job7", + jobDependencies: [], + jobDependants: ["job8"], + fileList: ["/test/file7.ets"], + isAbcJob: true }, - dependants: { - '/test/path/file2.ets': ['/test/path/file1.ets'], - '/test/path/file4.ets': ['/test/path/file3.ets'] + "job8": { + id: "job8", + jobDependencies: ["job9"], + jobDependants: [], + fileList: ["/test/file8.ets"], + isAbcJob: true } - }; + }; - (this as any).entryFiles = new Set(['/test/path/file1.ets']); - (this as any).compileFiles = new Map([ - ['/test/path/file1.ets', { filePath: '/test/path/file1.ets' }] - ]); + baseMode.testUpdateDependantJobs(jobId, processingJobs, jobs); + + expect(jobs["job8"].jobDependencies).toEqual(["job9"]); + expect((baseMode as any).addJobToQueues).not.toHaveBeenCalled(); +} - (this as any).moduleInfos = new Map(); - (this as any).moduleInfos.set("test", { +function test_updateDependantJobs() { + const mockConfig = { packageName: "test", moduleRootPath: "/test/path", - arktsConfigFile: "/test/path/config.json" - }); - - (this as any).allFiles = new Map(); - - (this as any).getJobDependencies = jest.fn().mockImplementation(() => new Set(['dep1', 'dep2'])); - (this as any).getJobDependants = jest.fn().mockImplementation(() => new Set(['dep3', 'dep4'])); - (this as any).getAbcJobId = jest.fn().mockImplementation((file) => '1' + file); - (this as any).getExternalProgramJobId = jest.fn().mockImplementation((file) => '0' + file); - (this as any).createExternalProgramJob = jest.fn(); - (this as any).dealWithDependants = jest.fn(); - (this as any).findStronglyConnectedComponents = jest.fn().mockImplementation(() => { - const cycleGroups = new Map(); - const cycle1 = new Set(['/test/path/cycle1.ets', '/test/path/cycle2.ets']); - cycleGroups.set('cycle-group-1', cycle1); - return cycleGroups; - }); + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; + global.finishedJob = []; + class TestBuildMode extends BuildMode { + public testUpdateDependantJobs(jobId: string, processingJobs: Set, jobs: Record): void { + return this.updateDependantJobs(jobId, processingJobs, jobs); + } } - } + const baseMode = new TestBuildMode(mockConfig as any); + (baseMode as any).addJobToQueues = jest.fn(); + test_updateDependantJobs1(baseMode); + test_updateDependantJobs2(global as any, baseMode as any); + test_updateDependantJobs3(global as any, baseMode as any); - const baseMode = new TestBuildMode(mockConfig as any); - - (baseMode as any).dependencyFileMap.dependants['/test/path/file6.ets'] = ['/test/path/file7.ets']; - - const jobs: Record = {}; + delete (global as any).finishedJob; +} - const findComponentsSpy = jest.spyOn(baseMode as any, 'findStronglyConnectedComponents'); - const getJobDependenciesSpy = jest.spyOn(baseMode as any, 'getJobDependencies'); - const getJobDependantsSpy = jest.spyOn(baseMode as any, 'getJobDependants'); - const getAbcJobIdSpy = jest.spyOn(baseMode as any, 'getAbcJobId'); - const getExternalProgramJobIdSpy = jest.spyOn(baseMode as any, 'getExternalProgramJobId'); - const createExternalProgramJobSpy = jest.spyOn(baseMode as any, 'createExternalProgramJob'); - const dealWithDependantsSpy = jest.spyOn(baseMode as any, 'dealWithDependants'); +function test_loadHashCache() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; - baseMode.testCollectCompileJobs(jobs); + const fs = require('fs'); + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn() + }; - expect((baseMode as any).dependencyFileMap.dependencies['/test/path/file6.ets']).toEqual([]); + const mockLogData = { code: "123", message: "Test error" }; - expect(findComponentsSpy).toHaveBeenCalledWith((baseMode as any).dependencyFileMap); + const LogDataFactory = { + newInstance: jest.fn().mockReturnValue(mockLogData) + }; - const cycleFiles = new Map(); - cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1']); - cycleFiles.set('/test/path/cycle2.ets', ['cycle-group-1']); + const ErrorCode = { + BUILDSYSTEM_LOAD_HASH_CACHE_FAIL: '11410100' + }; - expect(getJobDependenciesSpy).toHaveBeenCalled(); + class TestBuildMode extends BuildMode { + public testLoadHashCache(): Record { + return (this as any).loadHashCache(); + } + } - expect(getAbcJobIdSpy).toHaveBeenCalledWith('/test/path/file1.ets'); - expect(getAbcJobIdSpy).toHaveBeenCalledWith('/test/path/file3.ets'); - expect(getAbcJobIdSpy).not.toHaveBeenCalledWith('/test/path/file5.d.ets'); + const baseMode = new TestBuildMode(mockConfig as any); + (baseMode as any).logger = mockLogger; + (baseMode as any).hashCacheFile = "/test/cache/hash_cache.json"; - expect(jobs['1/test/path/file1.ets']).toBeDefined(); - expect(jobs['1/test/path/file3.ets']).toBeDefined(); + (global as any).LogDataFactory = LogDataFactory; + (global as any).ErrorCode = ErrorCode; - expect(createExternalProgramJobSpy).toHaveBeenCalled(); + jest.spyOn(fs, 'existsSync').mockReturnValueOnce(false); + let result = baseMode.testLoadHashCache(); + expect(result).toEqual({}); - expect((baseMode as any).allFiles.has('/test/path/file5.d.ets')).toBe(true); + (baseMode as any).entryFiles = new Set(['file1.ets', 'file2.ets']); + jest.spyOn(fs, 'existsSync').mockReturnValueOnce(true); + jest.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"file1.ets":"hash1","file2.ets":"hash2"}'); + result = baseMode.testLoadHashCache(); + expect(result).toEqual({ + "file1.ets": "hash1", + "file2.ets": "hash2" + }); - expect(getJobDependantsSpy).toHaveBeenCalled(); - expect(dealWithDependantsSpy).toHaveBeenCalled(); + jest.spyOn(fs, 'existsSync').mockReturnValueOnce(true); + jest.spyOn(fs, 'readFileSync').mockImplementationOnce(() => { + throw new Error("File read error"); + }); + result = baseMode.testLoadHashCache(); + expect(result).toEqual({}); - jest.restoreAllMocks(); + delete (global as any).LogDataFactory; + delete (global as any).ErrorCode; + jest.restoreAllMocks(); } -function test_dealWithDependants() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - class TestBuildMode extends BuildMode { - public testDealWithDependants(cycleFiles: Map, key: string, jobs: Record, dependants: Set): void { - return (this as any).dealWithDependants(cycleFiles, key, jobs, dependants); - } - } - const baseMode = new TestBuildMode(mockConfig as any); - { - const cycleFiles = new Map(); - cycleFiles.set('file1.ets', ['cycle-1', 'cycle-2']); - const jobs: Record = { - 'cycle-1': { - id: 'cycle-1', - fileList: ['file1.ets'], - dependencies: [], - dependants: ['dep1', 'dep2'], - isAbcJob: false - }, - 'cycle-2': { - id: 'cycle-2', - fileList: ['file1.ets', 'file2.ets'], - dependencies: [], - dependants: ['dep3'], - isAbcJob: false - } - }; - const dependants = new Set(['dep4', 'dep5', 'cycle-1']); - baseMode.testDealWithDependants(cycleFiles, 'file1.ets', jobs, dependants); - expect(jobs['cycle-1'].dependants).toEqual(expect.arrayContaining(['dep1', 'dep2', 'dep4', 'dep5'])); - expect(jobs['cycle-1'].dependants).not.toContain('cycle-1'); - expect(jobs['cycle-2'].dependants).toEqual(expect.arrayContaining(['dep3', 'dep4', 'dep5'])); - expect(jobs['cycle-2'].dependants).not.toContain('cycle-1'); - } - { - const cycleFiles = new Map(); - const jobs: Record = { - '0file2.ets': { - id: '0file2.ets', - fileList: ['file2.ets'], - dependencies: [], - dependants: ['dep1', 'dep2'], - isAbcJob: false - } +function test_isFileChanged() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG }; - const dependants = new Set(['dep3', 'dep4', '0file2.ets']); - baseMode.testDealWithDependants(cycleFiles, 'file2.ets', jobs, dependants); - expect(jobs['0file2.ets'].dependants).toEqual(expect.arrayContaining(['dep1', 'dep2', 'dep3', 'dep4'])); - expect(jobs['0file2.ets'].dependants).not.toContain('0file2.ets'); - } -} - -function test_addJobToQueues() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBuildMode extends BuildMode { - public testAddJobToQueues(job: Job, queues: Queues): void { - return (this as any).addJobToQueues(job, queues); - } - } - - const baseMode = new TestBuildMode(mockConfig as any); - - const job1: Job = { - id: 'job1', - fileList: ['/test/path/file1.ets'], - dependencies: [], - dependants: [], - isDeclFile: true, - isAbcJob: false - }; - const queues1: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; - baseMode.testAddJobToQueues(job1, queues1); - expect(queues1.externalProgramQueue.length).toBe(1); - expect(queues1.externalProgramQueue[0].id).toBe('job1'); - expect(queues1.abcQueue.length).toBe(0); - - const job2: Job = { - id: 'job2', - fileList: ['/test/path/file2.ets'], - dependencies: [], - dependants: [], - isDeclFile: false, - isAbcJob: true - }; - const queues2: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; - baseMode.testAddJobToQueues(job2, queues2); - expect(queues2.externalProgramQueue.length).toBe(0); - expect(queues2.abcQueue.length).toBe(1); - expect(queues2.abcQueue[0].id).toBe('job2'); - - const job3: Job = { - id: 'job3', - fileList: ['/test/path/file3.ets'], - dependencies: [], - dependants: [], - isDeclFile: true, - isAbcJob: false - }; - const queues3: Queues = { - externalProgramQueue: [job3], - abcQueue: [] - }; - baseMode.testAddJobToQueues(job3, queues3); - expect(queues3.externalProgramQueue.length).toBe(1); - expect(queues3.abcQueue.length).toBe(0); - - const job4: Job = { - id: 'job4', - fileList: ['/test/path/file4.ets'], - dependencies: [], - dependants: [], - isDeclFile: false, - isAbcJob: true - }; - const queues4: Queues = { - externalProgramQueue: [], - abcQueue: [job4] - }; - baseMode.testAddJobToQueues(job4, queues4); - expect(queues4.externalProgramQueue.length).toBe(0); - expect(queues4.abcQueue.length).toBe(1); -} -function test_initCompileQueues() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBuildMode extends BuildMode { - public testInitCompileQueues(jobs: Record, queues: Queues): void { - return (this as any).initCompileQueues(jobs, queues); + class TestBuildMode extends BuildMode { + public testIsFileChanged(etsFilePath: string, abcFilePath: string): boolean { + return (this as any).isFileChanged(etsFilePath, abcFilePath); + } } - constructor(buildConfig: any) { - super(buildConfig); - (this as any).collectCompileJobs = jest.fn().mockImplementation((jobs: Record) => { - jobs['job1'] = { - id: 'job1', - dependencies: [], - dependants: ['job3'], - fileList: ['/test/path/file1.ets'], - isAbcJob: true, - isDeclFile: false - }; + const fs = require('fs'); + const existsSyncSpy = jest.spyOn(fs, 'existsSync'); + const statSyncSpy = jest.spyOn(fs, 'statSync'); - jobs['job2'] = { - id: 'job2', - dependencies: [], - dependants: [], - fileList: ['/test/path/file2.ets'], - isAbcJob: false, - isDeclFile: true - }; + (global as any).getFileHash = jest.fn(); - jobs['job3'] = { - id: 'job3', - dependencies: ['job1'], - dependants: [], - fileList: ['/test/path/file3.ets'], - isAbcJob: true, - isDeclFile: false - }; - }); + const baseMode = new TestBuildMode(mockConfig as any); + (baseMode as any).hashCache = {}; - (this as any).addJobToQueues = jest.fn().mockImplementation((job: Job, queues: Queues) => { - if (job.isAbcJob) { - queues.abcQueue.push(job); - } else { - queues.externalProgramQueue.push(job); - } - }); - } - } + existsSyncSpy.mockReturnValueOnce(false); + let result = baseMode.testIsFileChanged('/test/file1.ets', '/test/file1.abc'); + expect(result).toBe(true); + expect(existsSyncSpy).toHaveBeenCalledWith('/test/file1.abc'); + expect(statSyncSpy).not.toHaveBeenCalled(); - const baseMode = new TestBuildMode(mockConfig as any); + existsSyncSpy.mockReturnValueOnce(true); + statSyncSpy.mockReturnValueOnce({ mtimeMs: 200 }); + statSyncSpy.mockReturnValueOnce({ mtimeMs: 100 }); + result = baseMode.testIsFileChanged('/test/file2.ets', '/test/file2.abc'); + expect(result).toBe(true); + expect(statSyncSpy).toHaveBeenCalledWith('/test/file2.ets'); + expect(statSyncSpy).toHaveBeenCalledWith('/test/file2.abc'); - const jobs: Record = {}; - const queues: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; + jest.restoreAllMocks(); + delete (global as any).getFileHash; +} + +function test_collectDependentCompileFiles() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn() + }; + + const LogDataFactory = { + newInstance: jest.fn().mockReturnValue({ code: "123", message: "Test error" }) + }; + + const ErrorCode = { + BUILDSYSTEM_Dependency_Analyze_FAIL: '11410001', + BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL: '11410002' + }; + + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; - const collectCompileJobsSpy = jest.spyOn(baseMode as any, 'collectCompileJobs'); - const addJobToQueuesSpy = jest.spyOn(baseMode as any, 'addJobToQueues'); + class TestBuildMode extends BuildMode { + public testCollectDependentCompileFiles(): void { + return (this as any).collectDependentCompileFiles(); + } + } - baseMode.testInitCompileQueues(jobs, queues); + (global as any).LogDataFactory = LogDataFactory; + (global as any).ErrorCode = ErrorCode; + (global as any).getFileHash = jest.fn().mockReturnValue("hash123"); - expect(collectCompileJobsSpy).toHaveBeenCalledWith(jobs); + const baseMode = new TestBuildMode(mockConfig as any); + (baseMode as any).logger = mockLogger; + (baseMode as any).cacheDir = "/test/cache"; + (baseMode as any).hashCache = {}; + (baseMode as any).abcFiles = new Set(); + (baseMode as any).allFiles = new Map(); + (baseMode as any).compileFiles = new Map(); - expect(addJobToQueuesSpy).toHaveBeenCalledTimes(2); + { + (baseMode as any).dependencyFileMap = null; - expect(queues.abcQueue.length).toBe(1); - expect(queues.abcQueue[0].id).toBe('job1'); - expect(queues.externalProgramQueue.length).toBe(1); - expect(queues.externalProgramQueue[0].id).toBe('job2'); + baseMode.testCollectDependentCompileFiles(); - expect(queues.abcQueue.find(job => job.id === 'job3')).toBeUndefined(); + mockLogger.printError.mockClear(); + LogDataFactory.newInstance.mockClear(); + } + + { + (baseMode as any).dependencyFileMap = { + dependants: { + "/test/other/path/file.ets": [] + } + }; + (baseMode as any).entryFiles = new Set(["/test/other/path/file.ets"]); + (baseMode as any).moduleInfos = new Map([ + ["test", { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"] + }] + ]); + + baseMode.testCollectDependentCompileFiles(); + + mockLogger.printError.mockClear(); + LogDataFactory.newInstance.mockClear(); + } - jest.restoreAllMocks(); + delete (global as any).LogDataFactory; + delete (global as any).ErrorCode; + delete (global as any).getFileHash; + jest.restoreAllMocks(); } -function test_checkAllTasksDone() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - - class TestBuildMode extends BuildMode { - public testCheckAllTasksDone(queues: Queues, workerPool: WorkerInfo[]): boolean { - return (this as any).checkAllTasksDone(queues, workerPool); +function test_getSerializableConfig() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG, + arkts: { + someFunction: () => { } + }, + bigIntValue: BigInt(9007199254740991) + }; + + class TestBuildMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + + public testGetSerializableConfig(): Object { + return (this as any).getSerializableConfig(); + } } - } - - const baseMode = new TestBuildMode(mockConfig as any); - const queues2: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; - const workerPool2 = [ - { worker: {} as ThreadWorker, isIdle: true }, - { worker: {} as ThreadWorker, isIdle: false } - ]; - - expect(baseMode.testCheckAllTasksDone(queues2, workerPool2)).toBe(false); - const queues3: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; - const workerPool3 = [ - { worker: {} as ThreadWorker, isIdle: true }, - { worker: {} as ThreadWorker, isIdle: true } - ]; - expect(baseMode.testCheckAllTasksDone(queues3, workerPool3)).toBe(true); - expect(baseMode.testCheckAllTasksDone(queues3, workerPool3)).toBe(true); + + const baseMode = new TestBuildMode(mockConfig as any); + + const result = baseMode.testGetSerializableConfig(); + + expect(result).not.toHaveProperty('arkts'); + + expect(result).not.toHaveProperty('bigIntValue'); + + expect(result).toHaveProperty('packageName', 'test'); + expect(result).toHaveProperty('moduleRootPath', '/test/path'); + expect(result).toHaveProperty('sourceRoots'); } -function test_processAfterCompile() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG, - arkts: { - destroyConfig: jest.fn() - }, - arktsGlobal: { - es2panda: { - _DestroyGlobalContext: jest.fn(), - _MemFinalize: jest.fn() - } +function test_getJobDependencies() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; + + class TestBuildMode extends BuildMode { + public testGetJobDependencies(fileDeps: string[], cycleFiles: Map): Set { + return (this as any).getJobDependencies(fileDeps, cycleFiles); + } } - }; - class TestBuildMode extends BuildMode { - public testProcessAfterCompile(config: any, globalContext: any): void { - (this as any).processAfterCompile(config, globalContext); + const baseMode = new TestBuildMode(mockConfig as any); + { + const fileDeps = ['/test/path/file1.ets', '/test/path/file2.ets']; + const cycleFiles = new Map(); + const result = baseMode.testGetJobDependencies(fileDeps, cycleFiles); + expect(result.size).toBe(2); + expect(result.has('0/test/path/file1.ets')).toBe(true); + expect(result.has('0/test/path/file2.ets')).toBe(true); } - public mergeAbcFiles(): void { + { + const fileDeps = ['/test/path/file1.ets', '/test/path/cycle1.ets']; + const cycleFiles = new Map(); + cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1', 'cycle-group-2']); + const result = baseMode.testGetJobDependencies(fileDeps, cycleFiles); + expect(result.size).toBe(3); + expect(result.has('0/test/path/file1.ets')).toBe(true); + expect(result.has('cycle-group-1')).toBe(true); + expect(result.has('cycle-group-2')).toBe(true); + expect(result.has('0/test/path/cycle1.ets')).toBe(false); + } + + { + const fileDeps = ['/test/path/cycle1.ets', '/test/path/cycle2.ets']; + const cycleFiles = new Map(); + cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1']); + cycleFiles.set('/test/path/cycle2.ets', ['cycle-group-2']); + const result = baseMode.testGetJobDependencies(fileDeps, cycleFiles); + expect(result.size).toBe(2); + expect(result.has('cycle-group-1')).toBe(true); + expect(result.has('cycle-group-2')).toBe(true); + expect(result.has('0/test/path/cycle1.ets')).toBe(false); + expect(result.has('0/test/path/cycle2.ets')).toBe(false); } - } - - const baseMode = new TestBuildMode(mockConfig as any); - const mergeAbcFilesSpy = jest.spyOn(baseMode, 'mergeAbcFiles').mockImplementation(() => { }); - - baseMode.testProcessAfterCompile('mockConfig', 'mockGlobalContext'); - expect(mockConfig.arktsGlobal.es2panda._DestroyGlobalContext).toHaveBeenCalledWith('mockGlobalContext'); - expect(mockConfig.arkts.destroyConfig).toHaveBeenCalledWith('mockConfig'); - expect(mockConfig.arktsGlobal.es2panda._MemFinalize).toHaveBeenCalled(); - expect(mergeAbcFilesSpy).toHaveBeenCalledTimes(1); - expect((baseMode as any).hasCleanWorker).toBe(true); - - jest.clearAllMocks(); - baseMode.testProcessAfterCompile('mockConfig2', 'mockGlobalContext2'); - expect(mockConfig.arktsGlobal.es2panda._DestroyGlobalContext).not.toHaveBeenCalled(); - expect(mockConfig.arkts.destroyConfig).not.toHaveBeenCalled(); - expect(mockConfig.arktsGlobal.es2panda._MemFinalize).not.toHaveBeenCalled(); - expect(mergeAbcFilesSpy).not.toHaveBeenCalled(); - - jest.restoreAllMocks(); } -function test_runConcurrent() { - const mockConfig = { - packageName: "test", - compileFiles: ["/test/path/file1.ets"], - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - } as any; - - const Logger = require('../../../src/logger').Logger; - Logger.getInstance = jest.fn().mockReturnValue({ - printInfo: jest.fn(), printError: jest.fn(), hasErrors: jest.fn().mockReturnValue(false) - }); - - class TestBuildMode extends BuildMode { - public async testRunConcurrent(): Promise { return this.runConcurrent(); } - - public generateModuleInfos(): void { - (this as any).compileFiles = new Map([ - ['/test/path/file1.ets', { - filePath: '/test/path/file1.ets', packageName: 'test', - abcFilePath: '/test/path/output.abc', arktsConfigFile: '/test/arktsconfig.json' - }] - ]); - (this as any).allFiles = (this as any).compileFiles; +function test_getJobDependants() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; + class TestBuildMode extends BuildMode { + public testGetJobDependants(fileDeps: string[], cycleFiles: Map): Set { + return (this as any).getJobDependants(fileDeps, cycleFiles); + } } - public generateArkTSConfigForModules(): void { } - - constructor(buildConfig: any) { - super(buildConfig); - const self = this as any; - self.initCompileQueues = function (jobs: any, queues: any): void { - queues.externalProgramQueue.push({ - id: '0/test/path/file1.ets', fileList: ['/test/path/file1.ets'], - dependencies: [], dependants: [], isDeclFile: true, isAbcJob: false - }); - }; - self.invokeWorkers = async function (): Promise { return Promise.resolve(); }; + const baseMode = new TestBuildMode(mockConfig as any); + { + const fileDeps = ['/test/path/file1.ets', '/test/path/file2.ets']; + const cycleFiles = new Map(); + const result = baseMode.testGetJobDependants(fileDeps, cycleFiles); + expect(result.size).toBe(4); + expect(result.has('1/test/path/file1.ets')).toBe(true); + expect(result.has('0/test/path/file1.ets')).toBe(true); + expect(result.has('1/test/path/file2.ets')).toBe(true); + expect(result.has('0/test/path/file2.ets')).toBe(true); + } + + { + const fileDeps = ['/test/path/file1.d.ets', '/test/path/file2.ets']; + const cycleFiles = new Map(); + const result = baseMode.testGetJobDependants(fileDeps, cycleFiles); + expect(result.size).toBe(3); + expect(result.has('1/test/path/file1.d.ets')).toBe(false); + expect(result.has('0/test/path/file1.d.ets')).toBe(true); + expect(result.has('1/test/path/file2.ets')).toBe(true); + expect(result.has('0/test/path/file2.ets')).toBe(true); + } + + { + const fileDeps = ['/test/path/file1.ets', '/test/path/cycle1.ets']; + const cycleFiles = new Map(); + cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1', 'cycle-group-2']); + const result = baseMode.testGetJobDependants(fileDeps, cycleFiles); + expect(result.size).toBe(5); + expect(result.has('1/test/path/file1.ets')).toBe(true); + expect(result.has('0/test/path/file1.ets')).toBe(true); + expect(result.has('cycle-group-1')).toBe(true); + expect(result.has('cycle-group-2')).toBe(true); + expect(result.has('0/test/path/cycle1.ets')).toBe(false); } - } - - const baseMode = new TestBuildMode(mockConfig); - const genModuleSpy = jest.spyOn(baseMode, 'generateModuleInfos'); - const genConfigSpy = jest.spyOn(baseMode, 'generateArkTSConfigForModules'); - const initQueuesSpy = jest.spyOn(baseMode, 'initCompileQueues' as any); - const invokeWorkersSpy = jest.spyOn(baseMode, 'invokeWorkers' as any); - return baseMode.testRunConcurrent().then(() => { - expect(genModuleSpy).toHaveBeenCalledTimes(1); - expect(genConfigSpy).toHaveBeenCalledTimes(1); - expect(initQueuesSpy).toHaveBeenCalledTimes(1); - expect(invokeWorkersSpy).toHaveBeenCalledTimes(1); - jest.restoreAllMocks(); - }); } -function test_collectDependencyModules_language_branches() { - const { LANGUAGE_VERSION } = require('../../../src/pre_define'); - class TestBaseMode extends BaseMode { - public run(): Promise { return Promise.resolve(); } - public testCollectDependencyModules( - packageName: string, module: ModuleInfo, - dynamicDepModules: Map, - staticDepModules: Map - ): void { - (this as any).collectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); +// NOTE: move to Dependency Analyzer ut +/* +function test_collectCompileJobs() { + const mockConfig = getMockConfig() + + class TestBuildMode extends BuildMode { + public testCollectCompileJobs(jobs: Record): void { + return (this as any).collectCompileJobs(jobs); + } + + constructor(buildConfig: any) { + super(buildConfig); + this.dependencyFileMap = { + dependencies: { + '/test/path/file1.ets': ['/test/path/file2.ets'], + '/test/path/file3.ets': ['/test/path/file4.ets'], + '/test/path/file5.d.ets': [] + }, + dependants: { + '/test/path/file2.ets': ['/test/path/file1.ets'], + '/test/path/file4.ets': ['/test/path/file3.ets'] + } + }; + + this.entryFiles = new Set(['/test/path/file1.ets']); + this.compileFiles = new Map([ + ['/test/path/file1.ets', { filePath: '/test/path/file1.ets' }] + ]); + + this.moduleInfos = new Map(); + this.moduleInfos.set("test", { + packageName: "test", + moduleRootPath: "/test/path", + arktsConfigFile: "/test/path/config.json" + }; + + this.allFiles = new Map(); + + this.getJobDependencies = jest.fn().mockImplementation(() => new Set(['dep1', 'dep2'])); + this.getJobDependants = jest.fn().mockImplementation(() => new Set(['dep3', 'dep4'])); + this.getAbcJobId = jest.fn().mockImplementation((file) => '1' + file); + this.getExternalProgramJobId = jest.fn().mockImplementation((file) => '0' + file); + this.createExternalProgramJob = jest.fn(); + this.dealWithDependants = jest.fn(); + this.findStronglyConnectedComponents = jest.fn().mockImplementation(() => { + const cycleGroups = new Map(); + const cycle1 = new Set(['/test/path/cycle1.ets', '/test/path/cycle2.ets']); + cycleGroups.set('cycle-group-1', cycle1); + return cycleGroups; + }); + } } - } - - const baseMode = new TestBaseMode({ - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - } as any); - - { - const packageName = "mod_1_1"; - const module = { packageName: "mod_1_1", language: LANGUAGE_VERSION.ARKTS_1_1 } as ModuleInfo; - const dynamicDepModules = new Map(); - const staticDepModules = new Map(); - - baseMode.testCollectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); - - expect(dynamicDepModules.has(packageName)).toBe(true); - expect(staticDepModules.has(packageName)).toBe(false); - expect(dynamicDepModules.get(packageName)).toBe(module); - } - - { - const packageName = "mod_1_2"; - const module = { packageName: "mod_1_2", language: LANGUAGE_VERSION.ARKTS_1_2 } as ModuleInfo; - const dynamicDepModules = new Map(); - const staticDepModules = new Map(); - - baseMode.testCollectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); - - expect(staticDepModules.has(packageName)).toBe(true); - } - - { - const packageName = "mod_hybrid"; - const module = { packageName: "mod_hybrid", language: LANGUAGE_VERSION.ARKTS_HYBRID } as ModuleInfo; - const dynamicDepModules = new Map(); - const staticDepModules = new Map(); - - baseMode.testCollectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); - - expect(dynamicDepModules.has(packageName)).toBe(true); - expect(staticDepModules.has(packageName)).toBe(true); - expect(dynamicDepModules.get(packageName)).toBe(module); - expect(staticDepModules.get(packageName)).toBe(module); - } + + const baseMode = new TestBuildMode(mockConfig); + + (baseMode as any).dependencyFileMap.dependants['/test/path/file6.ets'] = ['/test/path/file7.ets']; + + const jobs: Record = {}; + + const findComponentsSpy = jest.spyOn(baseMode as any, 'findStronglyConnectedComponents'); + const getJobDependenciesSpy = jest.spyOn(baseMode as any, 'getJobDependencies'); + const getJobDependantsSpy = jest.spyOn(baseMode as any, 'getJobDependants'); + const getAbcJobIdSpy = jest.spyOn(baseMode as any, 'getAbcJobId'); + const getExternalProgramJobIdSpy = jest.spyOn(baseMode as any, 'getExternalProgramJobId'); + const createExternalProgramJobSpy = jest.spyOn(baseMode as any, 'createExternalProgramJob'); + const dealWithDependantsSpy = jest.spyOn(baseMode as any, 'dealWithDependants'); + + baseMode.testCollectCompileJobs(jobs); + + expect((baseMode as any).dependencyFileMap.dependencies['/test/path/file6.ets']).toEqual([]); + + expect(findComponentsSpy).toHaveBeenCalledWith((baseMode as any).dependencyFileMap); + + const cycleFiles = new Map(); + cycleFiles.set('/test/path/cycle1.ets', ['cycle-group-1']); + cycleFiles.set('/test/path/cycle2.ets', ['cycle-group-1']); + + expect(getJobDependenciesSpy).toHaveBeenCalled(); + + expect(getAbcJobIdSpy).toHaveBeenCalledWith('/test/path/file1.ets'); + expect(getAbcJobIdSpy).toHaveBeenCalledWith('/test/path/file3.ets'); + expect(getAbcJobIdSpy).not.toHaveBeenCalledWith('/test/path/file5.d.ets'); + + expect(jobs['1/test/path/file1.ets']).toBeDefined(); + expect(jobs['1/test/path/file3.ets']).toBeDefined(); + + expect(createExternalProgramJobSpy).toHaveBeenCalled(); + + expect((baseMode as any).allFiles.has('/test/path/file5.d.ets')).toBe(true); + + expect(getJobDependantsSpy).toHaveBeenCalled(); + expect(dealWithDependantsSpy).toHaveBeenCalled(); + + jest.restoreAllMocks(); } +*/ -function test_getDependentModules_missing_module() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn(), - printErrorAndExit: jest.fn() - }; - const ErrorCode = { - BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND: 'BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND' - }; - jest.mock('../../../src/error_code', () => ({ - ErrorCode - })); - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - buildMode: BUILD_MODE.DEBUG - }; - const Logger = require('../../../src/logger').Logger; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); +function test_dealWithDependants() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; + class TestBuildMode extends BuildMode { + public testDealWithDependants(cycleFiles: Map, key: string, jobs: Record, dependants: Set): void { + return (this as any).dealWithDependants(cycleFiles, key, jobs, dependants); + } } - public testGetDependentModules(moduleInfo: ModuleInfo): Map[] { - return (this as any).getDependentModules(moduleInfo); + const baseMode = new TestBuildMode(mockConfig as any); + { + const cycleFiles = new Map(); + cycleFiles.set('file1.ets', ['cycle-1', 'cycle-2']); + const jobs: Record = { + 'cycle-1': { + id: 'cycle-1', + fileList: ['file1.ets'], + jobDependencies: [], + jobDependants: ['dep1', 'dep2'], + isAbcJob: false + }, + 'cycle-2': { + id: 'cycle-2', + fileList: ['file1.ets', 'file2.ets'], + jobDependencies: [], + jobDependants: ['dep3'], + isAbcJob: false + } + }; + const dependants = new Set(['dep4', 'dep5', 'cycle-1']); + baseMode.testDealWithDependants(cycleFiles, 'file1.ets', jobs, dependants); + expect(jobs['cycle-1'].jobDependants).toEqual(expect.arrayContaining(['dep1', 'dep2', 'dep4', 'dep5'])); + expect(jobs['cycle-1'].jobDependants).not.toContain('cycle-1'); + expect(jobs['cycle-2'].jobDependants).toEqual(expect.arrayContaining(['dep3', 'dep4', 'dep5'])); + expect(jobs['cycle-2'].jobDependants).not.toContain('cycle-1'); + } + { + const cycleFiles = new Map(); + const jobs: Record = { + '0file2.ets': { + id: '0file2.ets', + fileList: ['file2.ets'], + jobDependencies: [], + jobDependants: ['dep1', 'dep2'], + isAbcJob: false + } + }; + const dependants = new Set(['dep3', 'dep4', '0file2.ets']); + baseMode.testDealWithDependants(cycleFiles, 'file2.ets', jobs, dependants); + expect(jobs['0file2.ets'].jobDependants).toEqual(expect.arrayContaining(['dep1', 'dep2', 'dep3', 'dep4'])); + expect(jobs['0file2.ets'].jobDependants).not.toContain('0file2.ets'); } - } - const baseMode = new TestBaseMode(mockConfig as any); - (baseMode as any).logger = mockLogger; - const testModuleInfo = { - isMainModule: false, - dependencies: ['nonExistingModule'], - packageName: 'testModule' - } as ModuleInfo; - baseMode.testGetDependentModules(testModuleInfo); - expect(mockLogger.printErrorAndExit).toHaveBeenCalledWith( - expect.objectContaining({ - cause: "", - code: "11410011", - description: 'Module nonExistingModule not found in moduleInfos' - }) - ); } -function test_declgen_method() { - jest.resetAllMocks(); - jest.restoreAllMocks(); - const fs = require('fs'); - jest.spyOn(fs, 'readFileSync').mockReturnValue('test source code'); - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - arkts: { - Config: { create: jest.fn().mockReturnValue({ peer: 'mockConfigPeer' }) }, - Context: { - createFromString: jest.fn().mockReturnValue({ peer: 'mockContextPeer', program: 'mockProgram' }), - createFromStringWithHistory: jest.fn().mockReturnValue({ peer: 'mockContextPeer', program: 'mockProgram' }) - }, - proceedToState: jest.fn(), EtsScript: { fromContext: jest.fn().mockReturnValue('mockAst') }, - Es2pandaContextState: { ES2PANDA_STATE_PARSED: 'parsed', ES2PANDA_STATE_CHECKED: 'checked' }, - generateTsDeclarationsFromContext: jest.fn(), destroyConfig: jest.fn() - }, - arktsGlobal: { es2panda: { _DestroyContext: jest.fn() } } - }; - const Logger = require('../../../src/logger').Logger; - const PluginDriver = require('../../../src/plugins/plugins_driver').PluginDriver; - const utils = require('../../../src/util/utils'); - const path = require('path'); - Logger.getInstance = jest.fn().mockReturnValue({ printInfo: jest.fn(), printError: jest.fn() }); - PluginDriver.getInstance = jest.fn().mockReturnValue({ - getPluginContext: jest.fn().mockReturnValue({ setArkTSProgram: jest.fn(), setArkTSAst: jest.fn() }), - runPluginHook: jest.fn() - }); - jest.spyOn(utils, 'ensurePathExists').mockImplementation(() => { }); - jest.spyOn(utils, 'changeDeclgenFileExtension').mockReturnValueOnce('/test/path/output.d.ets').mockReturnValueOnce('/test/path/output.ts'); - jest.spyOn(path, 'relative').mockReturnValue('file1.ets'); - jest.spyOn(path, 'join').mockReturnValue('/test/path/output'); - class TestBuildMode extends BuildMode { - constructor(buildConfig: any) { - super(buildConfig); - (this as any).outputDir = './dist'; (this as any).cacheDir = './dist/cache'; +function test_addJobToQueues() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; + + class TestBuildMode extends BuildMode { + public testAddJobToQueues(job: JobInfo): void { + return this.consumeJob(job); + } } - public testDeclgen(fileInfo: any): void { return this.declgen(fileInfo); } - } - const baseMode = new TestBuildMode(mockConfig as any); - (baseMode as any).logger = Logger.getInstance(); - (baseMode as any).moduleInfos = new Map([['test', { - packageName: 'test', moduleRootPath: '/test/path', - declgenV1OutPath: './dist/declgen', declgenBridgeCodePath: './dist/bridge' - }]]); - baseMode.testDeclgen({ filePath: '/test/path/file1.ets', packageName: 'test', arktsConfigFile: '/test/path/arktsconfig.json' }); - expect(fs.readFileSync).toHaveBeenCalledWith('/test/path/file1.ets', 'utf8'); - expect(mockConfig.arkts.Context.createFromStringWithHistory).toHaveBeenCalled(); - expect(mockConfig.arkts.proceedToState).toHaveBeenCalledWith('parsed', 'mockContextPeer', true); - expect(mockConfig.arkts.proceedToState).toHaveBeenCalledWith('checked', 'mockContextPeer', true); - expect(mockConfig.arkts.generateTsDeclarationsFromContext).toHaveBeenCalled(); - jest.restoreAllMocks(); + + const baseMode = new TestBuildMode(mockConfig as any); + + const job1: JobInfo = { + id: 'job1', + fileList: ['/test/path/file1.ets'], + jobDependencies: [], + jobDependants: [], + isAbcJob: false + }; + baseMode.testAddJobToQueues(job1); + expect(queues1.externalProgramQueue.length).toBe(1); + expect(queues1.externalProgramQueue[0].id).toBe('job1'); + expect(queues1.abcQueue.length).toBe(0); + + const job2: JobInfo = { + id: 'job2', + fileList: ['/test/path/file2.ets'], + jobDependencies: [], + jobDependants: [], + isAbcJob: true + }; + baseMode.testAddJobToQueues(job2); + expect(queues2.externalProgramQueue.length).toBe(0); + expect(queues2.abcQueue.length).toBe(1); + expect(queues2.abcQueue[0].id).toBe('job2'); + + const job3: JobInfo = { + id: 'job3', + fileList: ['/test/path/file3.ets'], + jobDependencies: [], + jobDependants: [], + isAbcJob: false + }; + baseMode.testAddJobToQueues(job3, queues3); + expect(queues3.externalProgramQueue.length).toBe(1); + expect(queues3.abcQueue.length).toBe(0); + + const job4: JobInfo = { + id: 'job4', + fileList: ['/test/path/file4.ets'], + jobDependencies: [], + jobDependants: [], + isAbcJob: true + }; + baseMode.testAddJobToQueues(job4, queues4); + expect(queues4.externalProgramQueue.length).toBe(0); + expect(queues4.abcQueue.length).toBe(1); } -function test_generateDeclaration() { - const mockConfig: BuildConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["ets2panda/driver/build_system/test/ut/mock/a.ets"], - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - plugins: {}, - dependentModuleList: [], - buildType: BUILD_TYPE.BUILD, - hasMainModule: false, - moduleType: OHOS_MODULE_TYPE.HAR, - byteCodeHar: false, - arkts: {} as any, - arktsGlobal: {} as any, - declgenV1OutPath: "./dist/declgen", - declgenV2OutPath: "./dist/declgen/v2", - buildSdkPath: "./sdk", - externalApiPaths: [], - enableDeclgenEts2Ts: false - } as any; - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance(mockConfig); - - class TestBuildMode extends BuildMode { - public async testGenerateDeclaration(): Promise { - return this.generateDeclaration(); +function test_initCompileQueues() { + const mockConfig = mock.getMockedBuildConfig() + class TestBuildMode extends BuildMode { + public testInitCompileQueues(jobs: Record, queues: Queues): void { + return (this as any).initCompileQueues(jobs, queues); + } + + constructor(buildConfig: any) { + super(buildConfig); + this.collectCompileJobs = jest.fn().mockImplementation((jobs: Record) => { + jobs['job1'] = { + id: 'job1', + jobDependencies: [], + jobDependants: ['job3'], + fileList: ['/test/path/file1.ets'], + isAbcJob: true, + }; + + jobs['job2'] = { + id: 'job2', + jobDependencies: [], + jobDependants: [], + fileList: ['/test/path/file2.ets'], + isAbcJob: false, + }; + + jobs['job3'] = { + id: 'job3', + jobDependencies: ['job1'], + jobDependants: [], + fileList: ['/test/path/file3.ets'], + isAbcJob: true, + }; + }); + + (this as any).addJobToQueues = jest.fn().mockImplementation((job: JobInfo, queues: Queues) => { + if (job.isAbcJob) { + queues.abcQueue.push(job); + } else { + queues.externalProgramQueue.push(job); + } + }); + } } - public generateModuleInfos(): void { + const baseMode = new TestBuildMode(mockConfig as any); + + const jobs: Record = {}; + const collectCompileJobsSpy = jest.spyOn(baseMode, 'collectCompileJobs'); + const addJobToQueuesSpy = jest.spyOn(baseMode, 'addJobToQueues'); + + baseMode.testInitCompileQueues(jobs, queues); + + expect(collectCompileJobsSpy).toHaveBeenCalledWith(jobs); + + expect(addJobToQueuesSpy).toHaveBeenCalledTimes(2); + + expect(queues.abcQueue.length).toBe(1); + expect(queues.abcQueue[0].id).toBe('job1'); + expect(queues.externalProgramQueue.length).toBe(1); + expect(queues.externalProgramQueue[0].id).toBe('job2'); + + expect(queues.abcQueue.find((job: JobInfo) => job.id === 'job3')).toBeUndefined(); + + jest.restoreAllMocks(); +} + + +// NOTE: To be defined later +/* +function test_checkAllTasksDone() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; + + class TestBuildMode extends BuildMode { + public testCheckAllTasksDone(queues: Queues, workerPool: WorkerInfo[]): boolean { + return (this as any).checkAllTasksDone(queues, workerPool); + } } - public declgen(fileInfo: any): void { + const baseMode = new TestBuildMode(mockConfig as any); + const queues2: Queues = { + externalProgramQueue: [], + abcQueue: [] + }; + const workerPool2 = [ + { worker: {} as ThreadWorker, isIdle: true }, + { worker: {} as ThreadWorker, isIdle: false } + ]; + + expect(baseMode.testCheckAllTasksDone(queues2, workerPool2)).toBe(false); + const queues3: Queues = { + externalProgramQueue: [], + abcQueue: [] + }; + const workerPool3 = [ + { worker: {} as ThreadWorker, isIdle: true }, + { worker: {} as ThreadWorker, isIdle: true } + ]; + expect(baseMode.testCheckAllTasksDone(queues3, workerPool3)).toBe(true); + expect(baseMode.testCheckAllTasksDone(queues3, workerPool3)).toBe(true); +} + +function test_processAfterCompile() { + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG, + arkts: { + destroyConfig: jest.fn() + }, + arktsGlobal: { + es2panda: { + _DestroyGlobalContext: jest.fn(), + _MemFinalize: jest.fn() + } + } + }; + + class TestBuildMode extends BuildMode { + public testProcessAfterCompile(config: any, globalContext: any): void { + (this as any).processAfterCompile(config, globalContext); + } + + public mergeAbcFiles(): void { + } } - } - const baseMode = new TestBuildMode(mockConfig); + const baseMode = new TestBuildMode(mockConfig as any); + const mergeAbcFilesSpy = jest.spyOn(baseMode, 'mergeAbcFiles').mockImplementation(() => { }); - (baseMode as any).logger = { printInfo: jest.fn(), printError: jest.fn() }; + baseMode.testProcessAfterCompile('mockConfig', 'mockGlobalContext'); + expect(mockConfig.arktsGlobal.es2panda._DestroyGlobalContext).toHaveBeenCalledWith('mockGlobalContext'); + expect(mockConfig.arkts.destroyConfig).toHaveBeenCalledWith('mockConfig'); + expect(mockConfig.arktsGlobal.es2panda._MemFinalize).toHaveBeenCalled(); + expect(mergeAbcFilesSpy).toHaveBeenCalledTimes(1); + expect((baseMode as any).hasCleanWorker).toBe(true); - const generateModuleInfosSpy = jest.spyOn(baseMode, 'generateModuleInfos').mockImplementation(() => { }); - const declgenSpy = jest.spyOn(baseMode, 'declgen').mockImplementation(() => { }); + jest.clearAllMocks(); + baseMode.testProcessAfterCompile('mockConfig2', 'mockGlobalContext2'); + expect(mockConfig.arktsGlobal.es2panda._DestroyGlobalContext).not.toHaveBeenCalled(); + expect(mockConfig.arkts.destroyConfig).not.toHaveBeenCalled(); + expect(mockConfig.arktsGlobal.es2panda._MemFinalize).not.toHaveBeenCalled(); + expect(mergeAbcFilesSpy).not.toHaveBeenCalled(); - return baseMode.testGenerateDeclaration().then(() => { - expect(generateModuleInfosSpy).toHaveBeenCalledTimes(1); - generateModuleInfosSpy.mockRestore(); - declgenSpy.mockRestore(); - }); + jest.restoreAllMocks(); } -function test_runMethod() { - const mockConfig: BuildConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["/test/path/file1.ets", "/test/path/file2.ets"], - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - plugins: {}, - dependentModuleList: [], - buildType: BUILD_TYPE.BUILD, - hasMainModule: false, - moduleType: OHOS_MODULE_TYPE.HAR, - byteCodeHar: false, - arkts: { - compiler: '/path/to/compiler', - args: [], - destroyConfig: jest.fn() - } as any, - arktsGlobal: { - config: {} - } as any, - declgenV1OutPath: "./dist/declgen", - declgenV2OutPath: "./dist/declgen/v2", - buildSdkPath: "./sdk", - externalApiPaths: [], - enableDeclgenEts2Ts: false, - es2pandaMode: ES2PANDA_MODE.RUN - } as any; - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue({ - printInfo: jest.fn(), - printError: jest.fn(), - hasErrors: jest.fn().mockReturnValue(false), - printErrorAndExit: jest.fn() - }); - - const PluginDriver = require('../../../src/plugins/plugins_driver').PluginDriver; - PluginDriver.getInstance = jest.fn().mockReturnValue({ - runPluginHook: jest.fn() - }); - - class TestBuildMode extends BuildMode { - public compile(fileInfo: any): void { - super.compile(fileInfo); +function test_runConcurrent() { + const mockConfig = { + packageName: "test", + compileFiles: ["/test/path/file1.ets"], + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + } as any; + + const Logger = require('../../../src/logger').Logger; + Logger.getInstance = jest.fn().mockReturnValue({ + printInfo: jest.fn(), printError: jest.fn(), hasErrors: jest.fn().mockReturnValue(false) + }); + + class TestBuildMode extends BuildMode { + public async testRunConcurrent(): Promise { return this.runConcurrent(); } + + public generateModuleInfos(): void { + (this as any).compileFiles = new Map([ + ['/test/path/file1.ets', { + filePath: '/test/path/file1.ets', packageName: 'test', + abcFilePath: '/test/path/output.abc', arktsConfigFile: '/test/arktsconfig.json' + }] + ]); + (this as any).allFiles = (this as any).compileFiles; + } + public generateArkTSConfigForModules(): void { } + + constructor(buildConfig: any) { + super(buildConfig); + const self = this as any; + self.initCompileQueues = function(jobs: any, queues: any): void { + queues.externalProgramQueue.push({ + id: '0/test/path/file1.ets', fileList: ['/test/path/file1.ets'], + dependencies: [], dependants: [], isDeclFile: true, isAbcJob: false + }); + }; + self.invokeWorkers = async function(): Promise { return Promise.resolve(); }; + } } - protected executeCommand(command: string, args: string[], options?: any): Promise { - return Promise.resolve({ stdout: "mock stdout", stderr: "" }); + const baseMode = new TestBuildMode(mockConfig); + const genModuleSpy = jest.spyOn(baseMode, 'generateModuleInfos'); + const genConfigSpy = jest.spyOn(baseMode, 'generateArkTSConfigForModules'); + const initQueuesSpy = jest.spyOn(baseMode, 'initCompileQueues' as any); + const invokeWorkersSpy = jest.spyOn(baseMode, 'invokeWorkers' as any); + return baseMode.testRunConcurrent().then(() => { + expect(genModuleSpy).toHaveBeenCalledTimes(1); + expect(genConfigSpy).toHaveBeenCalledTimes(1); + expect(initQueuesSpy).toHaveBeenCalledTimes(1); + expect(invokeWorkersSpy).toHaveBeenCalledTimes(1); + jest.restoreAllMocks(); + }); +} +*/ + +function test_collectDependencyModules_language_branches() { + class TestBaseMode extends BaseMode { + public run(): Promise { return Promise.resolve(); } + public testCollectDependencyModules( + packageName: string, module: ModuleInfo, + dynamicDepModules: Map, + staticDepModules: Map + ): void { + (this as any).collectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); + } } - protected getCompileCommand(fileInfo: any): { command: string, args: string[] } { - return { - command: 'node', - args: ['/path/to/compiler', fileInfo.filePath] - }; + const baseMode = new TestBaseMode({ + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + } as any); + + { + const packageName = "mod_1_1"; + const module = { packageName: "mod_1_1", language: LANGUAGE_VERSION.ARKTS_1_1 } as ModuleInfo; + const dynamicDepModules = new Map(); + const staticDepModules = new Map(); + + baseMode.testCollectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); + + expect(dynamicDepModules.has(packageName)).toBe(true); + expect(staticDepModules.has(packageName)).toBe(false); + expect(dynamicDepModules.get(packageName)).toBe(module); } - } - - const baseMode = new TestBuildMode(mockConfig); - - const mockFileInfo1 = { - filePath: '/test/path/file1.ets', - abcFilePath: '/test/path/file1.abc', - packageName: 'test', - arktsConfigFile: '/test/path/arktsconfig.json', - dependentFiles: [] - }; - const mockFileInfo2 = { - filePath: '/test/path/file2.ets', - abcFilePath: '/test/path/file2.abc', - packageName: 'test', - arktsConfigFile: '/test/path/arktsconfig.json', - dependentFiles: [] - }; - - const generateModuleInfosSpy = jest.spyOn(baseMode as any, 'generateModuleInfos') - .mockImplementation(() => { - (baseMode as any).compileFiles = new Map([ - ['/test/path/file1.ets', mockFileInfo1], - ['/test/path/file2.ets', mockFileInfo2] - ]); - }); - return baseMode.run().then(() => { - expect(generateModuleInfosSpy).toHaveBeenCalledTimes(1); - generateModuleInfosSpy.mockRestore(); - }); -} + { + const packageName = "mod_1_2"; + const module = { packageName: "mod_1_2", language: LANGUAGE_VERSION.ARKTS_1_2 } as ModuleInfo; + const dynamicDepModules = new Map(); + const staticDepModules = new Map(); -function test_assignTaskToIdleWorker_empty_queues() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockConfig = { - packageName: "test", - moduleType: "har", - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - }; - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); + baseMode.testCollectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); + + expect(staticDepModules.has(packageName)).toBe(true); } - public testAssignTaskToIdleWorker( - workerInfo: WorkerInfo, - queues: Queues, - processingJobs: Set, - serializableConfig: Object, - globalContextPtr: any - ): void { - (this as any).assignTaskToIdleWorker( - workerInfo, - queues, - processingJobs, - serializableConfig, - globalContextPtr - ); + { + const packageName = "mod_hybrid"; + const module = { packageName: "mod_hybrid", language: LANGUAGE_VERSION.ARKTS_HYBRID } as ModuleInfo; + const dynamicDepModules = new Map(); + const staticDepModules = new Map(); + + baseMode.testCollectDependencyModules(packageName, module, dynamicDepModules, staticDepModules); + + expect(dynamicDepModules.has(packageName)).toBe(true); + expect(staticDepModules.has(packageName)).toBe(true); + expect(dynamicDepModules.get(packageName)).toBe(module); + expect(staticDepModules.get(packageName)).toBe(module); } - } - const baseMode = new TestBaseMode(mockConfig as any); - const mockWorker = { - postMessage: jest.fn() - }; - - const workerInfo: WorkerInfo = { - worker: mockWorker as unknown as ThreadWorker, - isIdle: true - }; - - const queues: Queues = { - externalProgramQueue: [], - abcQueue: [] - }; - - const processingJobs = new Set(); - const serializableConfig = {}; - const globalContextPtr = {}; - - (baseMode as any).allFiles = new Map([ - ['test/file.ets', { - filePath: 'test/file.ets', - packageName: 'test', - arktsConfigFile: 'test/config.json', - abcFilePath: './dist/file.abc' - }] - ]); - - const postMessageSpy = jest.spyOn(mockWorker, 'postMessage'); - - baseMode.testAssignTaskToIdleWorker( - workerInfo, - queues, - processingJobs, - serializableConfig, - globalContextPtr - ); - - expect(postMessageSpy).not.toHaveBeenCalled(); - expect(processingJobs.size).toBe(0); - expect(workerInfo.isIdle).toBe(true); - jest.restoreAllMocks(); } -function test_assignTaskToIdleWorker_abcQueue_no_job() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockConfig = { - packageName: "test", - moduleType: "har", - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - dependentModuleList: [], - }; - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } - public testAssignTaskToIdleWorker( - workerInfo: WorkerInfo, - queues: Queues, - processingJobs: Set, - serializableConfig: Object, - globalContextPtr: any - ): void { - (this as any).assignTaskToIdleWorker( - workerInfo, - queues, - processingJobs, - serializableConfig, - globalContextPtr - ); +function test_getDependentModules_missing_module() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn(), + printErrorAndExit: jest.fn() + }; + const ErrorCode = { + BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND: 'BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND' + }; + jest.mock('../../../src/util/error', () => ({ + ErrorCode + })); + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + buildMode: BUILD_MODE.DEBUG + }; + const Logger = require('../../../src/logger').Logger; + Logger.getInstance = jest.fn().mockReturnValue(mockLogger); + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + public testGetDependentModules(moduleInfo: ModuleInfo): Map[] { + return (this as any).getDependentModules(moduleInfo); + } } - } - - const baseMode = new TestBaseMode(mockConfig as any); - - const mockWorker = { - postMessage: jest.fn() - }; - - const workerInfo: WorkerInfo = { - worker: mockWorker as unknown as ThreadWorker, - isIdle: true - }; - - const queues: Queues = { - externalProgramQueue: [], - abcQueue: [{ - id: 'abc:test/nonexistentfile.ets', - type: 'abc', - dependencies: [], - dependants: [], - result: null, - fileList: ['test/nonexistentfile.ets'], - isDeclFile: false, - isAbcJob: true - }] - }; - - const processingJobs = new Set(); - const serializableConfig = {}; - const globalContextPtr = {}; - - (baseMode as any).allFiles = new Map([ - ['test/otherfile.ets', { - filePath: 'test/otherfile.ets', - packageName: 'test', - arktsConfigFile: 'test/config.json', - abcFilePath: './dist/otherfile.abc' - }] - ]); - - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { }); - const postMessageSpy = jest.spyOn(mockWorker, 'postMessage'); - try { - baseMode.testAssignTaskToIdleWorker( - workerInfo, - queues, - processingJobs, - serializableConfig, - globalContextPtr + const baseMode = new TestBaseMode(mockConfig as any); + (baseMode as any).logger = mockLogger; + const testModuleInfo = { + isMainModule: false, + dependencies: ['nonExistingModule'], + packageName: 'testModule' + } as ModuleInfo; + baseMode.testGetDependentModules(testModuleInfo); + expect(mockLogger.printErrorAndExit).toHaveBeenCalledWith( + expect.objectContaining({ + cause: "", + code: "11410011", + description: 'Module nonExistingModule not found in moduleInfos' + }) ); - fail('Expected method to throw, but it did not'); - } catch (error) { - expect(error).toBeInstanceOf(ReferenceError); - expect(workerInfo.isIdle).toBe(false); - } finally { - consoleSpy.mockRestore(); +} + +// NOTE: to be defined later +/* +function test_declgen_method() { + jest.resetAllMocks(); + jest.restoreAllMocks(); + const fs = require('fs'); + jest.spyOn(fs, 'readFileSync').mockReturnValue('test source code'); + const mockConfig = { + packageName: "test", + moduleRootPath: "/test/path", + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + arkts: { + Config: { create: jest.fn().mockReturnValue({ peer: 'mockConfigPeer' }) }, + Context: { + createFromString: jest.fn().mockReturnValue({ peer: 'mockContextPeer', program: 'mockProgram' }), + createFromStringWithHistory: jest.fn().mockReturnValue({ peer: 'mockContextPeer', program: 'mockProgram' }) + }, + proceedToState: jest.fn(), EtsScript: { fromContext: jest.fn().mockReturnValue('mockAst') }, + Es2pandaContextState: { ES2PANDA_STATE_PARSED: 'parsed', ES2PANDA_STATE_CHECKED: 'checked' }, + generateTsDeclarationsFromContext: jest.fn(), destroyConfig: jest.fn() + }, + arktsGlobal: { es2panda: { _DestroyContext: jest.fn() } } + }; + const PluginDriver = require('../../../src/plugins/plugins_driver').PluginDriver; + PluginDriver.getInstance = jest.fn().mockReturnValue({ + getPluginContext: jest.fn().mockReturnValue({ setArkTSProgram: jest.fn(), setArkTSAst: jest.fn() }), + runPluginHook: jest.fn() + }); + jest.spyOn(utils, 'ensurePathExists').mockImplementation(() => { }); + jest.spyOn(utils, 'changeDeclgenFileExtension').mockReturnValueOnce('/test/path/output.d.ets').mockReturnValueOnce('/test/path/output.ts'); + jest.spyOn(path, 'relative').mockReturnValue('file1.ets'); + jest.spyOn(path, 'join').mockReturnValue('/test/path/output'); + class TestBuildMode extends BuildMode { + constructor(buildConfig: any) { + super(buildConfig); + (this as any).outputDir = './dist'; (this as any).cacheDir = './dist/cache'; + } + public testDeclgen(fileInfo: any): void { return this.declgen(fileInfo); } + } + const baseMode = new TestBuildMode(mockConfig as any); + (baseMode as any).logger = Logger.getInstance(); + (baseMode as any).moduleInfos = new Map([['test', { + packageName: 'test', moduleRootPath: '/test/path', + declgenV1OutPath: './dist/declgen', declgenBridgeCodePath: './dist/bridge' + }]]); + baseMode.testDeclgen({ filePath: '/test/path/file1.ets', packageName: 'test', arktsConfigFile: '/test/path/arktsconfig.json' }); + expect(fs.readFileSync).toHaveBeenCalledWith('/test/path/file1.ets', 'utf8'); + expect(mockConfig.arkts.Context.createFromStringWithHistory).toHaveBeenCalled(); + expect(mockConfig.arkts.proceedToState).toHaveBeenCalledWith('parsed', 'mockContextPeer', true); + expect(mockConfig.arkts.proceedToState).toHaveBeenCalledWith('checked', 'mockContextPeer', true); + expect(mockConfig.arkts.generateTsDeclarationsFromContext).toHaveBeenCalled(); jest.restoreAllMocks(); - } } +*/ -function test_findStronglyConnectedComponents_branches() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - buildMode: "Debug", - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { return Promise.resolve(); } - public testFindStronglyConnectedComponents(graph: DependencyFileConfig): Map> { - return (this as any).findStronglyConnectedComponents(graph); +// NOTE: to be defined later +/* +function test_generateDeclaration() { + const mockConfig: BuildConfig = { + buildMode: BUILD_MODE.DEBUG, + compileFiles: ["ets2panda/driver/build_system/test/ut/mock/a.ets"], + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + plugins: {}, + dependencyModuleList: [], + buildType: BUILD_TYPE.BUILD, + hasMainModule: false, + moduleType: OHOS_MODULE_TYPE.HAR, + byteCodeHar: false, + arkts: {} as any, + arktsGlobal: {} as any, + declgenV1OutPath: "./dist/declgen", + declgenV2OutPath: "./dist/declgen/v2", + buildSdkPath: "./sdk", + externalApiPaths: [], + enableDeclgenEts2Ts: false + } as any; + + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance(mockConfig); + + class TestBuildMode extends BuildMode { + public async testGenerateDeclaration(): Promise { + return this.generateDeclaration(); + } + + public generateModuleInfos(): void { + } + + public declgen(fileInfo: any): void { + } } - protected createHash(input: string): string { return 'cycle-group-' + input.length; } - } - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue({ printInfo: jest.fn(), printError: jest.fn() }); - const baseMode = new TestBaseMode(mockConfig as any); - const graph1 = { - dependencies: { 'A': ['B', 'C'], 'B': ['C'], 'C': ['A'] }, - dependants: { 'A': ['C'], 'B': ['A'], 'C': ['A', 'B'] } - }; - const result1 = baseMode.testFindStronglyConnectedComponents(graph1); - expect(result1.size).toBe(1); - expect(Array.from(result1.values())[0].size).toBe(3); - const graph2 = { - dependencies: { 'A': ['B', 'C'], 'B': ['D'], 'C': ['D'], 'D': ['E'], 'E': ['B'] }, - dependants: { 'A': [], 'B': ['A', 'E'], 'C': ['A'], 'D': ['B', 'C'], 'E': ['D'] } - }; - const result2 = baseMode.testFindStronglyConnectedComponents(graph2); - expect(result2.size).toBe(1); - expect(Array.from(result2.values())[0].size).toBe(3); - const graph3 = { - dependencies: { 'A': ['B'], 'B': ['C'], 'C': ['D'], 'D': [], 'E': ['F'], 'F': ['E'] }, - dependants: { 'A': [], 'B': ['A'], 'C': ['B'], 'D': ['C'], 'E': ['F'], 'F': ['E'] } - }; - const result3 = baseMode.testFindStronglyConnectedComponents(graph3); - expect(result3.size).toBe(1); - expect(Array.from(result3.values())[0].size).toBe(2); - const graph4 = { - dependencies: { 'A': ['B'], 'B': ['C'], 'C': ['D'], 'D': [] }, - dependants: { 'A': [], 'B': ['A'], 'C': ['B'], 'D': ['C'] } - }; - const result4 = baseMode.testFindStronglyConnectedComponents(graph4); - expect(result4.size).toBe(0); + + const baseMode = new TestBuildMode(mockConfig); + + (baseMode as any).logger = { printInfo: jest.fn(), printError: jest.fn() }; + + const generateModuleInfosSpy = jest.spyOn(baseMode, 'generateModuleInfos').mockImplementation(() => { }); + const declgenSpy = jest.spyOn(baseMode, 'declgen').mockImplementation(() => { }); + + return baseMode.testGenerateDeclaration().then(() => { + expect(generateModuleInfosSpy).toHaveBeenCalledTimes(1); + generateModuleInfosSpy.mockRestore(); + declgenSpy.mockRestore(); + }); } +*/ + +async function test_runMethod() { + const mockConfig: BuildConfig = mock.getMockedBuildConfig() + mockConfig.compileFiles.push("/test/dependency/path/index.ets") + mockConfig.declgenV1OutPath = "./dist/declgen" + mockConfig.declgenV2OutPath = "./dist/declgen/v2" + mockConfig.dependencyModuleList.push({ + packageName: "testDependency", + moduleName: "testDependency", + moduleType: OHOS_MODULE_TYPE.HAR, + modulePath: "/test/dependency/path", + sourceRoots: ["./"], + entryFile: "index.ets", + language: "1.2" + }) -function test_createExternalProgramJob_branches() { - const mockConfig = { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - buildMode: "Debug", - moduleType: "har", - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); + class TestBuildMode extends BuildMode { + public run(): Promise { + return super.run(); + } + + public collectModuleInfos() { + super.collectModuleInfos() + } } - public testCreateExternalProgramJob(id: string, fileList: string[], - jobs: Record, dependencies: Set, isInCycle?: boolean): void { - return (this as any).createExternalProgramJob(id, fileList, jobs, dependencies, isInCycle); + const testBuildMode = new TestBuildMode(mockConfig); + testBuildMode.koalaModule = { + arkts: { + compiler: '/path/to/compiler', + args: [], + destroyConfig: jest.fn() + } as any, + arktsGlobal: { + config: {} + } as any + } as any + + const mainModuleInfo: ModuleInfo = getMockMainModuleInfo() + + const dependencyModuleInfo: ModuleInfo = { + isMainModule: false, + packageName: "dependency", + moduleRootPath: "/test/dependency/path", + moduleType: OHOS_MODULE_TYPE.HAR, + sourceRoots: ["./"], + entryFile: "index.ets", + + arktsConfigFile: "/dist/cache/test/dependency/arktsconfig.json", + dependencies: [], + staticDependencyModules: new Map(), + dynamicDependencyModules: new Map(), + language: LANGUAGE_VERSION.ARKTS_1_1, } - } + mainModuleInfo.dependencies.push("dependency") + mainModuleInfo.staticDependencyModules.set("dependency", dependencyModuleInfo) - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue({ - printInfo: jest.fn(), - printError: jest.fn() - }); - const baseMode = new TestBaseMode(mockConfig as any); + const generateModuleInfosSpy = jest.spyOn(testBuildMode, 'collectModuleInfos') + .mockImplementation(() => { + testBuildMode.fileToModule = new Map([ + ['/test/path/file1.ets', mainModuleInfo], + ['/test/path/file2.ets', dependencyModuleInfo] + ]); + return; + }); - { - const id = "external-program:test/file.ets"; - const fileList = ["test/file.ets"]; - const jobs: Record = {}; - const dependencies = new Set([id, "external-program:other.ets"]); - const isInCycle = false; - - baseMode.testCreateExternalProgramJob(id, fileList, jobs, dependencies, isInCycle); - - expect(dependencies.has(id)).toBe(false); - expect(dependencies.size).toBe(1); - - expect(jobs[id]).toBeDefined(); - expect(jobs[id].id).toBe(id); - expect(jobs[id].fileList).toEqual(fileList); - expect(jobs[id].isDeclFile).toBe(true); - expect(jobs[id].isInCycle).toBe(false); - expect(jobs[id].dependencies).toEqual(["external-program:other.ets"]); - expect(jobs[id].dependants).toEqual([]); - } - - { - const id = "external-program:test/file2.ets"; - const fileList = ["test/file2.ets", "test/file2b.ets"]; - const jobs: Record = { - [id]: { - id, - fileList: ["test/file2.ets"], - isDeclFile: false, - isInCycle: false, - isAbcJob: false, - dependencies: ["external-program:dep1.ets"], - dependants: ["external-program:dep3.ets"] - } - }; - - const dependencies = new Set(["external-program:dep2.ets"]); - const isInCycle = true; - - baseMode.testCreateExternalProgramJob(id, fileList, jobs, dependencies, isInCycle); - - expect(jobs[id]).toBeDefined(); - expect(jobs[id].id).toBe(id); - expect(jobs[id].fileList).toEqual(["test/file2.ets"]); - expect(jobs[id].isDeclFile).toBe(false); - expect(jobs[id].isInCycle).toBe(false); - expect(jobs[id].dependencies).toContain("external-program:dep1.ets"); - expect(jobs[id].dependencies).toContain("external-program:dep2.ets"); - expect(jobs[id].dependencies.length).toBe(2); - expect(jobs[id].dependants).toEqual(["external-program:dep3.ets"]); - } + return testBuildMode.run().then(() => { + expect(generateModuleInfosSpy).toHaveBeenCalledTimes(1); + generateModuleInfosSpy.mockRestore(); + }); } -function test_collectCompileFiles_bytecode_har() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockConfig = { - packageName: "test", - moduleType: "har", - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - enableDeclgenEts2Ts: true, - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); + +function test_findStronglyConnectedComponents_branches() { + const mockConfig: BuildConfig = mock.getMockedBuildConfig() + + class TestBaseMode extends BaseMode { + public run(): Promise { return Promise.resolve(); } + public testFindStronglyConnectedComponents(fileMap: DependencyFileMap): Map> { + return (this as any).findStronglyConnectedComponents(fileMap); + } + protected createHash(input: string): string { return 'cycle-group-' + input.length; } + } + + const baseMode = new TestBaseMode(mockConfig as any); + const graph1 = { + dependencies: { 'A': ['B', 'C'], 'B': ['C'], 'C': ['A'] }, + dependants: { 'A': ['C'], 'B': ['A'], 'C': ['A', 'B'] } + }; + const result1 = baseMode.testFindStronglyConnectedComponents(graph1); + expect(result1.size).toBe(1); + expect(Array.from(result1.values())[0].size).toBe(3); + const graph2 = { + dependencies: { 'A': ['B', 'C'], 'B': ['D'], 'C': ['D'], 'D': ['E'], 'E': ['B'] }, + dependants: { 'A': [], 'B': ['A', 'E'], 'C': ['A'], 'D': ['B', 'C'], 'E': ['D'] } + }; + const result2 = baseMode.testFindStronglyConnectedComponents(graph2); + expect(result2.size).toBe(1); + expect(Array.from(result2.values())[0].size).toBe(3); + const graph3 = { + dependencies: { 'A': ['B'], 'B': ['C'], 'C': ['D'], 'D': [], 'E': ['F'], 'F': ['E'] }, + dependants: { 'A': [], 'B': ['A'], 'C': ['B'], 'D': ['C'], 'E': ['F'], 'F': ['E'] } + }; + const result3 = baseMode.testFindStronglyConnectedComponents(graph3); + expect(result3.size).toBe(1); + expect(Array.from(result3.values())[0].size).toBe(2); + const graph4 = { + dependencies: { 'A': ['B'], 'B': ['C'], 'C': ['D'], 'D': [] }, + dependants: { 'A': [], 'B': ['A'], 'C': ['B'], 'D': ['C'] } + }; + const result4 = baseMode.testFindStronglyConnectedComponents(graph4); + expect(result4.size).toBe(0); +} + +function test_createExternalProgramJob_branches() { + const mockConfig: BuildConfig = mock.getMockedBuildConfig() + + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + + public testCreateExternalProgramJob(id: string, fileList: string[], + jobs: Record, dependencies: Set, isInCycle?: boolean): void { + return (this as any).createExternalProgramJob(id, fileList, jobs, dependencies, isInCycle); + } } - public testCollectCompileFiles(): void { - this.collectCompileFiles(); + const baseMode = new TestBaseMode(mockConfig); + + { + const id = "external-program:test/file.ets"; + const fileList = ["test/file.ets"]; + const jobs: Record = {}; + const dependencies = new Set([id, "external-program:other.ets"]); + const isInCycle = false; + + baseMode.testCreateExternalProgramJob(id, fileList, jobs, dependencies, isInCycle); + + expect(dependencies.has(id)).toBe(false); + expect(dependencies.size).toBe(1); + + expect(jobs[id]).toBeDefined(); + expect(jobs[id].id).toBe(id); + expect(jobs[id].fileList).toEqual(fileList); + expect(jobs[id].jobDependencies).toEqual(["external-program:other.ets"]); + expect(jobs[id].jobDependants).toEqual([]); } - public testCollectAbcFileFromByteCodeHar(): void { - this.collectAbcFileFromByteCodeHar(); + { + const id = "external-program:test/file2.ets"; + const fileList = ["test/file2.ets", "test/file2b.ets"]; + const jobs: Record = { + [id]: { + id, + fileList: ["test/file2.ets"], + isAbcJob: false, + jobDependencies: ["external-program:dep1.ets"], + jobDependants: ["external-program:dep3.ets"] + } + }; + + const dependencies = new Set(["external-program:dep2.ets"]); + const isInCycle = true; + + baseMode.testCreateExternalProgramJob(id, fileList, jobs, dependencies, isInCycle); + + expect(jobs[id]).toBeDefined(); + expect(jobs[id].id).toBe(id); + expect(jobs[id].fileList).toEqual(["test/file2.ets"]); + expect(jobs[id].jobDependencies).toContain("external-program:dep1.ets"); + expect(jobs[id].jobDependencies).toContain("external-program:dep2.ets"); + expect(jobs[id].jobDependencies.length).toBe(2); + expect(jobs[id].jobDependants).toEqual(["external-program:dep3.ets"]); } - } - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - const baseMode = new TestBaseMode(mockConfig as any); - - (baseMode as any).cacheDir = "./dist/cache"; - (baseMode as any).abcFiles = new Set(); - (baseMode as any).hashCache = {}; - (baseMode as any).compileFiles = new Map(); - - (baseMode as any).entryFiles = new Set([ - './test/ut/mock/a.ets', - ]); - - (baseMode as any).moduleInfos = new Map(); - (baseMode as any).moduleInfos.set("test", { - packageName: "test", - moduleType: "har", - byteCodeHar: true, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - arktsConfigFile: "./dist/cache/test/config.json", - compileFileInfos: [] - }); - - (global as any).getFileHash = jest.fn().mockReturnValue("hash123"); - const utils = require('../../../src/util/utils'); - - jest.spyOn(baseMode, 'testCollectAbcFileFromByteCodeHar').mockImplementation(() => { }); - - baseMode.testCollectCompileFiles(); } -function test_collectCompileFiles_file_not_in_module() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockConfig = { - packageName: "test", - moduleType: "har", - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - enableDeclgenEts2Ts: true, - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); - } +// NOTE: to be defined later +/* +function test_collectCompileFiles_bytecode_har() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn() + }; + + const mockConfig: BuildConfig = getMockConfig() - public testCollectCompileFiles(): void { - this.collectCompileFiles(); + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + + public testProcessEntryFiles(): void { + super.processEntryFiles(); + } + + // NOTE: to be defined later + // public testCollectAbcFileFromByteCodeHar(): void { + // this.collectAbcFileFromByteCodeHar(); + // } } - } - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - const baseMode = new TestBaseMode(mockConfig as any); - - (baseMode as any).cacheDir = "./dist/cache"; - (baseMode as any).abcFiles = new Set(); - (baseMode as any).hashCache = {}; - (baseMode as any).compileFiles = new Map(); - - (baseMode as any).entryFiles = new Set([ - '/other/path/test.ets' - ]); - - (baseMode as any).moduleInfos = new Map(); - (baseMode as any).moduleInfos.set("test", { - packageName: "test", - moduleType: "har", - byteCodeHar: false, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - arktsConfigFile: "./dist/cache/test/config.json", - compileFileInfos: [] - }); - - baseMode.testCollectCompileFiles(); - - expect(mockLogger.printError).toHaveBeenCalledWith( - expect.objectContaining({ - code: ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, - description: 'File does not belong to any module in moduleInfos.' - }) - ); - expect((baseMode as any).compileFiles.size).toBe(0); + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance = jest.fn().mockReturnValue(mockLogger); + const baseMode = new TestBaseMode(mockConfig); + + + baseMode.moduleInfos = new Map(); + baseMode.moduleInfos.set("test", { + packageName: "test", + moduleType: "har", + byteCodeHar: true, + moduleRootPath: "/test/path", + sourceRoots: ["./"], + arktsConfigFile: "./dist/cache/test/config.json", + compileFileInfos: [] + }); + + global.getFileHash = jest.fn().mockReturnValue("hash123"); + + jest.spyOn(baseMode, 'testCollectAbcFileFromByteCodeHar').mockImplementation(() => { }); + + baseMode.testCollectCompileFiles(); } +*/ -function test_collectCompileFiles_decl_ets_skip() { - const mockLogger = { - printInfo: jest.fn(), - printError: jest.fn() - }; - - const mockConfig = { - packageName: "test", - moduleType: "har", - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - enableDeclgenEts2Ts: true, - dependentModuleList: [], - }; - - class TestBaseMode extends BaseMode { - public run(): Promise { - return Promise.resolve(); +function test_processEntryFiles_file_not_in_module() { + const mockConfig: BuildConfig = mock.getMockedBuildConfig() + + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + + public testProcessEntryFiles(): void { + super.processEntryFiles(); + } } - public testCollectCompileFiles(): void { - this.collectCompileFiles(); + const baseMode = new TestBaseMode(mockConfig); + + baseMode.entryFiles = new Set([ + '/other/path/test.ets' + ]); + + baseMode.moduleInfos = new Map(); + baseMode.moduleInfos.set("test", getMockMainModuleInfo()) + + baseMode.testProcessEntryFiles(); + + expect(Logger.getInstance().printError).toHaveBeenCalledWith( + expect.objectContaining({ + code: ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, + description: 'File does not belong to any module in moduleInfos.' + }) + ); + + expect(baseMode.fileToModule.size).toBe(0); +} + +// NOTE: to be defined later +/* +function test_processEntryFiles_decl_ets_skip() { + const mockConfig: BuildConfig = getMockConfig() + + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + + public testProcessEntryFiles(): void { + this.processEntryFiles(); + } } - } - - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance = jest.fn().mockReturnValue(mockLogger); - const baseMode = new TestBaseMode(mockConfig as any); - - (baseMode as any).cacheDir = "./dist/cache"; - (baseMode as any).abcFiles = new Set(); - (baseMode as any).hashCache = {}; - (baseMode as any).compileFiles = new Map(); - - (baseMode as any).entryFiles = new Set([ - 'index.ets', - '/test/ut/mock/web.d.ets' - ]); - - (baseMode as any).moduleInfos = new Map(); - (baseMode as any).moduleInfos.set("test", { - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - arktsConfigFile: "./dist/cache/test/config.json", - compileFileInfos: [] - }); - - (global as any).getFileHash = jest.fn().mockReturnValue("hash123"); - const utils = require('../../../src/util/utils'); - - baseMode.testCollectCompileFiles(); + + const baseMode = new TestBaseMode(mockConfig); + + baseMode.cacheDir = "./dist/cache"; + baseMode.abcFiles = new Set(); + baseMode.filesHashCache = {}; + + baseMode.entryFiles = new Set([ + 'index.ets', + '/test/ut/mock/web.d.ets' + ]); + + baseMode.moduleInfos = new Map(); + baseMode.moduleInfos.set("test", { + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + arktsConfigFile: "./dist/cache/test/config.json", + compileFileInfos: [] + }); + + global.getFileHash = jest.fn().mockReturnValue("hash123"); + + baseMode.testProcessEntryFiles(); } +*/ function test_collectModuleInfos() { - const mockLogger = { - printError: jest.fn(), - printInfo: jest.fn() - }; - const mockConfig: BuildConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["test.ets"], - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - plugins: {}, - buildType: BUILD_TYPE.BUILD, - hasMainModule: true, - moduleType: OHOS_MODULE_TYPE.HAR, - arkts: {} as any, - arktsGlobal: {} as any, - enableDeclgenEts2Ts: false, - byteCodeHar: false, - declgenV1OutPath: "./dist/declgen", - declgenV2OutPath: "./dist/declgen/v2", - buildSdkPath: "./sdk", - externalApiPaths: [], - - dependentModuleList: [ - { - "packageName": "harA", - "moduleName": "harA", - "moduleType": "har", - "modulePath": "test/ut/mock/demo_1.2_dep_hsp1.2/harA", - "sourceRoots": ["./"], - "entryFile": "index.ets", - "language": "11.2", - "dependencies": ["hspA"], - "byteCodeHar": false - }, - { - "packageName": "hspA", - "moduleName": "hspA", - "moduleType": "shared", - "modulePath": "hspA", - "sourceRoots": ["./"], - "entryFile": "index.ets", - "language": "11.2", - "byteCodeHar": false - } - ] - } as any; - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance(mockConfig); - let baseModule: BuildMode = new BuildMode(mockConfig); - (baseModule as any).collectModuleInfos(); - - expect(mockLogger.printError).not.toHaveBeenCalledWith( - expect.objectContaining({ - code: ErrorCode.BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL, - description: 'Main module info from hvigor is not correct.' - }) - ); + const mockConfig: BuildConfig = { + buildMode: BUILD_MODE.DEBUG, + compileFiles: ["test.ets"], + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + plugins: {}, + buildType: BUILD_TYPE.BUILD, + hasMainModule: true, + moduleType: OHOS_MODULE_TYPE.HAR, + arkts: {} as any, + arktsGlobal: {} as any, + enableDeclgenEts2Ts: false, + byteCodeHar: false, + declgenV1OutPath: "./dist/declgen", + declgenV2OutPath: "./dist/declgen/v2", + buildSdkPath: "./sdk", + externalApiPaths: [], + + dependencyModuleList: [ + { + "packageName": "harA", + "moduleName": "harA", + "moduleType": "har", + "modulePath": "test/ut/mock/demo_1.2_dep_hsp1.2/harA", + "sourceRoots": ["./"], + "entryFile": "index.ets", + "language": "11.2", + "dependencies": ["hspA"], + "byteCodeHar": false + }, + { + "packageName": "hspA", + "moduleName": "hspA", + "moduleType": "shared", + "modulePath": "hspA", + "sourceRoots": ["./"], + "entryFile": "index.ets", + "language": "11.2", + "byteCodeHar": false + } + ] + } as any; + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance(mockConfig); + let baseModule: BuildMode = new BuildMode(mockConfig); + (baseModule as any).collectModuleInfos(); + + expect(Logger.getInstance().printError).not.toHaveBeenCalledWith( + expect.objectContaining({ + code: ErrorCode.BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL, + description: 'Main module info from hvigor is not correct.' + }) + ); } function test_collectDependentCompileFiles002() { - const mockLogger = { - printError: jest.fn(), - printInfo: jest.fn(), - hasErrors: jest.fn().mockReturnValue(false) - }; - - const moduleRootPath = "test/ut/mock/"; - const testFile = `${moduleRootPath}a.ets`; - - const mockConfig: BuildConfig = { - compileFiles: [testFile], - packageName: "entry", - moduleType: OHOS_MODULE_TYPE.HAR, - buildType: BUILD_TYPE.BUILD, - buildMode: BUILD_MODE.DEBUG, - moduleRootPath: moduleRootPath, - sourceRoots: ["./"], - loaderOutPath: "test/ut/mock/dist", - cachePath: "test/ut/mock/dist/cache", - dependentModuleList: [], - plugins: {}, - hasMainModule: false, - arkts: {} as any, - arktsGlobal: {} as any, - enableDeclgenEts2Ts: false, - byteCodeHar: false, - declgenV1OutPath: "./dist/declgen", - declgenV2OutPath: "./dist/declgen/v2", - buildSdkPath: "./sdk", - externalApiPaths: [] - } as any; - - const BuildMode = require('../../../src/build/build_mode').BuildMode; - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance(mockConfig); - let baseModule = new BuildMode(mockConfig); - - (baseModule as any).logger = mockLogger; - (baseModule as any).moduleInfos = new Map(); - (baseModule as any).moduleInfos.set("entry", { - packageName: "entry", - moduleRootPath: moduleRootPath, - sourceRoots: ["./"], - compileFileInfos: [] - }); - - (baseModule as any).entryFiles = new Set([testFile]); - (baseModule as any).dependencyFileMap = { - dependants: { - [testFile]: ["dependency1.ets", "dependency2.ets"] - } - }; - (baseModule as any).cacheDir = "test/ut/mock/dist/cache"; - (baseModule as any).hashCache = {}; - (baseModule as any).abcFiles = new Set(); - (baseModule as any).compileFiles = new Map(); + const mockLogger = { + printError: jest.fn(), + printInfo: jest.fn(), + hasErrors: jest.fn().mockReturnValue(false) + }; - (baseModule as any).isBuildConfigModified = true; + const moduleRootPath = "test/ut/mock/"; + const testFile = `${moduleRootPath}a.ets`; - (baseModule as any).isFileChanged = jest.fn().mockReturnValue(false); + const mockConfig: BuildConfig = { + compileFiles: [testFile], + packageName: "entry", + moduleType: OHOS_MODULE_TYPE.HAR, + buildType: BUILD_TYPE.BUILD, + buildMode: BUILD_MODE.DEBUG, + moduleRootPath: moduleRootPath, + sourceRoots: ["./"], + loaderOutPath: "test/ut/mock/dist", + cachePath: "test/ut/mock/dist/cache", + dependencyModuleList: [], + plugins: {}, + hasMainModule: false, + arkts: {} as any, + arktsGlobal: {} as any, + enableDeclgenEts2Ts: false, + byteCodeHar: false, + declgenV1OutPath: "./dist/declgen", + declgenV2OutPath: "./dist/declgen/v2", + buildSdkPath: "./sdk", + externalApiPaths: [] + } as any; + + const BuildMode = require('../../../src/build/build_mode').BuildMode; + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance(mockConfig); + let baseModule = new BuildMode(mockConfig); + + (baseModule as any).logger = mockLogger; + (baseModule as any).moduleInfos = new Map(); + (baseModule as any).moduleInfos.set("entry", { + packageName: "entry", + moduleRootPath: moduleRootPath, + sourceRoots: ["./"], + compileFileInfos: [] + }); - (baseModule as any).collectDependentCompileFiles(); + (baseModule as any).entryFiles = new Set([testFile]); + (baseModule as any).dependencyFileMap = { + dependants: { + [testFile]: ["dependency1.ets", "dependency2.ets"] + } + }; + (baseModule as any).cacheDir = "test/ut/mock/dist/cache"; + (baseModule as any).hashCache = {}; + (baseModule as any).abcFiles = new Set(); + (baseModule as any).compileFiles = new Map(); - expect(mockLogger.printError).not.toHaveBeenCalledWith( - expect.objectContaining({ - code: ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, - message: 'File does not belong to any module in moduleInfos.' - }) - ); + (baseModule as any).isBuildConfigModified = true; + + (baseModule as any).isFileChanged = jest.fn().mockReturnValue(false); - expect((baseModule as any).abcFiles.size).toBe(1); - const compileFilesArray = Array.from((baseModule as any).compileFiles.keys()); - expect(compileFilesArray.length).toBe(1); - expect(compileFilesArray[0]).toBe(testFile); + (baseModule as any).collectDependentCompileFiles(); + + expect(mockLogger.printError).not.toHaveBeenCalledWith( + expect.objectContaining({ + code: ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, + message: 'File does not belong to any module in moduleInfos.' + }) + ); + + expect((baseModule as any).abcFiles.size).toBe(1); + const compileFilesArray = Array.from((baseModule as any).compileFiles.keys()); + expect(compileFilesArray.length).toBe(1); + expect(compileFilesArray[0]).toBe(testFile); } +// NOTE: to be defined later +/* function test_shouldSkipFile() { - const mockLogger = { printError: jest.fn() }; - const mockConfig: BuildConfig = { - buildMode: BUILD_MODE.DEBUG, - compileFiles: ["test.ets"], - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - loaderOutPath: "./dist", - cachePath: "./dist/cache", - plugins: {}, - dependentModuleList: [], - buildType: BUILD_TYPE.BUILD, - hasMainModule: false, - moduleType: OHOS_MODULE_TYPE.HAR, - byteCodeHar: false, - arkts: {} as any, - arktsGlobal: {} as any, - declgenV1OutPath: "./dist/declgen", - declgenV2OutPath: "./dist/declgen/v2", - buildSdkPath: "./sdk", - externalApiPaths: [], - enableDeclgenEts2Ts: false - } as any; - const Logger = require('../../../src/logger').Logger; - Logger.instance = null; - Logger.getInstance(mockConfig); - let baseModule: BaseMode = new BuildMode(mockConfig); - (baseModule as any).logger = mockLogger; - (baseModule as any).hashCache = { - "/test/path/file.ets": "hash123" - }; - - const file = "/test/path/file.ets"; - const moduleInfo: ModuleInfo = { - isMainModule: false, - packageName: "test", - moduleRootPath: "/test/path", - sourceRoots: ["./"], - arktsConfigFile: "/cache/test/arktsconfig.json", - compileFileInfos: [], - declgenV1OutPath: "/dist/declgen", - declgenBridgeCodePath: "/dist/bridge", - dynamicDepModuleInfos: new Map(), - staticDepModuleInfos: new Map(), - dependenciesSet: new Set(), - dependentSet: new Set(), - moduleType: OHOS_MODULE_TYPE.HAR, - entryFile: "index.ets", - declgenV2OutPath: "/dist/declgen/v2", - byteCodeHar: false - }; - const filePathFromModuleRoot = "file.ets"; - const abcFilePath = "/cache/test/file.abc"; - - (baseModule as any).enableDeclgenEts2Ts = true; - let result3 = (baseModule as any).shouldSkipFile(file, moduleInfo, filePathFromModuleRoot, abcFilePath); - (baseModule as any).enableDeclgenEts2Ts = false; - let result4 = (baseModule as any).shouldSkipFile(file, moduleInfo, filePathFromModuleRoot, abcFilePath); - expect(result3).toBe(false); - expect(result4).toBe(false); + const mockConfig: BuildConfig = getMockBuildConfig() + let baseModule: BuildMode = new BuildMode(mockConfig); + baseModule.filesHashCache = { + "/test/path/file.ets": "hash123" + }; + + const file = "/test/path/file.ets"; + const moduleInfo: ModuleInfo = { + isMainModule: false, + packageName: "test", + moduleRootPath: "/test/path", + sourceRoots: ["./"], + arktsConfigFile: "/cache/test/arktsconfig.json", + declgenV1OutPath: "/dist/declgen", + declgenBridgeCodePath: "/dist/bridge", + dynamicDependencyModules: new Map(), + staticDependencyModules: new Map(), + dependencies: [], + moduleType: OHOS_MODULE_TYPE.HAR, + entryFile: "index.ets", + declgenV2OutPath: "/dist/declgen/v2", + byteCodeHar: false + }; + const filePathFromModuleRoot = "file.ets"; + const abcFilePath = "/cache/test/file.abc"; + + baseModule.enableDeclgenEts2Ts = true; + let result3 = baseModule.shouldSkipFile(file, moduleInfo, filePathFromModuleRoot, abcFilePath); + baseModule.enableDeclgenEts2Ts = false; + let result4 = baseModule.shouldSkipFile(file, moduleInfo, filePathFromModuleRoot, abcFilePath); + expect(result3).toBe(false); + expect(result4).toBe(false); } +function test_assignTaskToIdleWorker_empty_queues() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn() + }; + + const mockConfig = { + packageName: "test", + moduleType: "har", + buildMode: BUILD_MODE.DEBUG, + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + }; + + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance = jest.fn().mockReturnValue(mockLogger); + + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + + public testAssignTaskToIdleWorker( + workerInfo: WorkerInfo, + queues: Queues, + processingJobs: Set, + serializableConfig: Object, + globalContextPtr: any + ): void { + (this as any).assignTaskToIdleWorker( + workerInfo, + queues, + processingJobs, + serializableConfig, + globalContextPtr + ); + } + } + const baseMode = new TestBaseMode(mockConfig as any); + const mockWorker = { + postMessage: jest.fn() + }; + + const workerInfo: WorkerInfo = { + worker: mockWorker as unknown as ThreadWorker, + isIdle: true + }; + + const queues: Queues = { + externalProgramQueue: [], + abcQueue: [] + }; + + const processingJobs = new Set(); + const serializableConfig = {}; + const globalContextPtr = {}; + + (baseMode as any).allFiles = new Map([ + ['test/file.ets', { + filePath: 'test/file.ets', + packageName: 'test', + arktsConfigFile: 'test/config.json', + abcFilePath: './dist/file.abc' + }] + ]); + + const postMessageSpy = jest.spyOn(mockWorker, 'postMessage'); + + baseMode.testAssignTaskToIdleWorker( + workerInfo, + queues, + processingJobs, + serializableConfig, + globalContextPtr + ); + + expect(postMessageSpy).not.toHaveBeenCalled(); + expect(processingJobs.size).toBe(0); + expect(workerInfo.isIdle).toBe(true); + jest.restoreAllMocks(); +} + +function test_assignTaskToIdleWorker_abcQueue_no_job() { + const mockLogger = { + printInfo: jest.fn(), + printError: jest.fn() + }; + + const mockConfig = { + packageName: "test", + moduleType: "har", + buildMode: BUILD_MODE.DEBUG, + moduleRootPath: "/test/path", + sourceRoots: ["./"], + loaderOutPath: "./dist", + cachePath: "./dist/cache", + dependentModuleList: [], + }; + + const Logger = require('../../../src/logger').Logger; + Logger.instance = null; + Logger.getInstance = jest.fn().mockReturnValue(mockLogger); + + class TestBaseMode extends BaseMode { + public run(): Promise { + return Promise.resolve(); + } + public testAssignTaskToIdleWorker( + workerInfo: WorkerInfo, + queues: Queues, + processingJobs: Set, + serializableConfig: Object, + globalContextPtr: any + ): void { + (this as any).assignTaskToIdleWorker( + workerInfo, + queues, + processingJobs, + serializableConfig, + globalContextPtr + ); + } + } + + const baseMode = new TestBaseMode(mockConfig as any); + + const mockWorker = { + postMessage: jest.fn() + }; + + const workerInfo: WorkerInfo = { + worker: mockWorker as unknown as ThreadWorker, + isIdle: true + }; + + const queues: Queues = { + externalProgramQueue: [], + abcQueue: [{ + id: 'abc:test/nonexistentfile.ets', + type: 'abc', + dependencies: [], + dependants: [], + result: null, + fileList: ['test/nonexistentfile.ets'], + isDeclFile: false, + isAbcJob: true + }] + }; + + const processingJobs = new Set(); + const serializableConfig = {}; + const globalContextPtr = {}; + + (baseMode as any).allFiles = new Map([ + ['test/otherfile.ets', { + filePath: 'test/otherfile.ets', + packageName: 'test', + arktsConfigFile: 'test/config.json', + abcFilePath: './dist/otherfile.abc' + }] + ]); + + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { }); + const postMessageSpy = jest.spyOn(mockWorker, 'postMessage'); + try { + baseMode.testAssignTaskToIdleWorker( + workerInfo, + queues, + processingJobs, + serializableConfig, + globalContextPtr + ); + fail('Expected method to throw, but it did not'); + } catch (error) { + expect(error).toBeInstanceOf(ReferenceError); + expect(workerInfo.isIdle).toBe(false); + } finally { + consoleSpy.mockRestore(); + jest.restoreAllMocks(); + } +} +*/ diff --git a/ets2panda/driver/build_system/test/ut/build_framework_modeTest/build_framework_mode.test.ts b/ets2panda/driver/build_system/test/ut/build_framework_modeTest/build_framework_mode.test.ts index 2a902b0e6c56d9eccd3e27b7065e373487f47ee8..c2e1adc0769e80148f5992ed266dad137d791bb1 100755 --- a/ets2panda/driver/build_system/test/ut/build_framework_modeTest/build_framework_mode.test.ts +++ b/ets2panda/driver/build_system/test/ut/build_framework_modeTest/build_framework_mode.test.ts @@ -43,7 +43,7 @@ describe('test build_framework_mode.ts file api', () => { moduleRootPath: '/test/path', buildMode: BUILD_MODE.DEBUG, plugins: {} as PluginsConfig, - dependentModuleList: [], + dependencyModuleList: [], hasMainModule: false, byteCodeHar: false, arkts: {} as any, diff --git a/ets2panda/driver/build_system/test/ut/compile_thread_workerTest/compile_thread_worker.test.ts b/ets2panda/driver/build_system/test/ut/compile_thread_workerTest/compile_thread_worker.test.ts index 87d39b2da4288a32896ecfc5be613ca32bf007fb..e7ffd489484b9867fec50482de6cff164f1b3af0 100755 --- a/ets2panda/driver/build_system/test/ut/compile_thread_workerTest/compile_thread_worker.test.ts +++ b/ets2panda/driver/build_system/test/ut/compile_thread_workerTest/compile_thread_worker.test.ts @@ -15,8 +15,6 @@ // This file has to mock a lot because compile_thread_worker.ts only runs a `process.on`. -import { EventEmitter } from 'events'; - jest.mock('fs'); jest.mock('path'); jest.mock('../../../src/util/utils', () => ({ @@ -91,7 +89,7 @@ beforeEach(() => { // create a test to avoid throw error describe('mockSDK', () => { it('should load correctly', () => { - + }); }); @@ -214,6 +212,6 @@ describe('compile_thread_worker', () => { }).toThrow('exit'); spy.mockRestore(); }); - + }); - */ \ No newline at end of file + */ diff --git a/ets2panda/driver/build_system/test/ut/dependency_analyzer/dependency_analyzer.test.ts b/ets2panda/driver/build_system/test/ut/dependency_analyzer/dependency_analyzer.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ets2panda/driver/build_system/test/ut/fileManagerTest/filemanager.test.ts b/ets2panda/driver/build_system/test/ut/fileManagerTest/filemanager.test.ts index 8f760d966a2395e0d66743923291d87a6218e93a..5f8af67582a8539c8c088d9cada3661a395e9339 100755 --- a/ets2panda/driver/build_system/test/ut/fileManagerTest/filemanager.test.ts +++ b/ets2panda/driver/build_system/test/ut/fileManagerTest/filemanager.test.ts @@ -20,7 +20,7 @@ import * as utils from '../../../src/util/utils'; // This test suite is for the FileManager class, which manages file paths and language versions in the build system. describe('class FileManager', () => { const mockBuildConfig = { - dependentModuleList: [ + dependencyModuleList: [ { packageName: 'modA', modulePath: '/mock/path/modA', @@ -82,7 +82,7 @@ describe('class FileManager', () => { expect(fm.getLanguageVersionByFilePath('/other/path/file.ets')).toBe(LANGUAGE_VERSION.ARKTS_1_1); FileManager.init({ ...mockBuildConfig, - dependentModuleList: [ + dependencyModuleList: [ { packageName: 'modH', modulePath: '/mock/hybrid', @@ -96,7 +96,7 @@ describe('class FileManager', () => { jest.spyOn(FileManager as any, 'isFirstLineUseStatic').mockReturnValue(true); expect(fm.getLanguageVersionByFilePath('/mock/hybrid/file.ets')).toBe(LANGUAGE_VERSION.ARKTS_1_1); FileManager.init({ - dependentModuleList: [ + dependencyModuleList: [ { packageName: 'modH', modulePath: '/mock/hybrid', @@ -123,7 +123,7 @@ describe('class FileManager', () => { fm = FileManager.getInstance(); expect(fm.getLanguageVersionByFilePath('/any/path/file.ets')).toBe(LANGUAGE_VERSION.ARKTS_1_1); FileManager.init({ - dependentModuleList: [ + dependencyModuleList: [ { packageName: 'modB', modulePath: '/mock/path/modB', @@ -138,7 +138,7 @@ describe('class FileManager', () => { expect(fm.getLanguageVersionByFilePath('/mock/path/modB/file.ets')).toBe(LANGUAGE_VERSION.ARKTS_1_1); }); - test('empty dependentModuleList', () => { + test('empty dependencyModuleList', () => { // Make tsc ignore the access of private member or type error // @ts-ignore FileManager['initLanguageVersionFromDependentModuleMap']([]); @@ -152,7 +152,7 @@ describe('class FileManager', () => { // @ts-ignore expect(FileManager.instance).toBeUndefined(); FileManager.init({ - dependentModuleList: [], + dependencyModuleList: [], externalApiPaths: [], buildSdkPath: '/mock/sdk', compileFiles: [] diff --git a/ets2panda/driver/build_system/test/ut/generate_arktsconfigTest/generate_arktsconfig.test.ts b/ets2panda/driver/build_system/test/ut/generate_arktsconfigTest/generate_arktsconfig.test.ts index daca36aec1c60e867840182c5a863056afc755e6..02b0fe0f3fd1f1bbbaada203d01a03b4820198d9 100755 --- a/ets2panda/driver/build_system/test/ut/generate_arktsconfigTest/generate_arktsconfig.test.ts +++ b/ets2panda/driver/build_system/test/ut/generate_arktsconfigTest/generate_arktsconfig.test.ts @@ -60,7 +60,7 @@ const mockConfig: BuildConfig = { declgenV2OutPath: "./dist/declgen/v2", buildSdkPath: "./sdk", externalApiPaths: [], - dependentModuleList: [ + dependencyModuleList: [ ] } as any; diff --git a/ets2panda/driver/build_system/test/ut/loggerTest/logger.test.ts b/ets2panda/driver/build_system/test/ut/loggerTest/logger.test.ts index 1d79e806f572fac1200aa9accdcbb3548d8f7b9c..69e58e840ea731aaa2692cc6bea00adbc6f55718 100755 --- a/ets2panda/driver/build_system/test/ut/loggerTest/logger.test.ts +++ b/ets2panda/driver/build_system/test/ut/loggerTest/logger.test.ts @@ -13,157 +13,159 @@ * limitations under the License. */ -import { Logger, LogDataFactory, LogData } from '../../../src/logger'; -import { SubsystemCode, ErrorCode } from '../../../src/error_code'; -import { BuildConfig, BUILD_TYPE, BUILD_MODE } from '../../../src/types'; - -function createMockBuildConfig(): BuildConfig { - return { - getHvigorConsoleLogger: jest.fn(() => ({ - printInfo: jest.fn(), - printWarn: jest.fn(), - printDebug: jest.fn(), - printError: jest.fn(), - printErrorAndExit: jest.fn(), - })), - packageName: 'mockpkg', - moduleType: 'shared', - moduleRootPath: '/mock/module', - sourceRoots: [], - byteCodeHar: false, - plugins: {}, - compileFiles: [], - dependentModuleList: [], - buildType: BUILD_TYPE.BUILD, - buildMode: BUILD_MODE.DEBUG, - hasMainModule: true, - arkts: {} as any, - arktsGlobal: {} as any, - declgenV1OutPath: undefined, - declgenV2OutPath: undefined, - declgenBridgeCodePath: undefined, - buildSdkPath: '', - loaderOutPath: '', - cachePath: '', - externalApiPaths: [], - enableDeclgenEts2Ts: false, - //`as unknown` to satisfy TypeScript type checking - } as unknown as BuildConfig; -} +import { + ILogger, + Logger, + LogDataFactory, + LogData, + LoggerGetter, + SubsystemCode, + getConsoleLogger +} from '../../../src/logger'; +import { ErrorCode } from '../../../src/util/error'; +import { getMockLoggerGetter } from '../mock/data' // This test suite is for the Logger class, which handles logging for different subsystems in the build system. describe('test Logger class', () => { - let logger: Logger; - - beforeEach(() => { - Logger.destroyInstance(); - logger = Logger.getInstance(createMockBuildConfig()); - }); - - afterEach(() => { - Logger.destroyInstance(); - jest.restoreAllMocks(); - }); - - test('singleton', () => { - Logger.destroyInstance(); - expect(() => Logger.getInstance()).toThrow('projectConfig is required for the first instantiation.'); - const logger1 = Logger.getInstance(createMockBuildConfig()); - const logger2 = Logger.getInstance(); - expect(logger1).toBe(logger2); - const logger3 = Logger.getInstance(createMockBuildConfig()); - Logger.destroyInstance(); - const logger4 = Logger.getInstance(createMockBuildConfig()); - expect(logger3).not.toBe(logger4); - }); - - test('printInfo', () => { - const spy = jest.fn(); - (logger as any).loggerMap[SubsystemCode.BUILDSYSTEM].printInfo = spy; - (logger as any).loggerMap[SubsystemCode.BUILDSYSTEM].printWarn = spy; - (logger as any).loggerMap[SubsystemCode.BUILDSYSTEM].printDebug = spy; - logger.printInfo('info'); - logger.printWarn('warn'); - logger.printDebug('debug'); - expect(spy).toHaveBeenCalledWith('info'); - expect(spy).toHaveBeenCalledWith('warn'); - expect(spy).toHaveBeenCalledWith('debug'); - }); - - test('printError && printErrorAndExit', () => { - const spy = jest.fn(); - // insert persudo code '001' && '002' into the map, for testing. - (logger as any).loggerMap['001'] = { printError: spy }; - (logger as any).loggerMap['002'] = { printErrorAndExit: spy }; - let logData = LogDataFactory.newInstance('00100001' as ErrorCode, 'desc'); - logger.printError(logData); - expect((logger as any).hasErrorOccurred).toBe(true); - expect(spy).toHaveBeenCalledWith(logData); - logData = LogDataFactory.newInstance('00200001' as ErrorCode, 'desc'); - logger.printErrorAndExit(logData); - expect((logger as any).hasErrorOccurred).toBe(true); - expect(spy).toHaveBeenCalledWith(logData); - }); - - test('hasErrors && resetErrorFlag', () => { - expect(logger.hasErrors()).toBe(false); - (logger as any).hasErrorOccurred = true; - expect(logger.hasErrors()).toBe(true); - logger.resetErrorFlag(); - expect(logger.hasErrors()).toBe(false); - }); - - test('ValidErrorCode', () => { - expect((logger as any).isValidErrorCode('12345678')).toBe(true); - expect((logger as any).isValidErrorCode('1234567')).toBe(false); - expect((logger as any).isValidErrorCode('abcdefgh')).toBe(false); - expect(() => (logger as any).getLoggerFromSubsystemCode('INVALID')).toThrow('Invalid subsystemCode.'); - }); - - test('getLoggerFromSubsystemCode', () => { - const fakeLogger = { printInfo: jest.fn(), printWarn: jest.fn(), - printDebug: jest.fn(), printError: jest.fn(), printErrorAndExit: jest.fn() }; - (logger as any).loggerMap['FKLGR'] = fakeLogger; - expect((logger as any).getLoggerFromSubsystemCode('FKLGR')).toBe(fakeLogger); - }); - - test('getLoggerFromErrorCode', () => { - expect(() => (logger as any).getLoggerFromErrorCode('badcode')).toThrow('Invalid errorCode.'); - const fakeLogger = { - printInfo: jest.fn(), - printWarn: jest.fn(), - printDebug: jest.fn(), - printError: jest.fn(), - printErrorAndExit: jest.fn(), - }; - (logger as any).loggerMap['001'] = fakeLogger; - expect((logger as any).getLoggerFromErrorCode('00100001')).toBe(fakeLogger); - }); + + beforeEach(() => { + Logger.getInstance(getMockLoggerGetter()); + }); + + afterEach(() => { + Logger.destroyInstance(); + jest.restoreAllMocks(); + }); + + test('singleton', () => { + Logger.destroyInstance(); + expect(() => Logger.getInstance()).toThrow('loggerGetter is required for the first instantiation.'); + const logger1 = Logger.getInstance(getMockLoggerGetter()); + const logger2 = Logger.getInstance(); + expect(logger1).toBe(logger2); + const logger3 = Logger.getInstance(getMockLoggerGetter()); + Logger.destroyInstance(); + const logger4 = Logger.getInstance(getMockLoggerGetter()); + expect(logger3).not.toBe(logger4); + }); + + test('consoleLogger', () => { + getConsoleLogger('TEST') + }); + + test('printInfo', () => { + const spy: jest.Mock = jest.fn(); + Logger.destroyInstance(); + const logger = Logger.getInstance(getMockLoggerGetter(spy)); + logger.printInfo('info'); + logger.printWarn('warn'); + logger.printDebug('debug'); + expect(spy).toHaveBeenCalledWith('info'); + expect(spy).toHaveBeenCalledWith('warn'); + expect(spy).toHaveBeenCalledWith('debug'); + }); + + test('printError && printErrorAndExit', () => { + const spy: jest.Mock = jest.fn(); + const logger: Logger = Logger.getInstance(); + + (logger as any).loggerMap['001' as SubsystemCode] = getMockLoggerGetter(spy)('001' as SubsystemCode); + (logger as any).loggerMap['002' as SubsystemCode] = getMockLoggerGetter(spy)('002' as SubsystemCode); + + let logData = LogDataFactory.newInstance('00100001' as ErrorCode, 'desc'); + logger.printError(logData); + expect(logger.hasErrors()).toBe(true); + expect(spy).toHaveBeenCalledWith(logData); + + logData = LogDataFactory.newInstance('00200001' as ErrorCode, 'desc'); + logger.printErrorAndExit(logData); + expect(logger.hasErrors()).toBe(true); + expect(spy).toHaveBeenCalledWith(logData); + }); + + test('hasErrors && resetErrorFlag', () => { + const logger = Logger.getInstance() + expect(logger.hasErrors()).toBe(false); + (logger as any).hasErrorOccurred = true; + expect(logger.hasErrors()).toBe(true); + logger.resetErrorFlag(); + expect(logger.hasErrors()).toBe(false); + }); + + test('ValidErrorCode', () => { + const logger = Logger.getInstance() + expect((logger as any).isValidErrorCode('12345678' as ErrorCode)).toBe(true); + expect((logger as any).isValidErrorCode('1234567' as ErrorCode)).toBe(false); + expect((logger as any).isValidErrorCode('abcdefgh' as ErrorCode)).toBe(false); + }); + + test('getLoggerFromSubsystemCode', () => { + const logger = Logger.getInstance(); + expect(() => { (logger as any).getLoggerFromSubsystemCode('INVALID' as SubsystemCode) }).toThrow('Invalid subsystemCode.'); + const fakeLogger: ILogger = getMockLoggerGetter()('FKLGR' as SubsystemCode); + (logger as any).loggerMap['FKLGR' as SubsystemCode] = fakeLogger; + expect((logger as any).getLoggerFromSubsystemCode('FKLGR' as SubsystemCode)).toBe(fakeLogger); + }); + + test('getLoggerFromErrorCode', () => { + const logger = Logger.getInstance(); + expect(() => (logger as any).getLoggerFromErrorCode('badcode' as ErrorCode)).toThrow('Invalid errorCode.'); + const fakeLogger: ILogger = (getMockLoggerGetter())('001' as SubsystemCode); + (logger as any).loggerMap['001' as SubsystemCode] = fakeLogger; + expect((logger as any).getLoggerFromErrorCode('00100001' as ErrorCode)).toBe(fakeLogger); + }); }); // This test suite is for the LogDataFactory and LogData classes, which are used to create log data instances. -describe('test LogDataFactory && LogData', () => { - test('LogDataFactory.newInstance creates LogData', () => { - let logData = LogDataFactory.newInstance( - '00100001' as ErrorCode, 'desc', 'cause', 'pos', ['sol1', 'sol2'], { foo: 'bar' }); - expect(logData).toBeInstanceOf(LogData); - expect(logData.code).toBe('00100001'); - expect(logData.description).toBe('desc'); - expect(logData.cause).toBe('cause'); - expect(logData.position).toBe('pos'); - expect(logData.solutions).toEqual(['sol1', 'sol2']); - expect(logData.moreInfo).toEqual({ foo: 'bar' }); - logData = LogDataFactory.newInstance('00100001' as ErrorCode, 'desc', 'cause', 'pos', ['sol1'], { foo: 'bar' }); - let str = logData.toString(); - expect(str).toContain('ERROR Code: 00100001 desc'); - expect(str).toContain('Error Message: cause pos'); - expect(str).toContain('> sol1'); - expect(str).toContain('More Info:'); - expect(str).toContain('FOO: bar'); - logData = LogDataFactory.newInstance('00100001' as ErrorCode, 'desc', '', '', [''], undefined); - str = logData.toString(); - expect(str).toContain('ERROR Code: 00100001 desc'); - expect(str).not.toContain('Error Message:'); - expect(str).not.toContain('More Info:'); - }); +describe('test LogDataFactory and LogData', () => { + test('LogDataFactory.newInstance creates LogData', () => { + let logData = LogDataFactory.newInstance( + '00100001' as ErrorCode, 'desc', 'cause', 'pos', ['sol1', 'sol2'], { foo: 'bar' }); + expect(logData).toBeInstanceOf(LogData); + expect(logData.code).toBe('00100001'); + expect(logData.description).toBe('desc'); + expect(logData.cause).toBe('cause'); + expect(logData.position).toBe('pos'); + expect(logData.solutions).toEqual(['sol1', 'sol2']); + expect(logData.moreInfo).toEqual({ foo: 'bar' }); + logData = LogDataFactory.newInstance('00100001' as ErrorCode, 'desc', 'cause', 'pos', ['sol1'], { foo: 'bar' }); + let str = logData.toString(); + expect(str).toContain('ERROR Code: 00100001 desc'); + expect(str).toContain('Error Message: cause pos'); + expect(str).toContain('> sol1'); + expect(str).toContain('More Info:'); + expect(str).toContain('FOO: bar'); + logData = LogDataFactory.newInstance('00100001' as ErrorCode, 'desc'); + str = logData.toString(); + expect(str).toContain('ERROR Code: 00100001 desc'); + expect(str).not.toContain('Error Message:'); + expect(str).not.toContain('More Info:'); + }); +}); + +describe('test console logger', () => { + test('print', () => { + const logger = getConsoleLogger('test'); + const spy = jest.fn() + global.console.info = spy + global.console.debug = spy + global.console.warn = spy + global.console.error = spy + global.process.exit = jest.fn() as any + + logger.printInfo('info') + logger.printDebug('debug') + logger.printWarn('warn') + const errorData: LogData = LogDataFactory.newInstance('00100001' as ErrorCode, 'desc'); + const errorDataStr: string = errorData.toString(); + + logger.printError(errorData) + logger.printErrorAndExit(errorData) + + expect(spy).toHaveBeenCalledWith('info'); + expect(spy).toHaveBeenCalledWith('warn'); + expect(spy).toHaveBeenCalledWith('debug'); + expect(spy).toHaveBeenNthCalledWith(4, errorDataStr); + expect(spy).toHaveBeenNthCalledWith(5, errorDataStr); + }); }); diff --git a/ets2panda/driver/build_system/test/ut/mock/mockData.ts b/ets2panda/driver/build_system/test/ut/mock/data.ts similarity index 45% rename from ets2panda/driver/build_system/test/ut/mock/mockData.ts rename to ets2panda/driver/build_system/test/ut/mock/data.ts index 4b26f7de06175f8a302c081c463d2db876c38c3c..a1886e043052c965093230b3ce69c81c2d052c16 100755 --- a/ets2panda/driver/build_system/test/ut/mock/mockData.ts +++ b/ets2panda/driver/build_system/test/ut/mock/data.ts @@ -13,7 +13,88 @@ * limitations under the License. */ -import { ModuleInfo } from '../../../src/types'; +import { + BUILD_MODE, + BUILD_TYPE, + BuildConfig, + ES2PANDA_MODE, + ModuleInfo, + OHOS_MODULE_TYPE +} from '../../../src/types'; + +import { + ILogger, + LoggerGetter, +} from '../../../src/logger'; + +export function getMockedBuildConfig(): BuildConfig { + return { + // BuildBaseConfig + buildType: BUILD_TYPE.BUILD, + buildMode: BUILD_MODE.DEBUG, + es2pandaMode: ES2PANDA_MODE.RUN, + isBuildConfigModified: undefined, + recordType: undefined, + + // DeclGenConfig + enableDeclgenEts2Ts: false, + declgenV1OutPath: undefined, + declgenV2OutPath: undefined, + declgenBridgeCodePath: undefined, + skipDeclCheck: undefined, + + // LoggerConfig + getHvigorConsoleLogger: undefined, + + // ModuleConfig + packageName: "test", + moduleType: OHOS_MODULE_TYPE.HAR, + moduleRootPath: "/test/path", + sourceRoots: ["./"], + byteCodeHar: false, + entryFile: "index.ets", + + // PathConfig + loaderOutPath: "./dist", + cachePath: "./dist/cache", + buildSdkPath: "/path/to/sdk", + pandaSdkPath: undefined, + pandaStdlibPath: undefined, + externalApiPaths: [], + abcLinkerPath: undefined, + dependencyAnalyzerPath: undefined, + sdkAliasConfigPaths: undefined, + sdkAliasMap: new Map(), + interopSDKPaths: new Set(), + interopApiPaths: [], + projectRootPath: '', + + // FrameworkConfig + frameworkMode: undefined, + useEmptyPackage: undefined, + + // BuildConfig + plugins: {}, + paths: {}, + compileFiles: ["test/path/index.ets"], + dependencyModuleList: [], + aliasConfig: {} + + }; +} + +export function getMockLoggerGetter(spyMock?: jest.Mock): LoggerGetter { + return (): ILogger => { + return { + printInfo: spyMock ?? jest.fn(), + printWarn: spyMock ?? jest.fn(), + printDebug: spyMock ?? jest.fn(), + printError: spyMock ?? jest.fn(), + printErrorAndExit: spyMock ?? jest.fn(), + } + } +} + export const moduleInfoWithNullSourceRoots: ModuleInfo = { isMainModule: true, @@ -23,15 +104,13 @@ export const moduleInfoWithNullSourceRoots: ModuleInfo = { sourceRoots: [], entryFile: 'index.ets', arktsConfigFile: 'arktsconfig.json', - compileFileInfos: [], declgenV1OutPath: undefined, declgenV2OutPath: undefined, declgenBridgeCodePath: undefined, byteCodeHar: false, - staticDepModuleInfos: new Map(), - dynamicDepModuleInfos: new Map(), - dependenciesSet: new Set(), - dependentSet: new Set(), + dependencies: [], + staticDependencyModules: new Map(), + dynamicDependencyModules: new Map(), }; export const moduleInfoWithFalseEts2Ts: ModuleInfo = { @@ -42,15 +121,13 @@ export const moduleInfoWithFalseEts2Ts: ModuleInfo = { sourceRoots: ['/src/moduleA'], entryFile: 'index.ets', arktsConfigFile: 'arktsconfig.json', - compileFileInfos: [], declgenV1OutPath: undefined, declgenV2OutPath: undefined, declgenBridgeCodePath: undefined, byteCodeHar: false, - staticDepModuleInfos: new Map(), - dynamicDepModuleInfos: new Map(), - dependenciesSet: new Set(), - dependentSet: new Set(), + dependencies: [], + staticDependencyModules: new Map(), + dynamicDependencyModules: new Map(), }; export const moduleInfo: ModuleInfo = { @@ -61,19 +138,16 @@ export const moduleInfo: ModuleInfo = { sourceRoots: ['/src/moduleA'], entryFile: '/src/moduleA/index.ts', arktsConfigFile: '/path/to/moduleA/arktsConfig.json', - compileFileInfos: [], - dynamicDepModuleInfos: new Map(), - staticDepModuleInfos: new Map(), - dependenciesSet: new Set(), - dependentSet: new Set(), + dependencies: [], + staticDependencyModules: new Map(), + dynamicDependencyModules: new Map(), declgenV1OutPath: '/path/to/moduleA/declgen/v1', declgenV2OutPath: '/path/to/moduleA/declgen/v2', declgenBridgeCodePath: '/path/to/moduleA/bridge/code', language: "1.2", declFilesPath: '/path/to/moduleA/declFiles', - dependencies: [], byteCodeHar: true, - abcPath: '/path/to/moduleA/abc/file.abc' + abcPath: '/path/to/moduleA/abc/file.abc' }; export const mockModuleInfos: Map = new Map([ @@ -85,18 +159,9 @@ export const mockModuleInfos: Map = new Map([ ], ]); -export let mockLogger: mockLogger = { - printErrorAndExit: jest.fn(() => { throw new Error('Exit with error.'); }), - printWarn: jest.fn(), -}; -interface mockLogger { - printErrorAndExit: jest.Mock; - printWarn: jest.Mock; -} - describe('mockData', () => { - it('should load correctly', () => { - const mock = require('./mockData'); - expect(mock).toBeDefined(); - }); -}); \ No newline at end of file + it('should load correctly', () => { + const mock = require('./data'); + expect(mock).toBeDefined(); + }); +}); diff --git a/ets2panda/driver/build_system/test/ut/plugins_driverTest/plugins_driver.test.ts b/ets2panda/driver/build_system/test/ut/plugins_driverTest/plugins_driver.test.ts index b9fa0450e6d934728df4f55bee2eb57970b36ea1..495fae255b580816fe4e37f395755777936d7461 100755 --- a/ets2panda/driver/build_system/test/ut/plugins_driverTest/plugins_driver.test.ts +++ b/ets2panda/driver/build_system/test/ut/plugins_driverTest/plugins_driver.test.ts @@ -45,7 +45,7 @@ const mockConfig: BuildConfig = { buildSdkPath: "./sdk", externalApiPaths: [], - dependentModuleList: [ + dependencyModuleList: [ ] } as any; diff --git a/ets2panda/driver/build_system/test/ut/safeRealpath.test.ts b/ets2panda/driver/build_system/test/ut/safeRealpath.test.ts index cd1e822d8f4ee12e13866009941324211188c1c3..4a44a447a790560b1c17bb776ac92618499e851f 100644 --- a/ets2panda/driver/build_system/test/ut/safeRealpath.test.ts +++ b/ets2panda/driver/build_system/test/ut/safeRealpath.test.ts @@ -16,7 +16,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { safeRealpath } from '../../src/util/utils'; -import { ErrorCode } from '../../src/error_code'; +import { ErrorCode } from '../../src/util/error'; import { Logger } from '../../src/logger'; describe('safeRealpath', () => { @@ -60,7 +60,7 @@ describe('safeRealpath', () => { expect(e.code).toBe(ErrorCode.BUILDSYSTEM_PATH_RESOLVE_FAIL); } }); - + test('throws error for empty path string', () => { try { safeRealpath('', mockLogger); @@ -68,5 +68,5 @@ describe('safeRealpath', () => { expect(e.code).toBe(ErrorCode.BUILDSYSTEM_PATH_RESOLVE_FAIL); } }); - + }); diff --git a/ets2panda/driver/build_system/test/ut/utilsTest/utils.test.ts b/ets2panda/driver/build_system/test/ut/utilsTest/utils.test.ts index 403fcbcbbb49c83255f6bbdd57b71227bc51ba67..b3ced3d2da2958a6375c506a1e3c645a7d5d82fc 100755 --- a/ets2panda/driver/build_system/test/ut/utilsTest/utils.test.ts +++ b/ets2panda/driver/build_system/test/ut/utilsTest/utils.test.ts @@ -18,7 +18,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { ErrorCode, -} from '../../../src/error_code'; +} from '../../../src/util/error'; import { isWindows, isLinux, isMac, changeFileExtension, changeDeclgenFileExtension, diff --git a/ets2panda/driver/build_system/testSuite_ch.md b/ets2panda/driver/build_system/testSuite_ch.md index fc4646ecc4bd79deb386f58561ec0167a9ca6419..8cfb946c005be9c56bc81e011edfa937c72c02bf 100644 --- a/ets2panda/driver/build_system/testSuite_ch.md +++ b/ets2panda/driver/build_system/testSuite_ch.md @@ -475,7 +475,7 @@ mock 函数提供大量接口用于断言和检查,参考 [官方文档](https File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ---------------|---------|----------|---------|---------|--------------------------------------------------------- All files | 41.05 | 13.95 | 14.63 | 41.26 | - error_code.ts | 100 | 100 | 100 | 100 | + error.ts | 100 | 100 | 100 | 100 | logger.ts | 6.75 | 0 | 0 | 6.84 | 25-103,125-126,147-179,189-218 pre_define.ts | 100 | 100 | 100 | 100 | utils.ts | 36.76 | 0 | 23.07 | 36.76 | 51-53,57-60,64-71,77-78,82,87-95,99-109,114-120,128-143 diff --git a/ets2panda/driver/build_system/testSuite_en.md b/ets2panda/driver/build_system/testSuite_en.md index 0cf2e569e4c26d9de53dd144773db3bf8cf59a2a..a28417f7b100b56b8bd686fdff108c1cd9a73c6e 100644 --- a/ets2panda/driver/build_system/testSuite_en.md +++ b/ets2panda/driver/build_system/testSuite_en.md @@ -485,7 +485,7 @@ Example (may change in the future): File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ---------------|---------|----------|---------|---------|--------------------------------------------------------- All files | 41.05 | 13.95 | 14.63 | 41.26 | - error_code.ts | 100 | 100 | 100 | 100 | + error.ts | 100 | 100 | 100 | 100 | logger.ts | 6.75 | 0 | 0 | 6.84 | 25-103,125-126,147-179,189-218 pre_define.ts | 100 | 100 | 100 | 100 | utils.ts | 36.76 | 0 | 23.07 | 36.76 | 51-53,57-60,64-71,77-78,82,87-95,99-109,114-120,128-143 diff --git a/ets2panda/es2panda.h b/ets2panda/es2panda.h index b7e93a6cbdfdccd79d8f1575898297d78211e8cb..9b69010cf70ee3cf64fbcd98691dd9722f2d18d7 100644 --- a/ets2panda/es2panda.h +++ b/ets2panda/es2panda.h @@ -94,6 +94,7 @@ struct SourceFile { bool isModule {}; // NOTE(dkofanov): Should be aligned with 'Program::moduleInfo_'. bool isDeclForDynamicStaticInterop {}; + bool isExternalSourceImport {}; std::string_view dest {}; // NOLINTEND(misc-non-private-member-variables-in-classes) }; diff --git a/ets2panda/lexer/ETSLexer.cpp b/ets2panda/lexer/ETSLexer.cpp index 09ebb112982d974fccde8a068a795a75092bab36..bc0330997c2783c43c23cc60011ef620b055369e 100644 --- a/ets2panda/lexer/ETSLexer.cpp +++ b/ets2panda/lexer/ETSLexer.cpp @@ -20,6 +20,7 @@ namespace ark::es2panda::lexer { // NOLINTNEXTLINE(google-default-arguments) void ETSLexer::NextToken(NextTokenFlags flags) { + flags |= DefaultNextTokenFlags(); ETSKeywords kws(this, static_cast(flags & ~NextTokenFlags::KEYWORD_TO_IDENT)); Lexer::NextToken(&kws); } diff --git a/ets2panda/lexer/keywordsUtil.cpp b/ets2panda/lexer/keywordsUtil.cpp index f3bdff0a97c7bfff47e270c1a40b79abe5676fac..1614d719d32220f31ca13c69235b79db6539e651 100644 --- a/ets2panda/lexer/keywordsUtil.cpp +++ b/ets2panda/lexer/keywordsUtil.cpp @@ -231,7 +231,8 @@ void KeywordsUtil::ScanIdContinue() size_t cpSize {}; auto cp = Iterator().PeekCp(&cpSize); - if (!IsIdentifierPart(cp)) { + if (!IsIdentifierPart(cp) && + (cp != LEX_CHAR_PERCENT || (Flags() & NextTokenFlags::CHAR_PERCENT_ALLOWED) == 0)) { break; } diff --git a/ets2panda/lexer/lexer.h b/ets2panda/lexer/lexer.h index 4f5031912247c98c12da9f312d5cb32d3056afc7..464dd2a0e64e47ff388391efaea2b3cc5dc14ffc 100644 --- a/ets2panda/lexer/lexer.h +++ b/ets2panda/lexer/lexer.h @@ -40,6 +40,7 @@ enum class NextTokenFlags : uint32_t { NUMERIC_SEPARATOR_ALLOWED = 1U << 1U, BIGINT_ALLOWED = 1U << 2U, UNARY_MINUS = 1U << 3U, + CHAR_PERCENT_ALLOWED = 1U << 4U, }; class LexerPosition { @@ -269,6 +270,16 @@ public: return GetToken().Start(); } + NextTokenFlags DefaultNextTokenFlags() const + { + return defaultNextTokenFlags; + } + + void SetDefaultNextTokenFlags(NextTokenFlags flags) + { + defaultNextTokenFlags = flags; + } + protected: void NextToken(Keywords *kws); ArenaAllocator *Allocator(); @@ -377,11 +388,11 @@ protected: private: TemplateLiteralParserContext *tlCtx_ {}; ArenaAllocator *allocator_; - Keywords *kws_ {}; const parser::ParserContext *parserContext_; util::StringView source_; LexerPosition pos_; util::DiagnosticEngine &diagnosticEngine_; + NextTokenFlags defaultNextTokenFlags = NextTokenFlags::NONE; }; class TemplateLiteralParserContext { diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index d6c04b52feca3475996e137338f2d1570b3ad15a..edc97d8ffb091b48e64c1b7714aea939708265f9 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -108,6 +108,9 @@ std::unique_ptr ETSParser::InitLexer(const SourceFile &sourceFile) { GetProgram()->SetSource(sourceFile); auto lexer = std::make_unique(&GetContext(), DiagnosticEngine()); + if (sourceFile.isExternalSourceImport) { + lexer->SetDefaultNextTokenFlags(lexer::NextTokenFlags::CHAR_PERCENT_ALLOWED); + } SetLexer(lexer.get()); return lexer; } @@ -314,6 +317,7 @@ void ETSParser::ParseParseListElement(const util::ImportPathManager::ParseInfo & auto src = importData.HasSpecifiedDeclPath() ? importData.declPath : importData.resolvedSource; ES2PANDA_ASSERT(!extSrc.empty()); SourceFile sf {src, extSrc, importData.resolvedSource, false, importData.HasSpecifiedDeclPath()}; + sf.isExternalSourceImport = importData.IsExternalSourceImport(); parser::Program *newProg = ParseSource(sf); ES2PANDA_ASSERT(newProg != nullptr); if (!importData.IsImplicitPackageImported() || newProg->IsPackage()) {