#include "KMBuilderBuild.h"
#include "KMBuilderContext.h"
#include "common/KMException.h"
#include "common/KMLogger.h"

/**
 * @brief: 模块构建
 * @param: 缓存
 * @param: 模块module
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::doBuild(const shared_ptr<KMBuilderModule> &pBuildModule)
{
    return buildHelper(pBuildModule, m_subdir);
}

/**
 * @brief: 构建过程
 * @param: pBuilderCache缓存结构
 * @param: sourcedir子目录全路径名
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::buildHelper(const shared_ptr<KMBuilderModule> &pBuildModule, const string &sourcedir)
{
    KMTrace("KMBuilderBuild::buildHelper invoke begin");

    // 解压，代码提取
    if (!extract(pBuildModule, sourcedir))
    {
        return false;
    }

    string relativeSourceSubdir;
    string sourceSubdir = sourcedir;
    if (!pBuildModule->getData()->m_subdir.empty())
    {
        sourceSubdir = sourceSubdir + "/" + pBuildModule->getData()->m_subdir;
        relativeSourceSubdir = pBuildModule->getData()->m_subdir;
    }

    int type = pBuildModule->buildSystem();
    if (type == KMBuilderModule::BS_Unknow)
    {
        return false;
    }

    KMInfo("========================================================================");
    KMInfo("Building module " + pBuildModule->getData()->m_name + " in " + sourceSubdir);
    KMInfo("========================================================================");

    vector<string> buildArgs, env, secretEnv, confOpt, secretOpt;
    getBuildArgs(pBuildModule->getBuildOptions(), buildArgs);
    getBuildEnv(pBuildModule->getBuildOptions(), env);
    confOpt = pBuildModule->configOptions();
    getBuildConfigOpts(pBuildModule->getBuildOptions(), confOpt);
    getBuildSecretOpts(pBuildModule->getBuildOptions(), secretOpt);
    getBuildSecretEnv(pBuildModule->getBuildOptions(), secretEnv);

    int jobs = builderContext.getJobs();
    if (jobs <= 0)
        jobs = sysconf(_SC_NPROCESSORS_ONLN);

    if (pBuildModule->getData()->m_noParallelMake)
    {
        env.emplace_back("KAIMING_BUILDER_N_JOB=1");
    }
    else
    {
        env.emplace_back("KAIMING_BUILDER_N_JOB=" + to_string(jobs));
    }

    string config;
    if (!getConfigFilename(pBuildModule, type, sourcedir, config))
    {
        return false;
    }

    string appDir = builderContext.getAppDir();
    string name = pBuildModule->getData()->m_name;
    string ccacheDir = builderContext.getCcacheDir();
    vector<string> setupargs = setupBuildArgs(appDir, ccacheDir, name, sourcedir, relativeSourceSubdir, buildArgs, env);

    if (!pBuildModule->getData()->m_noAutogen && !genConfigFile(sourcedir, sourceSubdir, config, setupargs))
    {
        return false;
    }

    string buildDir = sourceSubdir;
    if (!config.empty() && fs::exists(config) && !genMakefile(pBuildModule, type, sourcedir, sourceSubdir, config, confOpt, secretOpt, buildDir, setupargs))
    {
        return false;
    }

    if (!isExistMakefile(buildDir, type))
    {
        return false;
    }

    vector<string> testargs;
    getTestArgs(pBuildModule->getBuildOptions(), testargs);
    vector<string> setuptestargs = setupBuildArgs(appDir, ccacheDir, name, sourcedir, relativeSourceSubdir, testargs, env);

    if (!makeAndInstall(pBuildModule, type, appDir, buildDir, secretEnv, setupargs, setuptestargs))
    {
        return false;
    }

    // TODO : 编译完成后的处理，strip，debug等，需要重写代码替换postProcess
    // if (!postProcess(pBuilderCache, pBuildModule->getBuildOptions(), appDir))
    //     return false;

    if (!builderContext.getIsKeepBuildDirs() || builderContext.getIsDeleteBuildDirs())
    {
        return cleanupBuildbdir(KMBuilderUtils::buildFilename(builderContext.getBuildDir(), name), sourceSubdir);
    }

    KMTrace("KMBuilderBuild::buildHelper invoke end");
    return true;
}

/**
 * @brief: 编译后处理
 * @param: pBuildCache缓存
 * @param: BuildOptions结构
 * @param: appDir目录
 * @return: true-成功，false-失败
 */
bool KMBuilderBuild::postProcess(const shared_ptr<KMBuilderCache> pBuildCache, const shared_ptr<KMBuildOptions> &opts, const string &appDir)
{
    vector<string> changes;
    if (!pBuildCache->getOutstandChanges(changes))
        return false;

    if (opts->m_strip)
        return strip(appDir, changes);

    if (!opts->m_noDebuginfo)
        return debugInfo(appDir, pBuildCache->getBuilderCache().buildDir, changes, !opts->m_noDebuginfoCompression);

    return true;
}

/**
 * @brief: debuginfo信息
 * @param: appDir目录
 * @param: changed文件列表
 * @param: flags标志
 * @return: true-成功，false-失败
 */
bool KMBuilderBuild::debugInfo(const string &appDir, const string &builddir, const vector<string> &changes, bool isCompressed)
{
    for (auto change : changes)
    {
        bool isShared, isStripped;
        if (!KMBuilderUtils::isElfFile(change, isShared, isStripped))
            continue;
        if (isStripped)
            continue;

        string debugDir, realDebugDir, sourceDirPath;
        string path = change;
        change = change.substr(appDir.length() + 1, change.length() - appDir.length() - 1);
        string parentDir = fs::path(change).parent_path().string();
        if (change.compare(0, strlen("files/"), "files/") == 0 && change.compare(0, strlen("files/lib/debug/"), "files/lib/debug/") != 0)
        {
            debugDir = appDir + "/files/lib/debug/" + change.substr(strlen("files/"), parentDir.length() - strlen("files/"));
            realDebugDir = "/app/lib/debug/" + change.substr(strlen("files/"), parentDir.length() - strlen("files/"));
            sourceDirPath = appDir + "/files/lib/debug/source";
        }
        else if (change.compare(0, strlen("usr/"), "usr/") == 0 && change.compare(0, strlen("usr/lib/debug/"), "usr/lib/debug/") != 0)
        {
            debugDir = appDir + "/usr/lib/debug/" + parentDir;
            realDebugDir = "/usr/lib/debug/" + parentDir;
            sourceDirPath = appDir + "/usr/lib/debug/source";
        }

        if (!debugDir.empty())
        {
            if (!KMBuilderUtils::ensureDir(debugDir))
                return false;
            if (!KMBuilderUtils::ensureDir(sourceDirPath))
                return false;
            string buildDir = "/run/build/";
            if (getBuildRuntime())
                buildDir = "/run/build-runtime/";
            fs::path changePath(change);
            string name = changePath.filename().string();
            string debugpath = debugDir + "/" + name + ".debug";
            string realDebugPath = realDebugDir + "/" + name + ".debug";

            vector<string> filerefs;
            // TODO: 因DEBUGEDIT工具未提供，暂不支持此功能,待提供后再放开
            // KMBuilderUtils::getDebuginfoFileRref(path, filerefs);
            for (const auto &fileref : filerefs)
            {
                if (fileref.compare(0, buildDir.length(), buildDir) != 0)
                    continue;

                string relativeFile = fileref.substr(buildDir.length(), fileref.length() - buildDir.length());
                fs::path src(builddir + "/" + relativeFile);
                fs::path dst(sourceDirPath + "/" + relativeFile);

                if (!fs::exists(dst.parent_path()) && !KMBuilderUtils::ensureDir(dst.parent_path().string()))
                    return false;

                if (fs::is_regular_file(src))
                {
                    fs::remove(dst);
                    fs::copy(src, dst);
                }
                else if (fs::is_directory(src) && !KMBuilderUtils::ensureDir(dst.string()))
                    return false;
            }

            if (isCompressed)
            {
                vector<string> args;
                args.emplace_back("eu-elfcompress");
                args.emplace_back("-t");
                args.emplace_back("zlib-gnu");
                args.emplace_back("-v");
                args.emplace_back(path);

                if (!KMBuilderUtils::spawn("", 0, args))
                {
                    KMError("eu-elfcompress failed ");
                    return false;
                }
            }

            vector<string> args;
            args.emplace_back("eu-strip");
            args.emplace_back("--remove-comment");
            args.emplace_back("--reloc-debug-sections");
            args.emplace_back("-f");
            args.emplace_back(debugpath);
            args.emplace_back("-F");
            args.emplace_back(realDebugPath);
            args.emplace_back(path);

            if (!KMBuilderUtils::spawn("", 0, args))
            {
                KMError("eu-strip failed ");
                return false;
            }
        }
    }

    return true;
}

/**
 * @brief: strip
 * @param: appDir目录
 * @param: changed文件列表
 * @return: true-成功，false-失败
 */
bool KMBuilderBuild::strip(const string &appDir, const vector<string> &changed)
{
    for (const auto change : changed)
    {
        bool isStriped, isShared;
        if (!KMBuilderUtils::isElfFile(change, isShared, isStriped))
            continue;
        if (isStriped)
            continue;

        vector<string> args;
        args.emplace_back("strip");
        args.emplace_back("--remove-section=.comment");
        args.emplace_back("--remove-section=.note");

        if (isShared)
            args.emplace_back("--strip-unneeded");

        args.emplace_back(change);

        if (!KMBuilderUtils::spawn("", 0, args))
            return false;
    }

    return true;
}

/**
 * @brief: 编译安装
 * @param: pBuildModule模块module
 * @param: type 编译类型
 * @param: buildDir 编译目录
 * @param: secEnv 加密环境变量
 * @param: setupArgs 构建参数
 * @param: setupTestArgs 测试参数
 * @return: true-成功，false-失败
 */
bool KMBuilderBuild::makeAndInstall(const shared_ptr<KMBuilderModule> &pBuildModule, const int &type, const string &appDir, string &buildDir, const vector<string> &secEnv, const vector<string> &setupArgs, const vector<string> &setupTestArgs)
{
    string makeCmd, makej, makel, testarg;
    switch (type)
    {
        case KMBuilderModule::BS_Meson:
        case KMBuilderModule::BS_Cmake_Ninja:
        {
            makeCmd = "ninja";
            makej = "-j1";
            testarg = "test";
            break;
        }
        case KMBuilderModule::BS_Autotools:
        case KMBuilderModule::BS_Qmake:
        case KMBuilderModule::BS_Cmake:
        {
            makeCmd = "make";
            testarg = "check";
            break;
        }
        case KMBuilderModule::BS_Simple:
            break;

        default:
            break;
    }

    if (!pBuildModule->getData()->m_testRule.empty())
        testarg = pBuildModule->getData()->m_testRule;

    if (!pBuildModule->getData()->m_noParallelMake)
    {
        int jobs = builderContext.getJobs();
        if (jobs <= 0)
            jobs = sysconf(_SC_NPROCESSORS_ONLN);
        makej = string("-j") + to_string(jobs);
        makel = string("-l") + to_string(jobs * 2);
    }

    if (!makeCmd.empty())
    {
        vector<any> args;
        vector<string> makeargs;
        getMakeArgs(pBuildModule->getBuildOptions(), pBuildModule->getData()->m_makeArgs, makeargs);
        args.emplace_back(makeCmd);
        args.emplace_back(makej);
        args.emplace_back(makel);
        args.emplace_back(string("strv"));
        args.emplace_back(makeargs);

        if (!build(buildDir, setupArgs, args))
            return false;
    }

    for (const auto cmd : pBuildModule->getData()->m_buildCommands)
    {
        vector<any> args;
        args.emplace_back(string("/bin/sh"));
        args.emplace_back(string("-c"));
        args.emplace_back(cmd);
        args.emplace_back(string("secret_env"));
        args.emplace_back(secEnv);

        if (!build(buildDir, setupArgs, args))
            return false;
    }

    if (!pBuildModule->getData()->m_noMakeInstall && makeCmd.length() > 0)
    {
        vector<any> args;
        args.emplace_back(makeCmd);
        if (pBuildModule->getData()->m_installRule.empty())
            args.emplace_back(string("install"));
        else
            args.emplace_back(pBuildModule->getData()->m_installRule);

        vector<string> installArgs;
        getInstallArgs(pBuildModule->getBuildOptions(), pBuildModule->getData()->m_makeInstallArgs, installArgs);
        args.emplace_back(string("strv"));
        args.emplace_back(installArgs);

        if (!build(buildDir, setupArgs, args))
            return false;
    }

    do
    {
        shared_ptr<KMBuilderConfig> pBuildconfig = builderContext.getBuilderConfig();
        if (pBuildconfig == nullptr)
            break;
        if (!pBuildconfig->getAttribute("separate-locales").has_value())
            break;
        bool bSeparate = any_cast<bool>(pBuildconfig->getAttribute("separate-locales"));
        if (!bSeparate)
            break;

        string rootdir;
        if (getBuildRuntime())
        {
            // rootdir = appDir + string("/usr");
            rootdir = appDir + string("/runtime");
        }
        else
        {
            rootdir = appDir + string("/files");
        }

        if (!KMBuilderUtils::migrateLocaleDirs(rootdir))
            return false;

    } while (0);

    for (const auto post : pBuildModule->getData()->m_postInstall)
    {
        vector<any> args;
        args.emplace_back(string("/bin/sh"));
        args.emplace_back(string("-c"));
        args.emplace_back(post);
        args.emplace_back(string("secret_env"));
        args.emplace_back(secEnv);

        if (!build(buildDir, setupArgs, args))
            return false;
    }

    // test unit
    if (pBuildModule->getData()->m_runTests)
    {
        KMInfo("Running tests");

        if (!makeCmd.empty() && !testarg.empty())
        {
            vector<any> args;
            args.emplace_back(makeCmd);
            args.emplace_back(testarg);

            if (!build(buildDir, setupTestArgs, args))
                return false;
        }

        for (const auto testcmd : pBuildModule->getData()->m_testCommands)
        {
            vector<any> args;
            args.emplace_back(string("/bin/sh"));
            args.emplace_back(string("-c"));
            args.emplace_back(testcmd);

            if (!build(buildDir, setupTestArgs, args))
                return false;
        }
    }

    return true;
}

/**
 * @brief: 获取编译选项前缀
 * @param: KMBuildOptions 构建选项
 * @return: 编译选项前缀
 */
string KMBuilderBuild::getOptionsPrefix(const shared_ptr<KMBuildOptions> &options)
{
    if (options && !options->m_prefix.empty())
    {
        return options->m_prefix;
    }

    // if (getBuildRuntime())
    // {
    //     // return "/usr";
    //     return "/app";
    // }

    // return "/app";

    // 使用overlay叠加挂载方式后，install阶段的prefix=/usr，不再是从flatpak参考来的/app或/usr了
    return "/usr";
}

/**
 * @brief: 获取编译选项libdir
 * @param: KMBuildOptions 构建选项
 * @return: 编译选项libdir
 */
string KMBuilderBuild::getOptionsLibdir(const shared_ptr<KMBuildOptions> &options)
{
    if (options && !options->m_libDir.empty())
    {
        return options->m_libDir;
    }

    if (getBuildRuntime())
    {
        // sdkconfig在新规范中已经没有了
        /*
        KMBuilderSdkConfig *psdkcongfig = builderContext.getBuilderSdkConfig();
        if (psdkcongfig == nullptr)
            return {};

        if (!psdkcongfig->m_libDir.empty())
            return psdkcongfig->m_libDir;
        */
    }

    return {};
}

/**
 * @brief: 获取是否编译运行时
 * @return: true-是, false-否
 */
bool KMBuilderBuild::getBuildRuntime()
{
    std::shared_ptr<KMBuilderManifest> manifest = builderContext.manifestConfig();
    return manifest->m_package.m_kind == PACKGE_KIND_RUNTIME;
}

/**
 * @brief: 获取install参数
 * @param: KMBuildOptions 构建选项
 * @param: moduleInstallargs 模块install参数
 * @param: args 返回的构建参数
 */
void KMBuilderBuild::getInstallArgs(const shared_ptr<KMBuildOptions> &options, const std::vector<std::string> &moduleInstallargs, vector<string> &args)
{
    for (const auto arg : moduleInstallargs)
        args.emplace_back(arg);

    for (const auto arg : options->m_makeInstallArgs)
        args.emplace_back(arg);
}

/**
 * @brief: 获取make参数
 * @param: KMBuildOptions 构建选项
 * @param: moduleMakeargs 模块make参数
 * @param: args 返回的构建参数
 */
void KMBuilderBuild::getMakeArgs(const shared_ptr<KMBuildOptions> &options, const std::vector<std::string> &moduleMakeargs, vector<string> &args)
{
    for (const auto arg : moduleMakeargs)
        args.emplace_back(arg);

    for (const auto arg : options->m_makeArgs)
        args.emplace_back(arg);
}

/**
 * @brief: 获取test参数
 * @param: KMBuildOptions 构建选项
 * @param: args 返回的构建参数
 */
void KMBuilderBuild::getTestArgs(const shared_ptr<KMBuildOptions> &options, vector<string> &args)
{
    for (const auto testarg : options->m_testArgs)
        args.emplace_back(testarg);

    args.emplace_back("--readonly");
}

/**
 * @brief: 解压源码
 * @param: pBuildModule模块module
 * @param: 子目录全路径名
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::extract(const shared_ptr<KMBuilderModule> &pBuildModule, const string &sourcedir)
{
    // TODO: 使用管道popen一次执行多个命令进行的源码提取，出现错误时，无法真正获取到错误状态，后续需要优化
    for (auto source : pBuildModule->buildSources())
    {
        if (source->type() == source->SRC_Archive || source->type() == source->SRC_Git)
        {
            bool unzip_status = source->unzipSource();
            if (unzip_status == false)
                return false;
        }
        else if (source->type() == KMBuilderSource::SRC_Patch)
        {
            static_cast<KMBuilderSourcePatch *>(source.get())->apply();
        }
        else if (source->type() == KMBuilderSource::SRC_Shell)
        {
            static_cast<KMBuilderSourceShell *>(source.get())->extractShell(pBuildModule,sourcedir);
        }
    }
    return true;
}

/**
 * @brief: makefile是否存在
 * @param: buildDir 编译目录
 * @param: type 编译类型
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::isExistMakefile(const string &buildDir, const int &type)
{
    switch (type)
    {
        case KMBuilderModule::BS_Meson:
        case KMBuilderModule::BS_Cmake_Ninja:
        {
            if (fs::exists(buildDir + "/build.ninja"))
            {
                return true;
            }
            
            throw KMException("Can't find build.ninja file in " + buildDir);
        }
        case KMBuilderModule::BS_Autotools:
        case KMBuilderModule::BS_Cmake:
        case KMBuilderModule::BS_Qmake:
        {
            vector<string> makefiles = { "Makefile", "makefile", "GNUmakefile" };
            for (auto makefile : makefiles)
            {
                if (fs::exists(buildDir + "/" + makefile))
                {
                    return true;
                }

                throw KMException("Can't find makefile in " + buildDir);
            }

            break;
        }
        default:
            return true;
    }

    return false;
}

/**
 * @brief: 生成makefile
 * @param: pBuildModule模块module
 * @param: sourceDir 源码目录全路径名
 * @param: sourceSubdir 子目录全路径名
 * @param: config 配置文件全路径名
 * @param: setupArgs 构建参数
 * @param: confopt 配置参数
 * @param: secopt 安全参数
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::genMakefile(const shared_ptr<KMBuilderModule> &pBuildModule, const int &type, const string &sourceDir, const string &sourceSubdir, const string &config, const vector<string> &confopt, const vector<string> &secopt, string &buildDir, vector<string> &setupArgs)
{
    if (config.empty())
        return true;
    if (!fs::exists(config))
        return true;

    g_autofree char *pContent = nullptr;
    if (!g_file_load_contents(g_file_new_for_path(config.c_str()), nullptr, &pContent, nullptr, nullptr, nullptr))
    {
        throw KMException("Failed to load file : " + config);
    }

    bool isRequireBuilddir = strstr(pContent, "buildapi-variable-require-builddir") != nullptr;
    bool isUseBuildir = isRequireBuilddir || pBuildModule->getData()->m_buildDir || (type == KMBuilderModule::BS_Meson);

    string configCmd, configArg, cmakeGenerator;
    if (!isUseBuildir)
    {
        buildDir = sourceSubdir;
        if (type == KMBuilderModule::BS_Cmake || type == KMBuilderModule::BS_Cmake_Ninja)
        {
            configCmd = "cmake";
            configArg = ".";
        }
        else if (type == KMBuilderModule::BS_Qmake)
        {
            configCmd = "qmake";
            configArg = fs::path(config).filename();
        }
        else
        {
            if (type == KMBuilderModule::BS_Meson)
            {
                assert(type != KMBuilderModule::BS_Meson);
            }
            configCmd = "./configure";
        }
    }
    else
    {
        buildDir = sourceSubdir + "/_kaiming_build";
        if (!fs::exists(buildDir) && !fs::create_directories(buildDir))
        {
            throw KMException("Failed to mkdir : " + buildDir);
        }

        if (!buildDir.empty())
        {
            for (size_t i = 0; i < setupArgs.size(); ++i)
            {
                if (setupArgs.at(i).compare(0, strlen("--build-dir="), "--build-dir=") == 0)
                {
                    string builddir = setupArgs.at(i) + string("/_kaiming_build");
                    setupArgs.at(i) = builddir;
                    break;
                }
            }
        }

        if (type == KMBuilderModule::BS_Cmake || type == KMBuilderModule::BS_Cmake_Ninja)
        {
            configCmd = "cmake";
            configArg = "..";
        }
        else if (type == KMBuilderModule::BS_Qmake)
        {
            configCmd = "qmake";
            configArg = string("../") + fs::path(config).filename().string();
        }
        else if (type == KMBuilderModule::BS_Meson)
        {
            configCmd = "meson";
            configArg = "..";
        }
        else
        {
            configCmd = "../configure";
        }
    }

    if (type == KMBuilderModule::BS_Cmake)
    {
        cmakeGenerator = "Unix Makefiles";
    }
    else if (type == KMBuilderModule::BS_Cmake_Ninja)
    {
        cmakeGenerator = "Ninja";
    }

    string prefix = getOptionsPrefix(pBuildModule->getBuildOptions());
    string libdir = getOptionsLibdir(pBuildModule->getBuildOptions());

    vector<string> configureArgs;
    if (type == KMBuilderModule::BS_Cmake || type == KMBuilderModule::BS_Cmake_Ninja)
    {
        configureArgs.emplace_back("-DCMAKE_INSTALL_PREFIX:PATH=" + prefix);
        if (!libdir.empty())
        {
            configureArgs.emplace_back("-DCMAKE_INSTALL_LIBDIR:PATH=" + libdir);
        }
        configureArgs.emplace_back("-G " + cmakeGenerator);
    }
    else if (type == KMBuilderModule::BS_Qmake)
    {
        configureArgs.emplace_back("PREFIX=" + prefix);
    }
    else
    {
        // autotools and meson 
        configureArgs.emplace_back("--prefix=" + prefix);
        if (!libdir.empty())
        {
            configureArgs.emplace_back("--libdir=" + libdir);
        }
    }

    configureArgs.emplace_back(configArg);

    vector<any> args;
    args.emplace_back(configCmd);
    args.emplace_back(string("strv"));
    args.emplace_back(configureArgs);
    args.emplace_back(string("strv"));
    args.emplace_back(confopt);
    args.emplace_back(string("secret"));
    args.emplace_back(secopt);

    if (!build(buildDir, setupArgs, args))
    {
        throw KMException("Failed to run : " + configCmd + " " + configArg);
    }

    return true;
}

/**
 * @brief: 构建
 * @param: setupArgs 构建参数
 * @param: args 构建参数
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::build(const string &cwdSubdir, const vector<string> &setupArgs, const vector<any> &args)
{
    const string skip = "skip";
    const string strv = "strv";
    const string secret = "secret";
    const string secretenv = "secret_env";
    vector<string> setupargs = setupArgs;
    for (int i = 0; i < args.size(); ++i)
    {
        string strArg;
        std::any arg = args[i];
        if (arg.type() == typeid(string))
            strArg = any_cast<string>(arg);
        if (strArg != secretenv)
            continue;
        arg = args[++i];
        if (arg.type() == typeid(vector<string>))
        {
            vector<string> argvs = any_cast<vector<string>>(arg);
            for (auto &argv : argvs)
            {
                const char *value = getenv(argv.c_str());
                if (value != nullptr)
                    setupargs.emplace_back("--env=" + argv + "=" + value);
            }
        }
    }

    for (int i = 0; i < args.size(); ++i)
    {
        string strArg;
        std::any arg = args[i];
        if (arg.type() == typeid(string))
            strArg = any_cast<string>(arg);
        else
            continue;
        if (strArg == strv)
        {
            arg = args[++i];
            if (arg.type() == typeid(vector<string>))
            {
                vector<string> argvs = any_cast<vector<string>>(arg);
                for (auto &argv : argvs)
                    setupargs.emplace_back(argv);
            }
        }
        else if (strArg == secret)
        {
            arg = args[++i];
            if (arg.type() == typeid(vector<string>))
            {
                vector<string> argvs = any_cast<vector<string>>(arg);
                for (auto &argv : argvs)
                {
                    int pos = argv.find("$");
                    if (pos == string::npos)
                        continue;
                    string key = argv.substr(0, pos);
                    string value = getenv(key.c_str());
                    if (!value.empty())
                        setupargs.emplace_back(key + "=" + value);
                }
            }
        }
        else if (strArg == secretenv)
            continue;
        else if (strArg != skip)
            setupargs.emplace_back(strArg);
    }

    if (!KMBuilderUtils::isInSanbox())
    {
        return KMBuilderUtils::spawn(cwdSubdir, 0, setupargs);
    }
    else
    {
        throw KMException("Not support build in sandbox");
    }

    return false;
}

/**
 * @brief: 构建编译参数
 * @param: appDir 应用目录全路径名
 * @param: cacheDir 缓存目录全路径名
 * @param: name 模块名称
 * @param: sourceDir 源码目录全路径名
 * @param: cwdSubdir 子目录全路径名
 * @param: opts 编译参数
 * @param: env 环境变量
 * @return: true-成功; false-失败
 */
vector<string> KMBuilderBuild::setupBuildArgs(const string &appDir, const string &ccacheDir, const string &name, const string &sourceDir, const string &cwdSubdir, const vector<string> &opts, const vector<string> &env)
{
    vector<string> args;
    args.emplace_back("kaiming");
    args.emplace_back("build");
    // TODO：将容器kaiming-box换成crun后，参数--die-with-parent应已无用，是否有替代参数？待定
    args.emplace_back("--die-with-parent");

    string buildDir = "/run/build/";
    if (getBuildRuntime())
    {
        buildDir = "/run/build-runtime/";
    }

    // TODO：将容器kaiming-box换成crun后，下面这种环境变量设置已经不再生效；只有原来kaiming-box这种定制容器才会使用KAIMING_BUILDER_BUILDDIR这类的环境变量；crun这种通用容器没有
    args.emplace_back("--env=KAIMING_BUILDER_BUILDDIR=" + buildDir + name);
    // TODO：权限设置的需要后面确定后才能确定替换方案
    args.emplace_back("--nofilesystem=host:reset");
    string realpathDir = realpath(sourceDir.c_str(), nullptr);
    args.emplace_back("--filesystem=" + realpathDir);

    if (realpathDir != sourceDir)
    {
        args.emplace_back("--bind-mount=" + sourceDir + "=" + realpathDir);
    }

    args.emplace_back("--bind-mount=" + buildDir + name + "=" + realpathDir);

    if (!cwdSubdir.empty())
    {
        args.emplace_back("--build-dir=" + buildDir + name + "/" + cwdSubdir);
    }
    else
    {
        args.emplace_back("--build-dir=" + buildDir + name);
    }

    args.emplace_back("--bind-mount=/run/ccache=" + ccacheDir);

    for (auto opt : opts)
    {
        args.emplace_back(opt);
    }

    for (auto e : env)
    {
        args.emplace_back("--env=" + e);
    }

    args.emplace_back(appDir);

    return args;
}

/**
 * @brief: 生成配置文件
 * @param: sourceDir 源码目录全路径名
 * @param: sourceSubdir 子目录全路径名
 * @param: config 配置文件全路径名
 * @param: setupArgs 构建参数
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::genConfigFile(const string &sourceDir, const string &sourceSubdir, const string &config, vector<string> setupArgs)
{
    if (config.empty())
        return true;
    if (fs::exists(config))
        return true;

    const vector<string> names = { "autogen", "autogen.sh", "bootstrap", "bootstrap.sh" };
    string autogen;
    for (auto name : names)
    {
        if (fs::exists(sourceDir + "/" + name))
        {
            autogen = "./" + name;
            break;
        }
    }

    if (autogen.empty())
    {
        throw KMException("Can't find autogen, autogen.sh, bootstrap.sh or bootstrap");
    }

    // TODO: 容器kaiming-box换成crun，环境变量NOCONFIGURE应已无用
    setupArgs.emplace_back("--env=NOCONFIGURE=1");

    vector<any> args;
    args.emplace_back(autogen);

    if (!build(sourceSubdir, setupArgs, args))
        return false;

    if (!fs::exists(config))
    {
        throw KMException("autogen did not create configure");
    }

    return true;
}

/**
 * @brief: 获取配置文件
 * @param: type 构建系统类型
 * @param: sourceSubdir 子目录全路径名
 * @param: config 配置文件全路径名
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::getConfigFilename(const shared_ptr<KMBuilderModule> &pBuildModule, const int &type, const string &sourceSubdir, string &config)
{
    switch (type)
    {
        case KMBuilderModule::BS_Unknow:
        {
            throw KMException("module : " + pBuildModule->getData()->m_name + ": Invalid buildsystem");
        }
        case KMBuilderModule::BS_Simple:
        {
            if (pBuildModule->getData()->m_buildCommands.empty())
            {
                throw KMException("module : " + pBuildModule->getData()->m_name + ": Buildsystem simple requires specifying \"build-commands\"");
            }
            break;
        }
        case KMBuilderModule::BS_Cmake:
        case KMBuilderModule::BS_Cmake_Ninja:
        {
            if (!fs::exists(sourceSubdir + "/CMakeLists.txt"))
            {
                throw KMException("module : " + pBuildModule->getData()->m_name + ": Can't find CMakeLists.txt");
            }
            config = sourceSubdir + "/CMakeLists.txt";
            break;
        }
        case KMBuilderModule::BS_Meson:
        {
            if (!fs::exists(sourceSubdir + "/meson.build"))
            {
                throw KMException("module : " + pBuildModule->getData()->m_name + ": Can't find meson.build");
            }
            config = sourceSubdir + "/meson.build";
            break;
        }
        case KMBuilderModule::BS_Qmake:
        {
            for (auto const &entry : fs::directory_iterator{ sourceSubdir })
            {
                if (!fs::is_regular_file(entry))
                    continue;

                fs::path p = entry.path();
                if (p.extension().string() != ".pro")
                    continue;

                config = p.string();
                break;
            }
            if (config.empty())
            {
                throw KMException("module : " + pBuildModule->getData()->m_name + ": Can't find *.pro file");
            }
            break;
        }
        case KMBuilderModule::BS_Autotools:
        {
            config = sourceSubdir + "/configure";
            if (!pBuildModule->getData()->m_rmConfigure)
                break;

            if (fs::exists(config) && !fs::remove(config))
                return false;

            break;
        }
        default:
            return false;
    }

    return true;
}

/**
 * @brief: 获取配置选项
 * @param: BuildOptions编译选项
 * @param: 返回的配置选项
 */
void KMBuilderBuild::getBuildConfigOpts(const shared_ptr<KMBuildOptions> &options, vector<string> &opts)
{
    if (!options)
    {
        return;
    }

    for (auto opt : options->m_configOpts)
    {
        opts.emplace_back(opt);
    }
}

/**
 * @brief: 获取秘密选项
 * @param: mapEnv环境变量
 * @param: 返回的配置选项
 */
void KMBuilderBuild::getBuildSecretOpts(const shared_ptr<KMBuildOptions> &options, vector<string> &opts)
{
    if (!options)
    {
        return;
    }

    for (auto opt : options->m_secretOpts)
    {
        opts.emplace_back(opt);
    }
}

/**
 * @brief: 获取编译参数
 * @param: options
 * @param: args编译参数
 */
void KMBuilderBuild::getBuildArgs(const shared_ptr<KMBuildOptions> &options, vector<string> &args)
{
    if (!options)
    {
        return;
    }

    for (const auto &arg : options->m_buildArgs)
    {
        args.emplace_back(arg);
    }

    // if (builderContext.getIsSandbox())
    // {
    //     if (!args.empty())
    //         args.clear();
    //     else
    //         args.emplace_back("--unshare=network");
    // }
}

/**
 * @brief : 处理环境变量中路径变量的前面插入和后面追加
 * @param : [in | out] envs，环境变量map
 * @param : [in] key, 路径类变量的key，如：PATH
 * @param : [in] prepend, 前面追加
 * @param : [in] append, 后面追加
 */ 
static void concatEnvironmentPath(std::map<std::string, std::string> &envs, const std::string &key, const std::string &prepend, const std::string &append)
{
    std::string value = envs[key];
    if (value.empty())
    {
        value = "$" + key;
    }
    
    if (!append.empty())
    {
        value.append(":" + append);
    }
    
    if (!prepend.empty())
    {
        value = prepend + ":" + value;
    }

    if (value != ("$" + key))
    {
        envs[key] = value;
    }
    else
    {
        envs.erase(key);
    }
}

/**
 * @brief: 获取环境变量
 * @param: KMBuildOptions
 * @param: env编译环境变量
 */
void KMBuilderBuild::getBuildEnv(const std::shared_ptr<KMBuildOptions> &options, std::vector<std::string> &env)
{
    std::map<std::string, std::string> envs = builderContext.manifestConfig()->m_envs;
    if (options != nullptr)
    {
        for (auto [k, v] : options->m_env)
        {
            envs[k] = v;
        }

        if (!options->m_cFlags.empty())
        {
            env.emplace_back("CFLAGS=" + options->m_cFlags);
        }
        if (!options->m_cppFlags.empty())
        {
            env.emplace_back("CPPFLAGS=" + options->m_cppFlags);
        }
        if (!options->m_cxxFlags.empty())
        {
            env.emplace_back("CXXFLAGS=" + options->m_cxxFlags);
        }
        if (!options->m_ldFlags.empty())
        {
            env.emplace_back("LDFLAGS=" + options->m_ldFlags);
        }

        concatEnvironmentPath(envs, "PATH", options->m_prependPath, options->m_appendPath);
        concatEnvironmentPath(envs, "LD_LIBRARY_PATH", options->m_prependLdLibraryPath, options->m_appendLdLibraryPath);
        concatEnvironmentPath(envs, "PKG_CONFIG_PATH", options->m_prependPkgConfigPath, options->m_appendPkgConfigPath);
    }

    std::string ccachedir = "/run/ccache/disabled";
    if (builderContext.isCcache())
    {
        ccachedir = "/run/ccache";
        std::string old = envs["PATH"];
        if (old.empty())
        {
            old = "$PATH";
        }

        std::string path = std::string("/run/ccache/bin:") + old;
        envs["PATH"] = path;
    }
    env.emplace_back("CCACHE_DIR=" + ccachedir);

    for (auto [k, v] : envs)
    {
        env.emplace_back(k + "=" + v);
    }

    // getExtenv(options, env);
}

/**
 * @brief: 获取额外的环境变量
 * @param: KMBuildOptions
 * @param: env编译环境变量
 */
void KMBuilderBuild::getExtenv(const shared_ptr<KMBuildOptions> &options, vector<string> &env)
{
    string path = "$PATH";
    string ccachedir = "/run/ccache/disabled";
    if (builderContext.isCcache())
    {
        ccachedir = "/run/ccache";
        path = string("/run/ccache/bin:") + path;
    }

    env.emplace_back("CCACHE_DIR=" + ccachedir);

    if (!options)
    {
        if (path != "$PATH")
        {
            env.emplace_back("PATH=" + path);
        }
        
        return;
    }

    string append = options->m_appendPath;
    if (!append.empty())
    {
        path.append(":" + append);
    }

    string prepend = options->m_prependPath;
    if (!prepend.empty())
    {
        // path.append(":" + prepend);
        path = prepend + ":" + path;
    }

    env.emplace_back("PATH=" + path);

    string ldpath = "$LD_LIBRARY_PATH";
    string appendLdPath = options->m_appendLdLibraryPath;
    if (!appendLdPath.empty())
    {
        ldpath.append(":" + appendLdPath);
    }

    string prependLdPath = options->m_prependLdLibraryPath;
    if (!prependLdPath.empty())
    {
        // ldpath.append(":" + prependLdPath);
        ldpath = prependLdPath + ":" + ldpath;
    }

    env.emplace_back("LD_LIBRARY_PATH=" + ldpath);

    string pkgconfigpath = "$PKG_CONFIG_PATH";
    string appendPkgConfig = options->m_appendPkgConfigPath;
    if (!appendPkgConfig.empty())
    {
        pkgconfigpath.append(":" + appendPkgConfig);
    }

    string preappendPkgConfig = options->m_prependPkgConfigPath;
    if (!preappendPkgConfig.empty())
    {
        // pkgconfigpath.append(":" + preappendPkgConfig);
        pkgconfigpath = preappendPkgConfig + ":" + pkgconfigpath;
    }

    env.emplace_back("PKG_CONFIG_PATH=" + pkgconfigpath);
}

/**
 * @brief: 获取加密环境变量
 * @param: BuildOptions编译选项
 * @param: env编译环境变量
 */
void KMBuilderBuild::getBuildSecretEnv(const shared_ptr<KMBuildOptions> &options, vector<string> &env)
{
    if (!options)
    {
        return;
    }

    for (auto [k, v] : options->m_secretEnv)
    {
        env.emplace_back(k + "=" + v);
    }
}

/**
 * @brief: 创建构建子目录
 * @param: [in], dir，构建目录
 * @param: [in], name，模块名
 * @param: [out], subdir，子目录全路径名
 * @return: true-成功; false-失败
 */
bool createSubdir(const std::string &dir, const std::string &name, std::string &subdir)
{
    if (!KMBuilderUtils::ensureDir(dir))
        return false;

    for (int i = 1; i < 1000; i++)
    {
        subdir = dir + "/" + name + "-" + std::to_string(i);
        if (fs::exists(subdir))
            continue;

        std::error_code ec;
        std::string link = dir + "/" + name;

        if (fs::exists(link))
            fs::remove(link);

        if (fs::create_directories(subdir) && KMBuilderUtils::createDirectorySymlink(subdir, link, ec))
            return true;
    }

    return false;
}

/**
 * @brief: 清理编译目录
 * @param: sourceLink，源码目录链接文件
 * @param: sourceDir，源码目录
 * @return: true-成功; false-失败
 */
bool KMBuilderBuild::cleanupBuildbdir(const string &sourceLink, const string &sourceDir)
{
    if (fs::exists(sourceLink))
    {
        fs::remove(sourceLink);
    }
    
    if (fs::exists(sourceDir))
    {
        return fs::remove_all(sourceDir);
    }

    return true;
}

bool download(const shared_ptr<KMBuilderModule> &pBuildModule, const string &sourcedir)
{
    for (auto source : pBuildModule->buildSources())
    {
        if (!source->isEnable())
        {
            continue ;
        }

        if (!source->downloadSource(sourcedir))
        {
            return false;
        }
    }
    
    return true;
}
