import path from "path";
import { fileURLToPath } from "url";
import process from "process";
import os from "os";
import fs from "fs-extra";
import chalk from "chalk";
import which from "which";
import { buildMetadataFileName, documentationPageUrls, llamaCppDirectory, llamaDirectory, llamaLocalBuildBinsDirectory, llamaPrebuiltBinsDirectory, llamaToolchainsDirectory } from "../../config.js";
import { convertBuildOptionsToBuildOptionsJSON } from "../types.js";
import { spawnCommand, SpawnError } from "../../utils/spawnCommand.js";
import { downloadCmakeIfNeeded, fixXpackPermissions, getCmakePath, hasBuiltinCmake } from "../../utils/cmake.js";
import { getConsoleLogPrefix } from "../../utils/getConsoleLogPrefix.js";
import { withLockfile } from "../../utils/withLockfile.js";
import { getModuleVersion } from "../../utils/getModuleVersion.js";
import { ensureLlamaCppRepoIsCloned, isLlamaCppRepoCloned } from "./cloneLlamaCppRepo.js";
import { getBuildFolderNameForBuildOptions } from "./getBuildFolderNameForBuildOptions.js";
import { setLastBuildInfo } from "./lastBuildInfo.js";
import { getPlatform } from "./getPlatform.js";
import { logDistroInstallInstruction } from "./logDistroInstallInstruction.js";
import { testCmakeBinary } from "./testCmakeBinary.js";
import { getCudaNvccPaths } from "./detectAvailableComputeLayers.js";
import { detectWindowsBuildTools } from "./detectBuildTools.js";
import { asyncSome } from "./asyncSome.js";
import { ensurePrebuiltBinariesGithubReleaseAssetForBuildOptions } from "./prebuiltBinariesGithubReleaseAssets.js";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const buildConfigType = "Release";
const requiresMsvcOnWindowsFlags = ["blas", "cann", "cuda", "hip", "kompute", "musa", "sycl", "opencl"]
    .map((backend) => ("GGML_" + backend.toUpperCase()));
export async function compileLlamaCpp(buildOptions, compileOptions) {
    const { nodeTarget = process.version, updateLastBuildInfo: updateLastBuildInfoArg = true, includeBuildOptionsInBinaryFolderName = true, ensureLlamaCppRepoIsCloned: ensureLlamaCppRepoIsClonedArg = false, downloadCmakeIfNeeded: downloadCmakeIfNeededArg = false, ignoreWorkarounds = [], envVars = process.env, ciMode = false } = compileOptions;
    const platform = getPlatform();
    const buildFolderName = await getBuildFolderNameForBuildOptions(buildOptions);
    const finalBuildFolderName = includeBuildOptionsInBinaryFolderName
        ? buildFolderName.withCustomCmakeOptions
        : buildFolderName.withoutCustomCmakeOptions;
    const useWindowsLlvm = (platform === "win" &&
        (buildOptions.gpu === false ||
            buildOptions.gpu === "vulkan") &&
        !ignoreWorkarounds.includes("avoidWindowsLlvm") &&
        !buildOptions.customCmakeOptions.has("CMAKE_TOOLCHAIN_FILE") &&
        !requiresMsvcOnWindowsFlags.some((flag) => buildOptions.customCmakeOptions.has(flag)))
        ? areWindowsBuildToolsCapableForLlvmBuild(await detectWindowsBuildTools())
        : false;
    const outDirectory = path.join(llamaLocalBuildBinsDirectory, finalBuildFolderName);
    let parallelBuildThreads = getParallelBuildThreadsToUse(platform, buildOptions.gpu, ciMode);
    if (ignoreWorkarounds.includes("singleBuildThread"))
        parallelBuildThreads = 1;
    else if (ignoreWorkarounds.includes("reduceParallelBuildThreads"))
        parallelBuildThreads = reduceParallelBuildThreads(parallelBuildThreads);
    await fs.mkdirp(llamaLocalBuildBinsDirectory);
    try {
        await withLockfile({
            resourcePath: outDirectory
        }, async () => {
            try {
                if (ensureLlamaCppRepoIsClonedArg)
                    await ensureLlamaCppRepoIsCloned({ progressLogs: buildOptions.progressLogs });
                else if (!(await isLlamaCppRepoCloned()))
                    throw new Error(`"${llamaCppDirectory}" directory does not exist`);
                if (downloadCmakeIfNeededArg)
                    await downloadCmakeIfNeeded(buildOptions.progressLogs);
                const cmakePathArgs = await getCmakePathArgs();
                const cmakeGeneratorArgs = getCmakeGeneratorArgs(buildOptions.platform, buildOptions.arch, useWindowsLlvm);
                const toolchainFile = await getToolchainFileForArch(buildOptions.arch, useWindowsLlvm);
                const runtimeVersion = nodeTarget.startsWith("v") ? nodeTarget.slice("v".length) : nodeTarget;
                const cmakeCustomOptions = new Map(buildOptions.customCmakeOptions);
                const cmakeToolchainOptions = new Map();
                if (!cmakeCustomOptions.has("GGML_BUILD_NUMBER"))
                    cmakeCustomOptions.set("GGML_BUILD_NUMBER", "1");
                cmakeCustomOptions.set("CMAKE_CONFIGURATION_TYPES", buildConfigType);
                cmakeCustomOptions.set("NLC_CURRENT_PLATFORM", platform + "-" + process.arch);
                cmakeCustomOptions.set("NLC_TARGET_PLATFORM", buildOptions.platform + "-" + buildOptions.arch);
                cmakeCustomOptions.set("NLC_VARIANT", buildFolderName.binVariant);
                if (toolchainFile != null && !cmakeCustomOptions.has("CMAKE_TOOLCHAIN_FILE"))
                    cmakeToolchainOptions.set("CMAKE_TOOLCHAIN_FILE", toolchainFile);
                if (toolchainFile != null &&
                    buildOptions.gpu === "vulkan" &&
                    (useWindowsLlvm || (platform === "win" && buildOptions.arch === "arm64")) &&
                    !cmakeCustomOptions.has("GGML_VULKAN_SHADERS_GEN_TOOLCHAIN"))
                    cmakeToolchainOptions.set("GGML_VULKAN_SHADERS_GEN_TOOLCHAIN", toolchainFile);
                if (buildOptions.gpu === "metal" && process.platform === "darwin" && !cmakeCustomOptions.has("GGML_METAL"))
                    cmakeCustomOptions.set("GGML_METAL", "1");
                else if (!cmakeCustomOptions.has("GGML_METAL"))
                    cmakeCustomOptions.set("GGML_METAL", "OFF");
                if (buildOptions.gpu === "cuda" && !cmakeCustomOptions.has("GGML_CUDA"))
                    cmakeCustomOptions.set("GGML_CUDA", "1");
                if (buildOptions.gpu === "vulkan" && !cmakeCustomOptions.has("GGML_VULKAN"))
                    cmakeCustomOptions.set("GGML_VULKAN", "1");
                if (!cmakeCustomOptions.has("GGML_CCACHE"))
                    cmakeCustomOptions.set("GGML_CCACHE", "OFF");
                // avoid linking to extra libraries that we don't use
                {
                    if (!cmakeCustomOptions.has("LLAMA_CURL") || isCmakeValueOff(cmakeCustomOptions.get("LLAMA_CURL")))
                        cmakeCustomOptions.set("LLAMA_CURL", "OFF");
                    if (!cmakeCustomOptions.has("LLAMA_HTTPLIB") || isCmakeValueOff(cmakeCustomOptions.get("LLAMA_HTTPLIB"))) {
                        cmakeCustomOptions.set("LLAMA_HTTPLIB", "OFF");
                        if (!cmakeCustomOptions.has("LLAMA_BUILD_BORINGSSL"))
                            cmakeCustomOptions.set("LLAMA_BUILD_BORINGSSL", "OFF");
                        if (!cmakeCustomOptions.has("LLAMA_OPENSSL"))
                            cmakeCustomOptions.set("LLAMA_OPENSSL", "OFF");
                    }
                }
                if (buildOptions.platform === "win" && buildOptions.arch === "arm64" && !cmakeCustomOptions.has("GGML_OPENMP"))
                    cmakeCustomOptions.set("GGML_OPENMP", "OFF");
                if (useWindowsLlvm)
                    cmakeCustomOptions.set("GGML_OPENMP", "OFF");
                if (ciMode) {
                    if (!cmakeCustomOptions.has("CMAKE_OSX_DEPLOYMENT_TARGET"))
                        cmakeCustomOptions.set("CMAKE_OSX_DEPLOYMENT_TARGET", "14.0");
                    if (!cmakeCustomOptions.has("GGML_OPENMP"))
                        cmakeCustomOptions.set("GGML_OPENMP", "OFF");
                    if (!cmakeCustomOptions.has("GGML_NATIVE") || isCmakeValueOff(cmakeCustomOptions.get("GGML_NATIVE"))) {
                        cmakeCustomOptions.set("GGML_NATIVE", "OFF");
                        if (buildOptions.arch === "x64" && !cmakeCustomOptions.has("GGML_CPU_ALL_VARIANTS")) {
                            cmakeCustomOptions.set("GGML_CPU_ALL_VARIANTS", "ON");
                            cmakeCustomOptions.set("GGML_BACKEND_DL", "ON");
                        }
                        else if (!cmakeCustomOptions.has("GGML_BACKEND_DL"))
                            cmakeCustomOptions.set("GGML_BACKEND_DL", "ON");
                    }
                }
                await fs.remove(outDirectory);
                await spawnCommand("npm", [
                    "run", "-s", "cmake-js-llama", "--", "clean",
                    "--log-level", "warn",
                    "--out", path.relative(llamaDirectory, outDirectory),
                    ...cmakePathArgs
                ], __dirname, envVars, buildOptions.progressLogs);
                await spawnCommand("npm", [
                    "run", "-s", "cmake-js-llama", "--", "compile",
                    "--log-level", "warn",
                    "--config", buildConfigType,
                    "--arch=" + buildOptions.arch,
                    "--out", path.relative(llamaDirectory, outDirectory),
                    "--runtime-version=" + runtimeVersion,
                    "--parallel=" + parallelBuildThreads,
                    ...cmakeGeneratorArgs,
                    ...cmakePathArgs,
                    ...([
                        ...cmakeCustomOptions,
                        ...cmakeToolchainOptions
                    ].map(([key, value]) => "--CD" + key + "=" + value))
                ], __dirname, envVars, buildOptions.progressLogs);
                const compiledResultDirPath = await moveBuildFilesToResultDir(outDirectory);
                await fs.writeFile(path.join(compiledResultDirPath, buildMetadataFileName), JSON.stringify({
                    buildOptions: convertBuildOptionsToBuildOptionsJSON(buildOptions)
                }), "utf8");
                await fs.writeFile(path.join(outDirectory, "buildDone.status"), "", "utf8");
                if (updateLastBuildInfoArg) {
                    await setLastBuildInfo({
                        folderName: finalBuildFolderName
                    });
                }
            }
            finally {
                await fixXpackPermissions();
            }
        });
    }
    catch (err) {
        if (platform === "linux" && await which("make", { nothrow: true }) == null) {
            console.info("\n" +
                getConsoleLogPrefix(true) +
                chalk.yellow('It seems that "make" is not installed in your system. Install it to resolve build issues'));
            await logDistroInstallInstruction('To install "make", ', {
                linuxPackages: { apt: ["make"], apk: ["make"] },
                macOsPackages: { brew: ["make"] }
            });
        }
        else if (platform === "linux" && !(await testCmakeBinary(await getCmakePath()))) {
            console.info("\n" +
                getConsoleLogPrefix(true) +
                chalk.yellow('It seems that the used "cmake" doesn\'t work properly. Install it on your system to resolve build issues'));
            await logDistroInstallInstruction('To install "cmake", ', {
                linuxPackages: { apt: ["cmake"], apk: ["cmake"] },
                macOsPackages: { brew: ["cmake"] }
            });
        }
        else if (platform === "mac" && ((await which("clang", { nothrow: true })) == null || (err instanceof SpawnError &&
            err.combinedStd.toLowerCase().includes('"/usr/bin/cc" is not able to compile a simple test program'))))
            console.info("\n" +
                getConsoleLogPrefix(true) +
                chalk.yellow("It seems that Xcode command line tools are not installed in your system. Install it to resolve build issues\n") +
                getConsoleLogPrefix(true) +
                chalk.yellow('To install Xcode command line tools, run "xcode-select --install"'));
        else if (buildOptions.gpu === "cuda") {
            if (!ignoreWorkarounds.includes("cudaArchitecture") && (platform === "win" || platform === "linux") &&
                err instanceof SpawnError && (err.combinedStd.toLowerCase().includes("CUDA Toolkit not found".toLowerCase()) ||
                err.combinedStd.toLowerCase().includes("Failed to detect a default CUDA architecture".toLowerCase()) ||
                err.combinedStd.toLowerCase().includes("CMAKE_CUDA_COMPILER-NOTFOUND".toLowerCase()) || (err.combinedStd.toLowerCase().includes("Tell CMake where to find the compiler by setting either the environment".toLowerCase()) &&
                err.combinedStd.toLowerCase().includes('variable "CUDACXX" or the CMake cache entry CMAKE_CUDA_COMPILER to the full'.toLowerCase())) || (err.combinedStd.toLowerCase().includes("The CUDA compiler".toLowerCase()) &&
                err.combinedStd.toLowerCase().includes("is not able to compile a simple test program".toLowerCase()) &&
                err.combinedStd.toLowerCase().includes("nvcc fatal".toLowerCase())))) {
                for (const { nvccPath, cudaHomePath } of await getCudaNvccPaths()) {
                    if (buildOptions.progressLogs)
                        console.info(getConsoleLogPrefix(true) +
                            `Trying to compile again with "CUDACXX=${nvccPath}" and "CUDA_PATH=${cudaHomePath}" environment variables`);
                    try {
                        return await compileLlamaCpp(buildOptions, {
                            ...compileOptions,
                            envVars: {
                                ...envVars,
                                CUDACXX: nvccPath,
                                "CUDA_PATH": cudaHomePath
                            },
                            ignoreWorkarounds: [...ignoreWorkarounds, "cudaArchitecture"]
                        });
                    }
                    catch (err) {
                        if (buildOptions.progressLogs)
                            console.error(getConsoleLogPrefix(true, false), err);
                    }
                }
            }
            else if ((!ignoreWorkarounds.includes("reduceParallelBuildThreads") || !ignoreWorkarounds.includes("singleBuildThread")) &&
                (platform === "win" || platform === "linux") &&
                err instanceof SpawnError &&
                reduceParallelBuildThreads(parallelBuildThreads) !== parallelBuildThreads && (err.combinedStd.toLowerCase().includes("LLVM error : out of memory".toLowerCase()) ||
                err.combinedStd.toLowerCase().includes("compiler is out of heap space".toLowerCase()))) {
                if (buildOptions.progressLogs) {
                    if (ignoreWorkarounds.includes("reduceParallelBuildThreads"))
                        console.info(getConsoleLogPrefix(true) + "Trying to compile again with a single build thread");
                    else
                        console.info(getConsoleLogPrefix(true) + "Trying to compile again with reduced parallel build threads");
                }
                try {
                    return await compileLlamaCpp(buildOptions, {
                        ...compileOptions,
                        ignoreWorkarounds: [
                            ...ignoreWorkarounds,
                            ignoreWorkarounds.includes("reduceParallelBuildThreads")
                                ? "singleBuildThread"
                                : "reduceParallelBuildThreads"
                        ]
                    });
                }
                catch (err) {
                    if (buildOptions.progressLogs)
                        console.error(getConsoleLogPrefix(true, false), err);
                }
            }
            console.info("\n" +
                getConsoleLogPrefix(true) +
                chalk.yellow("To resolve errors related to CUDA compilation, see the CUDA guide: ") +
                documentationPageUrls.CUDA);
        }
        else if (buildOptions.gpu === "vulkan")
            console.info("\n" +
                getConsoleLogPrefix(true) +
                chalk.yellow("To resolve errors related to Vulkan compilation, see the Vulkan guide: ") +
                documentationPageUrls.Vulkan);
        else if (useWindowsLlvm && !ciMode) {
            if (buildOptions.progressLogs)
                console.info(getConsoleLogPrefix(true) + "Trying to compile again without LLVM");
            try {
                return await compileLlamaCpp(buildOptions, {
                    ...compileOptions,
                    ignoreWorkarounds: [...ignoreWorkarounds, "avoidWindowsLlvm"]
                });
            }
            catch (err) {
                if (buildOptions.progressLogs)
                    console.error(getConsoleLogPrefix(true, false), err);
            }
        }
        throw err;
    }
}
export async function getLocalBuildBinaryPath(folderName) {
    const binaryPath = path.join(llamaLocalBuildBinsDirectory, folderName, buildConfigType, "llama-addon.node");
    const buildMetadataFilePath = path.join(llamaLocalBuildBinsDirectory, folderName, buildConfigType, buildMetadataFileName);
    const buildDoneStatusPath = path.join(llamaLocalBuildBinsDirectory, folderName, "buildDone.status");
    const [binaryExists, buildMetadataExists, buildDoneStatusExists] = await Promise.all([
        fs.pathExists(binaryPath),
        fs.pathExists(buildMetadataFilePath),
        fs.pathExists(buildDoneStatusPath)
    ]);
    if (binaryExists && buildMetadataExists && buildDoneStatusExists)
        return binaryPath;
    return null;
}
export async function getLocalBuildBinaryBuildMetadata(folderName) {
    const buildMetadataFilePath = path.join(llamaLocalBuildBinsDirectory, folderName, buildConfigType, buildMetadataFileName);
    if (!(await fs.pathExists(buildMetadataFilePath)))
        throw new Error(`Could not find build metadata file for local build "${folderName}"`);
    const buildMetadata = await fs.readJson(buildMetadataFilePath);
    return buildMetadata;
}
export async function getPrebuiltBinaryPath(buildOptions, folderName) {
    const localPrebuiltBinaryDirectoryPath = path.join(llamaPrebuiltBinsDirectory, folderName);
    const binaryPath = await resolvePrebuiltBinaryPath(localPrebuiltBinaryDirectoryPath);
    if (binaryPath != null)
        return {
            binaryPath,
            folderName,
            folderPath: localPrebuiltBinaryDirectoryPath,
            extBackendsPath: undefined
        };
    const packagePrebuiltBinariesDirectoryPath = await getPrebuiltBinariesPackageDirectoryForBuildOptions(buildOptions);
    if (packagePrebuiltBinariesDirectoryPath == null)
        return null;
    const prebuiltBinariesDirPath = typeof packagePrebuiltBinariesDirectoryPath === "string"
        ? packagePrebuiltBinariesDirectoryPath
        : packagePrebuiltBinariesDirectoryPath.binsDir;
    const prebuiltBinariesExtDirPath = typeof packagePrebuiltBinariesDirectoryPath === "string"
        ? undefined
        : packagePrebuiltBinariesDirectoryPath.extBinsDir;
    const packagePrebuiltBinaryDirectoryPath = path.join(prebuiltBinariesDirPath, folderName);
    const extPackagePrebuiltBinaryDirectoryPath = prebuiltBinariesExtDirPath == null
        ? undefined
        : path.join(prebuiltBinariesExtDirPath, folderName);
    const binaryPathFromPackage = await resolvePrebuiltBinaryPath(packagePrebuiltBinaryDirectoryPath);
    if (binaryPathFromPackage != null)
        return {
            binaryPath: binaryPathFromPackage,
            folderName,
            folderPath: packagePrebuiltBinaryDirectoryPath,
            extBackendsPath: extPackagePrebuiltBinaryDirectoryPath
        };
    return null;
}
export async function getPrebuiltBinaryBuildMetadata(folderPath, folderName) {
    const buildMetadataFilePath = path.join(folderPath, buildMetadataFileName);
    if (!(await fs.pathExists(buildMetadataFilePath)))
        throw new Error(`Could not find build metadata file for prebuilt build "${folderName}"`);
    const buildMetadata = await fs.readJson(buildMetadataFilePath);
    return buildMetadata;
}
async function moveBuildFilesToResultDir(outDirectory, canCreateReleaseDir = false) {
    const binFilesDirPaths = [
        path.join(outDirectory, "bin"),
        path.join(outDirectory, "llama.cpp", "bin")
    ];
    const compiledResultDirPath = path.join(outDirectory, buildConfigType);
    if (!await fs.pathExists(compiledResultDirPath)) {
        if (canCreateReleaseDir) {
            if (await asyncSome(binFilesDirPaths.map((dirPath) => fs.pathExists(dirPath))))
                await fs.ensureDir(compiledResultDirPath);
            else
                throw new Error(`Could not find ${buildConfigType} directory or any other output directory`);
        }
        else
            throw new Error(`Could not find ${buildConfigType} directory`);
    }
    for (const binFilesDirPath of binFilesDirPaths) {
        if (await fs.pathExists(binFilesDirPath)) {
            const itemNames = await fs.readdir(binFilesDirPath);
            await Promise.all(itemNames.map((itemName) => (fs.copy(path.join(binFilesDirPath, itemName), path.join(compiledResultDirPath, itemName), {
                overwrite: false
            }))));
        }
    }
    await applyResultDirFixes(compiledResultDirPath, path.join(outDirectory, "_temp"));
    return compiledResultDirPath;
}
async function applyResultDirFixes(resultDirPath, tempDirPath) {
    const releaseDirPath = path.join(resultDirPath, buildConfigType);
    if (await fs.pathExists(releaseDirPath)) {
        await fs.remove(tempDirPath);
        await fs.move(releaseDirPath, tempDirPath);
        const itemNames = await fs.readdir(tempDirPath);
        await Promise.all(itemNames.map((itemName) => (fs.move(path.join(tempDirPath, itemName), path.join(resultDirPath, itemName), {
            overwrite: true
        }))));
        await fs.remove(tempDirPath);
    }
    // the vulkan-shaders-gen binary is not needed at runtime
    const vulkanShadersGenBinary = path.join(resultDirPath, getPlatform() === "win"
        ? "vulkan-shaders-gen.exe"
        : "vulkan-shaders-gen");
    await fs.remove(vulkanShadersGenBinary);
}
async function resolvePrebuiltBinaryPath(prebuiltBinaryDirectoryPath) {
    const binaryPath = path.join(prebuiltBinaryDirectoryPath, "llama-addon.node");
    const buildMetadataFilePath = path.join(prebuiltBinaryDirectoryPath, buildMetadataFileName);
    const [binaryExists, buildMetadataExists] = await Promise.all([
        fs.pathExists(binaryPath),
        fs.pathExists(buildMetadataFilePath)
    ]);
    if (binaryExists && buildMetadataExists)
        return binaryPath;
    return null;
}
export async function checkWhetherPrebuiltBinariesModuleIsInstalled(gpu) {
    const prebuiltBinariesPaths = await getPrebuiltBinariesPackageDirectoryForBuildOptions({
        platform: getPlatform(),
        arch: process.arch,
        gpu
    });
    return prebuiltBinariesPaths != null;
}
function getPrebuiltBinariesPackageDirectoryForBuildOptions(buildOptions) {
    async function getBinariesPathFromModules(moduleImport) {
        try {
            const [binariesModule, currentModuleVersion] = await Promise.all([
                moduleImport(),
                getModuleVersion()
            ]);
            const { binsDir, packageVersion } = binariesModule?.getBinsDir?.() ?? {};
            if (binsDir == null || packageVersion !== currentModuleVersion)
                return null;
            return binsDir;
        }
        catch (err) {
            return null;
        }
    }
    async function getBinariesPathFromModulesWithExtModule(currentBuildOptions, moduleImport, extModuleImport) {
        const moduleBinsDir = await getBinariesPathFromModules(moduleImport);
        if (moduleBinsDir == null)
            return null;
        let extModuleBinsDir = await getBinariesPathFromModules(extModuleImport);
        if (extModuleBinsDir == null)
            extModuleBinsDir = await ensurePrebuiltBinariesGithubReleaseAssetForBuildOptions(currentBuildOptions);
        if (extModuleBinsDir == null)
            return moduleBinsDir;
        return {
            binsDir: moduleBinsDir,
            extBinsDir: extModuleBinsDir
        };
    }
    /* eslint-disable import/no-unresolved */
    if (buildOptions.platform === "mac") {
        if (buildOptions.arch === "arm64" && buildOptions.gpu === "metal")
            // @ts-ignore
            return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-mac-arm64-metal"));
        else if (buildOptions.arch === "x64" && buildOptions.gpu === false)
            // @ts-ignore
            return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-mac-x64"));
    }
    else if (buildOptions.platform === "linux") {
        if (buildOptions.arch === "x64") {
            if (buildOptions.gpu === "cuda")
                return getBinariesPathFromModulesWithExtModule(buildOptions, 
                // @ts-ignore
                () => import("@realtimex/node-llama-cpp-linux-x64-cuda"), 
                // @ts-ignore
                () => import("@realtimex/node-llama-cpp-linux-x64-cuda-ext"));
            else if (buildOptions.gpu === "vulkan")
                // @ts-ignore
                return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-linux-x64-vulkan"));
            else if (buildOptions.gpu === false)
                // @ts-ignore
                return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-linux-x64"));
        }
        else if (buildOptions.arch === "arm64")
            // @ts-ignore
            return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-linux-arm64"));
        else if (buildOptions.arch === "arm")
            // @ts-ignore
            return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-linux-armv7l"));
    }
    else if (buildOptions.platform === "win") {
        if (buildOptions.arch === "x64") {
            if (buildOptions.gpu === "cuda")
                return getBinariesPathFromModulesWithExtModule(buildOptions, 
                // @ts-ignore
                () => import("@realtimex/node-llama-cpp-win-x64-cuda"), 
                // @ts-ignore
                () => import("@realtimex/node-llama-cpp-win-x64-cuda-ext"));
            else if (buildOptions.gpu === "vulkan")
                // @ts-ignore
                return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-win-x64-vulkan"));
            else if (buildOptions.gpu === false)
                // @ts-ignore
                return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-win-x64"));
        }
        else if (buildOptions.arch === "arm64")
            // @ts-ignore
            return getBinariesPathFromModules(() => import("@realtimex/node-llama-cpp-win-arm64"));
    }
    /* eslint-enable import/no-unresolved */
    return null;
}
async function getCmakePathArgs() {
    if (await hasBuiltinCmake())
        return [];
    const cmakePath = await getCmakePath();
    if (cmakePath == null)
        return [];
    return ["--cmake-path", cmakePath];
}
async function getToolchainFileForArch(targetArch, windowsLlvmSupport = false) {
    let toolchainPrefix = "";
    if (process.platform === "win32" && process.arch === "arm64") {
        // a toolchain is needed to cross-compile to arm64 on Windows, and to compile on arm64 on Windows
    }
    else if (process.platform === "win32" && process.arch === "x64" && targetArch === "x64" && windowsLlvmSupport) {
        toolchainPrefix = "llvm.";
    }
    else if (process.arch === targetArch)
        return null;
    const platform = process.platform;
    const hostArch = process.arch;
    const toolchainFilename = `${toolchainPrefix}${platform}.host-${hostArch}.target-${targetArch}.cmake`;
    const filePath = path.join(llamaToolchainsDirectory, toolchainFilename);
    if (await fs.pathExists(filePath))
        return path.resolve(filePath);
    return null;
}
function getCmakeGeneratorArgs(targetPlatform, targetArch, windowsLlvmSupport) {
    if (targetPlatform === "win" && targetArch === "arm64")
        return ["--generator", "Ninja Multi-Config"];
    else if (windowsLlvmSupport && targetPlatform === "win" && process.arch === "x64" && targetArch === "x64")
        return ["--generator", "Ninja Multi-Config"];
    return [];
}
function getParallelBuildThreadsToUse(platform, gpu, ciMode = false) {
    const cpuCount = os.cpus().length;
    if (ciMode && platform === "win" && gpu === "cuda" && cpuCount === 4)
        return 3; // workaround for `compiler is out of heap space` error on GitHub Actions on Windows when building with CUDA
    if (cpuCount <= 4)
        return cpuCount;
    if (platform === "mac" && process.arch === "arm64")
        return cpuCount - 1;
    return cpuCount - 2;
}
function reduceParallelBuildThreads(originalParallelBuildThreads) {
    return Math.max(1, Math.round(originalParallelBuildThreads / 2));
}
function isCmakeValueOff(value) {
    return value === "OFF" || value === "0";
}
function areWindowsBuildToolsCapableForLlvmBuild(detectedBuildTools) {
    return detectedBuildTools.hasLlvm && detectedBuildTools.hasNinja && detectedBuildTools.hasLibExe;
}
//# sourceMappingURL=compileLLamaCpp.js.map