"use strict";
/*
 * Copyright (c) 2025-2026 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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseMode = void 0;
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const child_process = __importStar(require("child_process"));
const ets2panda_1 = require("../util/ets2panda");
const pre_define_1 = require("../pre_define");
const utils_1 = require("../util/utils");
const logger_1 = require("../logger");
const dependency_analyzer_1 = require("../dependency_analyzer");
const error_1 = require("../util/error");
const types_1 = require("../types");
const generate_arktsconfig_1 = require("./generate_arktsconfig");
const statsRecorder_1 = require("../util/statsRecorder");
const worker_exit_handler_1 = require("../util/worker_exit_handler");
const TaskManager_1 = require("../util/TaskManager");
const graph_1 = require("../util/graph");
var BuildSystemEvent;
(function (BuildSystemEvent) {
    BuildSystemEvent["COLLECT_MODULES"] = "Collect module infos";
    BuildSystemEvent["GEN_CONFIGS"] = "Generate arktsconfigs for modules";
    BuildSystemEvent["PROCESS_ENTRY_FILES"] = "Process entry files";
    BuildSystemEvent["BACKWARD_COMPAT"] = "Backward compatibility stuff";
    BuildSystemEvent["DEPENDENCY_ANALYZER"] = "Dependency analyzer";
    BuildSystemEvent["RUN_SIMULTANEOUS"] = "Run simultaneous";
    BuildSystemEvent["RUN_PARALLEL"] = "Run parallel";
    BuildSystemEvent["RUN_SEQUENTIAL"] = "Run sequential";
    BuildSystemEvent["RUN_LINKER"] = "Run linker";
    BuildSystemEvent["DECLGEN_V1_SEQUENTIAL"] = "Generate v1 declaration files (sequential)";
    BuildSystemEvent["DECLGEN_V1_PARALLEL"] = "Generate v1 declaration files (parallel)";
})(BuildSystemEvent || (BuildSystemEvent = {}));
function formEvent(event) {
    return '[Build system] ' + event;
}
class BaseMode {
    constructor(buildConfig) {
        this.declFileMap = new Map();
        this.buildConfig = buildConfig;
        this.entryFiles = new Set(buildConfig.compileFiles);
        this.fileToModule = new Map();
        this.moduleInfos = new Map();
        this.mergedAbcFile = path.resolve(this.outputDir, pre_define_1.MERGED_ABC_FILE);
        this.logger = logger_1.Logger.getInstance();
        this.abcFiles = new Set();
        this.moduleType = buildConfig.moduleType;
        this.statsRecorder = new statsRecorder_1.StatisticsRecorder(path.resolve(this.cacheDir, statsRecorder_1.BS_PERF_FILE_NAME), this.recordType, `Build system with mode: ${this.es2pandaMode}`);
        this.processBuildConfig();
        this.statsRecorder.record(formEvent(BuildSystemEvent.BACKWARD_COMPAT));
        this.backwardCompatibilityWorkaroundStub();
    }
    loadDeclFileMap() {
        const declMapFile = path.join(this.cacheDir, pre_define_1.DECL_FILE_MAP_NAME);
        if (!fs.existsSync(declMapFile)) {
            return;
        }
        try {
            const content = fs.readFileSync(declMapFile, 'utf-8');
            const data = JSON.parse(content);
            for (const [key, value] of Object.entries(data)) {
                this.declFileMap.set(key, value);
            }
        }
        catch (error) {
            const logData = logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ERRORS_OCCURRED, `Failed to load decl file map from ${declMapFile}.`, error instanceof Error ? error.message : String(error));
            this.logger.printError(logData);
        }
    }
    needsRegeneration(job) {
        const sourceFilePath = job.fileList[0];
        const sourceStat = fs.statSync(sourceFilePath);
        const currentModified = sourceStat.mtimeMs;
        const fileInfo = this.declFileMap.get(sourceFilePath);
        if (!fileInfo || fileInfo.sourceFileLastModified === null) {
            return true;
        }
        return currentModified > fileInfo.sourceFileLastModified;
    }
    async saveDeclFileMap() {
        const declMapFile = path.join(this.cacheDir, pre_define_1.DECL_FILE_MAP_NAME);
        const data = {};
        this.declFileMap.forEach((value, key) => {
            data[key] = value;
        });
        try {
            await fs.promises.mkdir(path.dirname(declMapFile), { recursive: true });
            await fs.promises.writeFile(declMapFile, JSON.stringify(data, null, 2));
        }
        catch (error) {
            this.logger.printError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ERRORS_OCCURRED, `Failed to save decl file map to disk.`, error instanceof Error ? error.message : String(error)));
            throw error;
        }
    }
    getOutputFilePaths(job) {
        const inputFilePath = job.fileInfo.input;
        const filePathFromModuleRoot = path.relative(job.fileInfo.moduleRoot, inputFilePath);
        const declEtsOutputPath = (0, utils_1.changeDeclgenFileExtension)(path.resolve(job.declgenConfig.output, job.fileInfo.moduleName, filePathFromModuleRoot), pre_define_1.DECL_ETS_SUFFIX);
        const glueCodeOutputPath = (0, utils_1.changeDeclgenFileExtension)(path.resolve(job.declgenConfig.bridgeCode, job.fileInfo.moduleName, filePathFromModuleRoot), pre_define_1.TS_SUFFIX);
        (0, utils_1.ensurePathExists)(declEtsOutputPath);
        (0, utils_1.ensurePathExists)(glueCodeOutputPath);
        return { declEtsOutputPath, glueCodeOutputPath };
    }
    async needsBackup(job) {
        const { declEtsOutputPath, glueCodeOutputPath } = this.getOutputFilePaths(job);
        let needsDeclBackup = false;
        let needsGlueCodeBackup = false;
        const declInfo = this.declFileMap.get(job.fileList[0]);
        const isFileExists = async (path) => {
            try {
                const stat = await fs.promises.stat(path);
                return stat.isFile();
            }
            catch {
                return false;
            }
        };
        const [declFileExists, glueCodeExists] = await Promise.all([isFileExists(declEtsOutputPath), isFileExists(glueCodeOutputPath)]);
        if (declFileExists) {
            if (declInfo?.declLastModified != null) {
                const declStat = await fs.promises.stat(declEtsOutputPath);
                const declModified = declStat.mtimeMs;
                if (declModified > declInfo.declLastModified) {
                    needsDeclBackup = true;
                }
            }
        }
        if (glueCodeExists) {
            if (declInfo?.glueCodeLastModified != null) {
                const glueStat = await fs.promises.stat(glueCodeOutputPath);
                const glueCodeModified = glueStat.mtimeMs;
                if (glueCodeModified > declInfo.glueCodeLastModified) {
                    needsGlueCodeBackup = true;
                }
            }
        }
        return { needsDeclBackup, needsGlueCodeBackup };
    }
    async backupFiles(job, needsDecl, needsGlue) {
        const { declEtsOutputPath, glueCodeOutputPath } = this.getOutputFilePaths(job);
        const doCopy = async (filePath, type) => {
            if (!fs.existsSync(filePath)) {
                return;
            }
            const backupPath = `${filePath}.backup`;
            try {
                await fs.promises.copyFile(filePath, backupPath);
                this.logger.printDebug(`Backup completed for ${type}: ${backupPath}`);
            }
            catch (error) {
                const logData = logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ERRORS_OCCURRED, `Critical: Failed to backup ${type} at ${filePath}.`, error instanceof Error ? error.message : String(error));
                this.logger.printError(logData);
                throw error;
            }
        };
        const backups = [];
        if (needsDecl) {
            backups.push(doCopy(declEtsOutputPath, 'declaration file'));
        }
        if (needsGlue) {
            backups.push(doCopy(glueCodeOutputPath, 'glue code file'));
        }
        await Promise.all(backups);
    }
    async updateDeclFileMapAsync(job) {
        const sourceFilePath = job.fileList[0];
        const { declEtsOutputPath, glueCodeOutputPath } = this.getOutputFilePaths(job);
        const [sourceFileStat, declStat, glueCodeStat] = await Promise.all([
            fs.promises.stat(sourceFilePath),
            fs.promises.stat(declEtsOutputPath).catch(() => null),
            fs.promises.stat(glueCodeOutputPath).catch(() => null)
        ]);
        if (declStat || glueCodeStat) {
            this.declFileMap.set(sourceFilePath, {
                delFilePath: declEtsOutputPath,
                declLastModified: declStat?.mtimeMs ?? null,
                glueCodeFilePath: glueCodeOutputPath,
                glueCodeLastModified: glueCodeStat?.mtimeMs ?? null,
                sourceFilePath: sourceFilePath,
                sourceFileLastModified: sourceFileStat.mtimeMs ?? null,
            });
            this.logger.printDebug(`Updated decl file map for: ${sourceFilePath}`);
        }
        else {
            const detail = `Expected outputs missing: ${declEtsOutputPath} or ${glueCodeOutputPath}`;
            const logData = logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ERRORS_OCCURRED, `Build integrity error for ${sourceFilePath}`, detail);
            this.logger.printError(logData);
            throw new Error(detail);
        }
    }
    get abcLinkerPath() {
        return this.buildConfig.abcLinkerPath;
    }
    get hasMainModule() {
        return this.buildConfig.hasMainModule;
    }
    get useEmptyPackage() {
        return this.buildConfig.useEmptyPackage ?? false;
    }
    get frameworkMode() {
        return this.buildConfig.frameworkMode ?? false;
    }
    get genDeclAnnotations() {
        return this.buildConfig.genDeclAnnotations ?? true;
    }
    get skipDeclCheck() {
        return this.buildConfig.skipDeclCheck ?? true;
    }
    get es2pandaMode() {
        return this.buildConfig.es2pandaMode;
    }
    get dumpDependencyGraph() {
        return this.buildConfig.dumpDependencyGraph;
    }
    get entryFile() {
        return this.buildConfig.entryFile;
    }
    get mainPackageName() {
        return this.buildConfig.packageName;
    }
    get mainModuleRootPath() {
        return this.buildConfig.moduleRootPath;
    }
    get mainModuleType() {
        return this.buildConfig.moduleType;
    }
    get outputDir() {
        return this.buildConfig.loaderOutPath;
    }
    get cacheDir() {
        return this.buildConfig.cachePath;
    }
    get dependencyModuleList() {
        return this.buildConfig.dependencyModuleList;
    }
    get enableDeclgenEts2Ts() {
        return this.buildConfig.enableDeclgenEts2Ts;
    }
    get isBuildConfigModified() {
        return this.buildConfig.isBuildConfigModified;
    }
    set isBuildConfigModified(modified) {
        this.buildConfig.isBuildConfigModified = modified;
    }
    get byteCodeHar() {
        return this.buildConfig.byteCodeHar;
    }
    get mainSourceRoots() {
        return this.buildConfig.sourceRoots;
    }
    get declgenV1OutPath() {
        return this.buildConfig.declgenV1OutPath;
    }
    get declgenV2OutPath() {
        return this.buildConfig.declgenV2OutPath;
    }
    get declgenBridgeCodePath() {
        return this.buildConfig.declgenBridgeCodePath;
    }
    get isDebug() {
        return this.buildConfig.buildMode === types_1.BUILD_MODE.DEBUG;
    }
    get dumpPerf() {
        return this.buildConfig.dumpPerf;
    }
    get recordType() {
        return this.buildConfig.recordType;
    }
    compile(id, job) {
        job.type = (this.moduleType === types_1.OHOS_MODULE_TYPE.HAR || this.moduleType === types_1.OHOS_MODULE_TYPE.SHARED) ? types_1.CompileJobType.DECL_ABC : job.type;
        const ets2panda = ets2panda_1.Ets2panda.getInstance();
        let errOccurred = false;
        ets2panda.initalize();
        try {
            ets2panda.compile(id, job, this.isDebug);
        }
        catch (error) {
            if (error instanceof error_1.DriverError) {
                this.logger.printError(error.logData);
                errOccurred = true;
            }
        }
        finally {
            ets2panda.finalize();
        }
        return !errOccurred;
    }
    async run() {
        this.statsRecorder.record(formEvent(BuildSystemEvent.DEPENDENCY_ANALYZER));
        const depAnalyzer = new dependency_analyzer_1.DependencyAnalyzer(this.buildConfig);
        const allOutputs = [];
        const buildGraph = depAnalyzer.getGraph(this.entryFiles, this.fileToModule, this.moduleInfos, allOutputs);
        if (!buildGraph.hasNodes()) {
            this.logger.printWarn('Nothing to compile. Exiting...');
            return;
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.RUN_SEQUENTIAL));
        // Just to init
        ets2panda_1.Ets2panda.getInstance(this.buildConfig);
        let success = true;
        const tasks = graph_1.Graph.topologicalSort(buildGraph)
            .map((nodeId) => {
            return buildGraph.getNodeById(nodeId);
        })
            .map((node) => {
            return { id: node.id, job: node.data };
        });
        while (tasks.length > 0) {
            const task = tasks.shift();
            const job = task.job;
            const id = task.id;
            if (job.fileList.length > 1) {
                // Compile cycle simultaneous
                this.logger.printDebug('Compiling cycle....');
                this.logger.printDebug(`file list: \n${job.fileList.join('\n')}`);
                const res = this.compileSimultaneous(id, job);
                success = res && success;
            }
            else {
                const res = this.compile(id, job);
                success = res && success;
            }
        }
        ets2panda_1.Ets2panda.destroyInstance();
        if (!success) {
            const logData = logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ERRORS_OCCURRED, 'One or more errors occured.');
            this.logger.printError(logData);
            throw new Error('Run failed.');
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.RUN_LINKER));
        this.mergeAbcFiles(allOutputs);
    }
    compileSimultaneous(id, job) {
        job.type = (this.moduleType === types_1.OHOS_MODULE_TYPE.HAR || this.moduleType === types_1.OHOS_MODULE_TYPE.SHARED) ? types_1.CompileJobType.DECL_ABC : job.type;
        const ets2panda = ets2panda_1.Ets2panda.getInstance(this.buildConfig);
        ets2panda.initalize();
        let errOccurred = false;
        try {
            ets2panda.compileSimultaneous(id, job, this.isDebug, this.dumpPerf);
        }
        catch (error) {
            if (error instanceof error_1.DriverError) {
                this.logger.printError(error.logData);
                errOccurred = true;
            }
        }
        finally {
            ets2panda.finalize();
        }
        return !errOccurred;
    }
    collectAbcFileFromByteCodeHar() {
        // 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 === types_1.OHOS_MODULE_TYPE.HAR) {
            return;
        }
        for (const [packageName, moduleInfo] of this.moduleInfos) {
            if (!(moduleInfo.moduleType === types_1.OHOS_MODULE_TYPE.HAR && moduleInfo.byteCodeHar)) {
                continue;
            }
            if (moduleInfo.language === pre_define_1.LANGUAGE_VERSION.ARKTS_1_1) {
                continue;
            }
            if (!moduleInfo.abcPath) {
                const logData = logger_1.LogDataFactory.newInstance(error_1.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 = logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ABC_FILE_NOT_EXIST_IN_BCHAR, `${moduleInfo.abcPath} does not exist. `);
                this.logger.printErrorAndExit(logData);
            }
            this.abcFiles.add(moduleInfo.abcPath);
        }
    }
    mergeAbcFiles(outPuts = []) {
        this.collectAbcFileFromByteCodeHar();
        let linkerInputFile = path.join(this.cacheDir, pre_define_1.LINKER_INPUT_FILE);
        let allFiles = outPuts.concat(Array.from(this.abcFiles));
        let linkerInputContent = allFiles.join(os.EOL);
        fs.writeFileSync(linkerInputFile, linkerInputContent);
        let abcLinkerCmd = ['"' + this.abcLinkerPath + '"'];
        abcLinkerCmd.push('--strip-unused');
        abcLinkerCmd.push('--output');
        abcLinkerCmd.push('"' + this.mergedAbcFile + '"');
        abcLinkerCmd.push('--');
        abcLinkerCmd.push('@' + '"' + linkerInputFile + '"');
        let abcLinkerCmdStr = abcLinkerCmd.join(' ');
        if ((0, utils_1.isMac)()) {
            const loadLibrary = 'DYLD_LIBRARY_PATH=' + '"' + process.env.DYLD_LIBRARY_PATH + '"';
            abcLinkerCmdStr = loadLibrary + ' ' + abcLinkerCmdStr;
        }
        this.logger.printDebug(abcLinkerCmdStr);
        (0, utils_1.ensurePathExists)(this.mergedAbcFile);
        try {
            child_process.execSync(abcLinkerCmdStr).toString();
        }
        catch (error) {
            if (error instanceof Error) {
                const logData = logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_LINK_ABC_FAIL, 'Link abc files failed.', error.message);
                this.logger.printError(logData);
            }
        }
    }
    getDependencyModules(moduleInfo) {
        const dynamicDependencyModules = new Map();
        const staticDependencyModules = new Map();
        // NOTE: workaround
        // NOTE: to be refactored
        this.processDependencyModule(moduleInfo.packageName, moduleInfo, dynamicDependencyModules, staticDependencyModules);
        if (moduleInfo.dependencies) {
            moduleInfo.dependencies.forEach((packageName) => {
                let dependency = this.moduleInfos.get(packageName);
                if (!dependency) {
                    throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND, `Module ${packageName} is not found in dependencyModuleList`));
                }
                this.processDependencyModule(packageName, dependency, dynamicDependencyModules, staticDependencyModules);
            });
        }
        return [dynamicDependencyModules, staticDependencyModules];
    }
    processDependencyModule(packageName, module, dynamicDependencyModules, staticDependencyModules) {
        if (module.language === pre_define_1.LANGUAGE_VERSION.ARKTS_1_2) {
            staticDependencyModules.set(packageName, module);
        }
        else if (module.language === pre_define_1.LANGUAGE_VERSION.ARKTS_1_1) {
            dynamicDependencyModules.set(packageName, module);
        }
        else if (module.language === pre_define_1.LANGUAGE_VERSION.ARKTS_HYBRID) {
            staticDependencyModules.set(packageName, module);
            dynamicDependencyModules.set(packageName, module);
        }
    }
    generateArkTSConfigForModules() {
        // Just to init the generator
        generate_arktsconfig_1.ArkTSConfigGenerator.getInstance(this.buildConfig);
        const dependenciesSets = new Map;
        // Fill dependenciesSets and generate ArktsConfigs
        this.moduleInfos.forEach((moduleInfo) => {
            dependenciesSets.set(moduleInfo.packageName, new Set());
            moduleInfo.dependencies?.forEach((dependency) => {
                dependenciesSets.get(moduleInfo.packageName).add(dependency);
            });
            generate_arktsconfig_1.ArkTSConfigGenerator.getInstance().generateArkTSConfigFile(moduleInfo, this.enableDeclgenEts2Ts);
        });
        // Merge ArktsConfigs
        // Start the recursive merge from the main module
        let arktsConfig = generate_arktsconfig_1.ArkTSConfigGenerator.getInstance().getArktsConfigByPackageName(this.mainPackageName);
        arktsConfig.mergeArktsConfigByDependencies(dependenciesSets.get(this.mainPackageName), dependenciesSets);
        dependenciesSets.forEach((_, module) => {
            let moduleInfo = this.moduleInfos.get(module);
            let arktsConfig = generate_arktsconfig_1.ArkTSConfigGenerator.getInstance().getArktsConfigByPackageName(module);
            fs.writeFileSync(moduleInfo.arktsConfigFile, JSON.stringify(arktsConfig.object, null, 2));
        });
    }
    collectModuleDependencies() {
        this.moduleInfos.forEach((moduleInfo) => {
            let [dynamicDepModules, staticDepModules] = this.getDependencyModules(moduleInfo);
            moduleInfo.dynamicDependencyModules = dynamicDepModules;
            moduleInfo.staticDependencyModules = staticDepModules;
        });
    }
    // ModuleFiles contain static file information of hsp and the packages that hsp depends on
    // This field only serves hsp
    collectModuleFiles() {
        if (!this.buildConfig.moduleFiles || this.buildConfig.moduleFiles.length === 0) {
            return;
        }
        const moduleFiles = this.buildConfig.moduleFiles;
        for (const moduleFile of moduleFiles) {
            let packageName = moduleFile.packageName;
            if (!this.moduleInfos.has(packageName)) {
                throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_PACKAGENAME_NOT_INCLUDED_IN_MODULEINFOS, `Package '${packageName}' is not included in moduleInfos.`));
            }
            this.moduleInfos.get(packageName).staticFiles = moduleFile.staticFiles;
        }
    }
    collectModuleInfos() {
        // NOTE: workaround for frameworkMode
        if (this.hasMainModule && (!this.mainPackageName || !this.mainModuleRootPath || !this.mainSourceRoots)) {
            throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL, 'Main module info is not correct.'));
        }
        const mainModuleInfo = this.getMainModuleInfo();
        this.moduleInfos.set(this.mainPackageName, mainModuleInfo);
        this.dependencyModuleList.forEach((dependency) => {
            if (!(0, utils_1.checkDependencyModuleInfoCorrectness)(dependency)) {
                throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_CORRECT_FAIL, 'Dependent module info is not correct.'));
            }
            if (this.moduleInfos.has(dependency.packageName)) {
                return;
            }
            // NOTE: workaround
            // NOTE: to be refactored
            const getNormalizedEntryFile = (dependency) => {
                if (path.isAbsolute(dependency.entryFile)) {
                    return path.relative(dependency.modulePath, dependency.entryFile);
                }
                return dependency.entryFile;
            };
            let moduleInfo = {
                isMainModule: false,
                packageName: dependency.packageName,
                moduleRootPath: dependency.modulePath,
                moduleType: dependency.moduleType,
                sourceRoots: dependency.sourceRoots,
                entryFile: getNormalizedEntryFile(dependency),
                arktsConfigFile: path.resolve(this.cacheDir, dependency.packageName, pre_define_1.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,
                staticFiles: []
            };
            this.moduleInfos.set(dependency.packageName, moduleInfo);
            this.moduleInfos.get(this.mainPackageName).dependencies.push(dependency.packageName);
        });
        this.collectModuleDependencies();
    }
    getMainModuleInfo() {
        const mainModuleInfo = this.dependencyModuleList.find((module) => module.packageName === this.mainPackageName);
        return {
            isMainModule: true,
            packageName: this.mainPackageName,
            moduleRootPath: mainModuleInfo?.modulePath ?? this.mainModuleRootPath,
            moduleType: mainModuleInfo?.moduleType ?? this.moduleType,
            sourceRoots: this.mainSourceRoots,
            entryFile: this.entryFile ?? '',
            arktsConfigFile: path.resolve(this.cacheDir, this.mainPackageName, pre_define_1.ARKTSCONFIG_JSON_FILE),
            dynamicDependencyModules: new Map(),
            staticDependencyModules: new Map(),
            declgenV1OutPath: mainModuleInfo?.declgenV1OutPath ?? this.declgenV1OutPath,
            declgenV2OutPath: mainModuleInfo?.declgenV2OutPath ?? this.declgenV2OutPath,
            declgenBridgeCodePath: mainModuleInfo?.declgenBridgeCodePath ?? this.declgenBridgeCodePath,
            byteCodeHar: this.byteCodeHar,
            language: mainModuleInfo?.language ?? pre_define_1.LANGUAGE_VERSION.ARKTS_1_2,
            declFilesPath: mainModuleInfo?.declFilesPath,
            dependencies: mainModuleInfo?.dependencies ?? [],
            staticFiles: []
        };
    }
    processEntryFiles() {
        this.entryFiles.forEach((file) => {
            for (const [_, moduleInfo] of this.moduleInfos) {
                const relativePath = path.relative(moduleInfo.moduleRootPath, file);
                if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
                    continue;
                }
                this.fileToModule.set(path.resolve(file), moduleInfo);
                return;
            }
            throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, 'File does not belong to any module in moduleInfos.', '', file));
        });
        if (!this.buildConfig.enableDeclgenEts2Ts) {
            this.entryFiles = new Set([...this.entryFiles].filter(file => !file.endsWith(pre_define_1.DECL_ETS_SUFFIX)));
        }
        this.logger.printDebug(`collected fileToModule ${JSON.stringify([...this.fileToModule.entries()], null, 1)}`);
    }
    processBuildConfig() {
        this.statsRecorder.record(formEvent(BuildSystemEvent.COLLECT_MODULES));
        this.collectModuleInfos();
        this.collectModuleFiles();
        this.logger.printDebug(`ModuleInfos: ${JSON.stringify([...this.moduleInfos], null, 1)}`);
        this.statsRecorder.record(formEvent(BuildSystemEvent.GEN_CONFIGS));
        this.generateArkTSConfigForModules();
        this.statsRecorder.record(formEvent(BuildSystemEvent.PROCESS_ENTRY_FILES));
        this.processEntryFiles();
    }
    backwardCompatibilityWorkaroundStub() {
        const mainModule = this.moduleInfos.get(this.mainPackageName);
        // NOTE: workaround (just to add entryFile to mainModule)
        // NOTE: to be refactored
        const mainModuleFileList = [...this.fileToModule.entries()].filter(([_, module]) => {
            return module.isMainModule;
        }).map(([file, _]) => {
            return file;
        });
        mainModule.entryFile = mainModuleFileList[0];
        this.logger.printDebug(`mainModule entryFile: ${mainModule.entryFile}`);
    }
    async runSimultaneous() {
        this.statsRecorder.record(formEvent(BuildSystemEvent.RUN_SIMULTANEOUS));
        const mainModule = this.moduleInfos.get(this.mainPackageName);
        let entryFile = mainModule.entryFile || [...this.entryFiles][0];
        let arktsConfigFile = mainModule.arktsConfigFile;
        let intermediateFilePath = path.resolve(this.cacheDir, pre_define_1.MERGED_INTERMEDIATE_FILE);
        this.logger.printDebug(`entryFile: ${entryFile}`);
        this.logger.printDebug(`module: ${JSON.stringify(mainModule, null, 1)}`);
        this.logger.printDebug(`arktsConfigFile: ${arktsConfigFile}`);
        // Just to init
        ets2panda_1.Ets2panda.getInstance(this.buildConfig);
        // We do not need any queues just compile a bunch of files
        // Ets2panda will build it simultaneous
        let res = this.compileSimultaneous('SimultaneousBuildId', {
            fileList: [...this.entryFiles],
            fileInfo: {
                input: entryFile,
                output: intermediateFilePath,
                arktsConfig: arktsConfigFile,
                moduleName: mainModule.packageName,
                moduleRoot: mainModule.moduleRootPath
            },
            declgenConfig: {
                output: mainModule.declgenV2OutPath
            },
            type: types_1.CompileJobType.ABC
        });
        ets2panda_1.Ets2panda.destroyInstance();
        if (!res) {
            throw new Error('Simultaneous build failed.');
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.RUN_LINKER));
        this.mergeAbcFiles([intermediateFilePath]);
    }
    async runParallel() {
        if (pre_define_1.ENABLE_CLUSTERS && this.entryFiles.size <= pre_define_1.CLUSTER_FILES_TRESHOLD) {
            await this.runSimultaneous();
            return;
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.DEPENDENCY_ANALYZER));
        const depAnalyzer = new dependency_analyzer_1.DependencyAnalyzer(this.buildConfig);
        const allOutputs = [];
        const buildGraph = depAnalyzer.getGraph(this.entryFiles, this.fileToModule, this.moduleInfos, allOutputs);
        if (!buildGraph.hasNodes()) {
            this.logger.printWarn('Nothing to compile. Exiting...');
            return;
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.RUN_PARALLEL));
        const taskManager = new TaskManager_1.TaskManager(worker_exit_handler_1.handleCompileProcessWorkerExit, false, pre_define_1.DEFAULT_WORKER_NUMS);
        const workerFactory = new TaskManager_1.DriverProcessFactory(path.resolve(__dirname, 'compile_process_worker.js'), ['process child:' + __filename], {
            stdio: ['inherit', 'inherit', 'inherit', 'ipc']
        });
        taskManager.startWorkers(workerFactory);
        const newNodes = [];
        for (const node of buildGraph.nodes) {
            const newType = (this.moduleType === types_1.OHOS_MODULE_TYPE.HAR || this.moduleType === types_1.OHOS_MODULE_TYPE.SHARED)
                ? types_1.CompileJobType.DECL_ABC : node.data.type;
            newNodes.push({
                id: node.id,
                data: {
                    ...node.data,
                    type: newType,
                    buildConfig: this.buildConfig
                },
                predecessors: node.predecessors,
                descendants: node.descendants,
            });
        }
        const newGraph = graph_1.Graph.createGraphFromNodes(newNodes);
        taskManager.buildGraph = newGraph;
        taskManager.initTaskQueue();
        const res = await taskManager.finish();
        if (!res) {
            const logData = logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ERRORS_OCCURRED, 'One or more errors occured.');
            this.logger.printError(logData);
            throw new Error('Parallel run failed.');
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.RUN_LINKER));
        this.mergeAbcFiles(allOutputs);
    }
    async runConcurrent() {
        // NOTE: TBD
        throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, 'Concurrent mode is currently unavailable.'));
    }
    declgenV1(job) {
        let module = this.fileToModule.get(job.fileInfo.input);
        let declgenV1OutPath = module.declgenV1OutPath;
        let declgenBridgeCodePath = module.declgenBridgeCodePath;
        let declgenJob = { ...job, declgenConfig: { output: declgenV1OutPath, bridgeCode: declgenBridgeCodePath } };
        let result = true;
        const ets2panda = ets2panda_1.Ets2panda.getInstance();
        ets2panda.initalize();
        try {
            ets2panda.declgenV1(declgenJob, this.skipDeclCheck, this.genDeclAnnotations);
        }
        catch (error) {
            // Report the error, do not crash the declgen process
            const err = error;
            this.logger.printError(err.logData);
            result = false;
        }
        finally {
            ets2panda.finalize();
        }
        return result;
    }
    async generateDeclarationV1() {
        this.statsRecorder.record(formEvent(BuildSystemEvent.DEPENDENCY_ANALYZER));
        const depAnalyzer = new dependency_analyzer_1.DependencyAnalyzer(this.buildConfig, false);
        const buildGraph = depAnalyzer.getGraph(this.entryFiles, this.fileToModule, this.moduleInfos, []);
        if (!buildGraph.hasNodes()) {
            this.logger.printWarn('Nothing to compile. Exiting...');
            return;
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.DECLGEN_V1_SEQUENTIAL));
        const jobs = graph_1.Graph.topologicalSort(buildGraph)
            .map((nodeId) => {
            return buildGraph.getNodeById(nodeId);
        })
            .map((node) => {
            return node.data;
        });
        // Just to init
        ets2panda_1.Ets2panda.getInstance(this.buildConfig);
        let success = true;
        while (jobs.length > 0) {
            let job = jobs.shift();
            const res = this.declgenV1(job);
            success = res && success;
        }
        ets2panda_1.Ets2panda.destroyInstance();
        if (!success) {
            throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_ERRORS_OCCURRED, 'One or more errors occured.'));
        }
    }
    async generateDeclarationV1Parallel() {
        this.statsRecorder.record(formEvent(BuildSystemEvent.DEPENDENCY_ANALYZER));
        this.loadDeclFileMap();
        const depAnalyzer = new dependency_analyzer_1.DependencyAnalyzer(this.buildConfig, false);
        const buildGraph = depAnalyzer.getGraph(this.entryFiles, this.fileToModule, this.moduleInfos, []);
        if (!buildGraph.hasNodes()) {
            this.logger.printWarn('Nothing to compile. Exiting...');
            return;
        }
        this.statsRecorder.record(formEvent(BuildSystemEvent.DECLGEN_V1_PARALLEL));
        const taskManager = new TaskManager_1.TaskManager(worker_exit_handler_1.handleDeclgenWorkerExit, true);
        const workerFactory = new TaskManager_1.DriverProcessFactory(path.resolve(__dirname, 'declgen_process_worker.js'), ['process child:' + __filename], {
            stdio: ['inherit', 'inherit', 'inherit', 'ipc']
        });
        taskManager.startWorkers(workerFactory);
        const newNodes = [];
        const declgenJobs = [];
        for (const node of buildGraph.nodes) {
            const module = this.fileToModule.get(node.data.fileList[0]);
            const declgenV1OutPath = module.declgenV1OutPath;
            const declgenBridgeCodePath = module.declgenBridgeCodePath;
            const declgenJob = {
                ...node.data,
                declgenConfig: { output: declgenV1OutPath, bridgeCode: declgenBridgeCodePath }
            };
            if (this.needsRegeneration(declgenJob)) {
                newNodes.push({
                    id: node.id,
                    data: { ...declgenJob, buildConfig: this.buildConfig },
                    predecessors: node.predecessors,
                    descendants: node.descendants,
                });
                declgenJobs.push(declgenJob);
            }
        }
        await Promise.all(declgenJobs.map(async (declgenJob) => {
            const { needsDeclBackup, needsGlueCodeBackup } = await this.needsBackup(declgenJob);
            if (needsDeclBackup || needsGlueCodeBackup) {
                await this.backupFiles(declgenJob, needsDeclBackup, needsGlueCodeBackup);
            }
        }));
        const newGraph = graph_1.Graph.createGraphFromNodes(newNodes);
        taskManager.buildGraph = newGraph;
        taskManager.initTaskQueue();
        // Ignore the result
        await taskManager.finish();
        await Promise.all(declgenJobs.map(async (declgenJob) => {
            await this.updateDeclFileMapAsync(declgenJob);
        }));
        await this.saveDeclFileMap();
    }
}
exports.BaseMode = BaseMode;
