网站加水印,做网站费用列入什么科目,网站建设的一些背景图片,做网站 pc端与手机端兼容在 C 项目中#xff0c;常常需要自动化地管理编译流程#xff0c;例如使用 MinGW 或 Visual Studio 编译器进行代码的编译和链接。为了方便管理不同编译器和简化编译流程#xff0c;我们开发了一个 CompilerManager 类#xff0c;用于抽象编译器的查找、命令生成以及执行。…在 C 项目中常常需要自动化地管理编译流程例如使用 MinGW 或 Visual Studio 编译器进行代码的编译和链接。为了方便管理不同编译器和简化编译流程我们开发了一个 CompilerManager 类用于抽象编译器的查找、命令生成以及执行。
本文将详细介绍这个编译管理器的设计、功能实现以及如何在实际项目中使用。 项目背景
在实际开发中跨平台编译器管理是一个复杂的问题不同的操作系统、不同的编译工具链都会对项目开发带来一定的复杂性。尤其是当一个项目需要支持 MinGW 和 Visual Studio 两种编译器时用户不仅需要手动配置环境还需要通过命令行正确调用编译命令。
为了解决上述问题本文设计了一个通用的编译管理器类 CompilerManager支持以下功能
自动检测编译器根据环境变量或预定义路径自动查找 MinGW 和 Visual Studio 的编译器路径。支持两种编译模式 MinGW直接调用 g完成源文件到可执行文件的编译。Visual Studio分离编译和链接阶段分别生成 .obj 文件和最终的可执行文件。 封装编译命令自动根据编译器生成正确的编译和链接命令。动态设置环境变量支持通过 _putenv 动态设置 PATH、INCLUDE 和 LIB 环境变量确保 Visual Studio 的命令行工具能够正确工作。执行命令捕获输出封装命令执行支持捕获输出和错误信息。 编译管理器的核心功能
编译管理器类设计
CompilerManager 类封装了编译器的查找、命令生成和执行逻辑。以下是它的核心成员变量和方法
#pragma once#include string
#include vector// 编译器管理类
class CompilerManager {
public:// 枚举类型支持 MinGW 和 Visual Studioenum CompilerType {MinGW,VisualStudio};// 构造函数CompilerManager();// 从注册表中读取路径std::string GetDebuggerPathFromRegistry(const std::string regKey, const std::string regValue);// 设置编译器类型void SetCompilerType(CompilerType type);// 自动查找编译器路径bool FindCompiler(std::string outputMessage);// 获取 C 编译器路径std::string GetCCompilerPath() const;// 获取 C 编译器路径std::string GetCppCompilerPath() const;// 获取编译器版本std::string GetCompilerVersion() const;// 编译单个文件bool CompileSingleFile(const std::string sourceFile, const std::string outputFile, std::string outputMessage);// 编译多个文件bool CompileMultipleFiles(const std::vectorstd::string sourceFiles, const std::string outputFile, std::string outputMessage);private:// 检查文件是否存在bool FileExists(const std::string path);// 查找 MinGW 编译器bool FindMinGW(std::string outputMessage);// 查找 Microsoft 调试器路径bool FindMicrosoftDebugger(std::string debuggerPath);// 查找 LLDB 调试器路径bool FindLLDBDebugger(std::string debuggerPath, std::string errorMessage);// 查找 Visual Studio 调试器路径bool FindVisualStudioLinker(const std::string vsPath, std::string linkerPath);// 查找 Visual Studio 的安装路径记录std::string FindVswhereFromRegistry();// 调用 vcvarsall.bat 初始化 Visual Studio 环境bool InitializeEnvironment(std::string outputMessage);// 查找 Visual Studio 编译器bool FindVisualStudio(std::string outputMessage);std::string GetEnvironmentVariableSafe(const std::string variableName);// 在 PATH 环境变量中查找可执行文件std::string FindInPath(const std::string executable);// 执行命令行命令std::string ExecuteCommand(const std::string command);// 构建编译命令std::string BuildCompileCommand(const std::vectorstd::string sourceFiles, const std::string outputFile, std::string outputMessage);// 构建链接命令std::string BuildLinkCommand(const std::vectorstd::string objectFiles, const std::string outputFile, std::string outputMessage);// 执行编译命令bool ExecuteCompileCommand(const std::string command, std::string outputMessage);private:CompilerType m_compilerType; // 当前选择的编译器类型std::string m_workspacePath; // 工作集路径std::string m_cCompilerPath; // C 编译器路径std::string m_cppCompilerPath; // C 编译器路径std::string m_linkerPath; // 链接器路径std::string m_buildToolPath; // 构建工具路径std::string m_debuggerPath; // 调试器路径std::string m_compilerVersion; // 编译器版本信息
};编译器路径查找
MinGW 查找通过 PATH 环境变量搜索 gcc.exe 和 g.exe。Visual Studio 查找通过调用 vswhere.exe 或解析注册表获取最新版本 Visual Studio 的安装路径并找到 cl.exe 和 link.exe。
示例代码如下
bool CompilerManager::FindMinGW(std::string outputMessage) {// 查找工具路径std::string gccPath FindInPath(gcc.exe);std::string gppPath FindInPath(g.exe);std::string gdbPath FindInPath(gdb.exe);std::string ldPath FindInPath(ld.exe);// 如果任何一个工具未找到则返回错误if (gccPath.empty() || gppPath.empty() || gdbPath.empty()) {outputMessage Error: MinGW tools not found in PATH.\n;if (gccPath.empty()) outputMessage C Compiler (gcc.exe) not found.\n;if (gppPath.empty()) outputMessage C Compiler (g.exe) not found.\n;if (gdbPath.empty()) outputMessage Debugger (gdb.exe) not found.\n;if (ldPath.empty()) outputMessage Linker (ld.exe) not found.\n;return false;}// 设置工具路径m_cCompilerPath gccPath;m_cppCompilerPath gppPath;m_debuggerPath gdbPath;m_linkerPath ldPath;// 验证工具是否在同一目录中std::size_t gccPos gccPath.find_last_of(\\/);std::size_t gppPos gppPath.find_last_of(\\/);std::size_t gdbPos gdbPath.find_last_of(\\/);std::size_t ldPos ldPath.find_last_of(\\/);if (gccPath.substr(0, gccPos) ! gppPath.substr(0, gppPos) ||gccPath.substr(0, gccPos) ! gdbPath.substr(0, gdbPos) ||gdbPath.substr(0, gdbPos) ! ldPath.substr(0, ldPos)) {m_workspacePath ;outputMessage Error: MinGW tools are not located in the same directory.\n;return false;}else {if (gccPos ! std::string::npos) {m_workspacePath gccPath.substr(0, gccPos);}}// 获取编译器版本信息m_compilerVersion ExecuteCommand(gccPath --version);if (m_compilerVersion.empty()) {outputMessage Error: Failed to retrieve MinGW compiler version.;return false;}// 输出成功信息outputMessage \n;outputMessage MinGW Tools Found\n;outputMessage \n;outputMessage C Compiler : m_cCompilerPath \n;outputMessage C Compiler : m_cppCompilerPath \n;outputMessage Debugger : m_debuggerPath \n;outputMessage Linker : m_linkerPath \n;outputMessage Workspace : m_workspacePath \n;outputMessage \nVersion:\n m_compilerVersion;return true;
}bool CompilerManager::FindVisualStudio(std::string outputMessage) {std::string vswherePath C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe;if (!FileExists(vswherePath)) {vswherePath FindVswhereFromRegistry();if (!FileExists(vswherePath)) {std::string vswherePath FindInPath(vswhere.exe);if (vswherePath.empty()) {outputMessage Error: vswhere.exe not found. Please ensure it is installed and accessible.;return false;}}}// 调用 vswhere.exe 获取 Visual Studio 的安装路径std::string vsPath ExecuteCommand(\ vswherePath \ -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath);if (vsPath.empty()) {outputMessage Error: Failed to locate Visual Studio installation.;return false;}// 去掉返回路径的末尾换行符if (!vsPath.empty() vsPath.back() \n) {vsPath.pop_back();}// 拼接路径到 cl.exestd::string clPath vsPath \\VC\\Tools\\MSVC;std::string clExePath;// 遍历目录寻找 cl.exestd::istringstream stream(ExecuteCommand(dir \ clPath \ /b /s));std::string line;while (std::getline(stream, line)) {if (line.find(cl.exe) ! std::string::npos) {clExePath line;break;}}if (clExePath.empty()) {outputMessage Error: Failed to find cl.exe in Visual Studio installation.;return false;}// 获取调试器路径优先查找 Microsoft 调试器std::string debuggerPath;if (!FindMicrosoftDebugger(debuggerPath)) {// 如果未找到 Microsoft 调试器尝试查找 LLDB 调试器if (!FindLLDBDebugger(debuggerPath, outputMessage)) {outputMessage Error: Failed to find a compatible debugger (cdb.exe or lldb.exe).;return false;}}// 获取链接器路径std::string linkerPath;if (!FindVisualStudioLinker(vsPath, linkerPath)) {outputMessage Error: Failed to find link.exe in Visual Studio installation.;return false;}m_workspacePath vsPath;m_cCompilerPath clExePath;m_cppCompilerPath clExePath;m_linkerPath linkerPath;m_debuggerPath debuggerPath;if (!InitializeEnvironment(outputMessage)) {return false;}m_compilerVersion ExecuteCommand(\ m_cCompilerPath \ /?);if (m_compilerVersion.empty()) {outputMessage Error: Failed to retrieve Visual Studio compiler version.;return false;}// 输出成功信息outputMessage \n;outputMessage Visual Studio compilers found:\n;outputMessage \n;outputMessage C Compiler : m_cCompilerPath \n;outputMessage C Compiler : m_cppCompilerPath \n;outputMessage Debugger : m_debuggerPath \n;outputMessage Linker : m_linkerPath \n;outputMessage Workspace : m_workspacePath \n;outputMessage \nVersion:\n m_compilerVersion;return true;
}构建编译命令
BuildCompileCommand 方法根据不同编译器生成正确的编译命令
MinGWg source.cpp -o output.exeVisual Studiocl.exe /EHsc /Iinclude_path source.cpp /Fooutput.obj /Zi使用方法
下面是如何在项目中使用 CompilerManager 类
CompilerManager manager;// 设置编译器类型为 MinGW 或 Visual Studio
manager.SetCompilerType(CompilerManager::MinGW);
// manager.SetCompilerType(CompilerManager::VisualStudio);// 查找编译器
std::string outputMessage;
if (manager.FindCompiler(outputMessage)) {std::cout Compiler found successfully! std::endl;std::cout outputMessage std::endl;
} else {std::cerr Failed to find compiler. std::endl;std::cerr outputMessage std::endl;
}// 编译单个文件
std::string sourceFile D:\WorkCode\Demo\MyTest\main.cpp;
std::string outputFile D:\WorkCode\Demo\MyTest\main.exe;if (manager.CompileSingleFile(sourceFile, outputFile, outputMessage)) {std::cout Compilation successful! std::endl;
} else {std::cerr Compilation failed! std::endl;std::cerr outputMessage std::endl;
}存在的问题与改进计划
目前 CompilerManager 的核心功能已实现但在 Visual Studio 的使用中仍存在问题
编译错误在 Visual Studio 中编译时输出错误 STL1001: Unexpected compiler version。 原因可能是环境变量未正确设置或者调用了错误版本的 cl.exe。改进计划进一步优化 _putenv 的动态环境配置确保正确的编译器版本被调用。 分离编译和链接对于多文件项目尚未完全实现分离编译生成 .obj 文件与链接生成可执行文件的功能。 总结
通过 CompilerManager 类我们能够简化项目的编译管理流程并支持自动化调用 MinGW 和 Visual Studio 的编译器。虽然目前 Visual Studio 的支持仍需进一步完善但这个管理器已经为多编译器支持提供了一个良好的基础。
在这篇文章中我们介绍了如何使用 CompilerManager 类管理代码编译。这个工具类通过调用现有编译器如 MinGW 和 Visual Studio来完成源代码的编译任务而不是重新实现一个编译器。通过对编译器路径的检测和环境变量的初始化CompilerManager 提供了一种方便的方式来管理不同编译器的使用。
完整代码CompilerManager.cpp
#include pch.h
#include CompilerManager.h
#include sstream
#include cstdlib
#include iostream
#include fstream
#include cstdio
#include algorithm// 构造函数
CompilerManager::CompilerManager() : m_compilerType(MinGW) {}// 从注册表中读取路径
std::string CompilerManager::GetDebuggerPathFromRegistry(const std::string regKey, const std::string regValue) {HKEY hKey;char value[512];DWORD valueLength sizeof(value);// 打开注册表键if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, regKey.c_str(), 0, KEY_READ, hKey) ! ERROR_SUCCESS) {return ;}// 查询注册表值if (RegQueryValueExA(hKey, regValue.c_str(), nullptr, nullptr, (LPBYTE)value, valueLength) ERROR_SUCCESS) {RegCloseKey(hKey);return std::string(value);}RegCloseKey(hKey);return ;
}// 设置编译器类型
void CompilerManager::SetCompilerType(CompilerType type) {m_compilerType type;
}// 自动查找编译器路径
bool CompilerManager::FindCompiler(std::string outputMessage) {if (m_compilerType MinGW) {return FindMinGW(outputMessage);}if (m_compilerType VisualStudio) {return FindVisualStudio(outputMessage);}outputMessage Error: Unknown compiler type.;return false;
}// 获取 C 编译器路径
std::string CompilerManager::GetCCompilerPath() const {return m_cCompilerPath;
}// 获取 C 编译器路径
std::string CompilerManager::GetCppCompilerPath() const {return m_cppCompilerPath;
}// 获取编译器版本
std::string CompilerManager::GetCompilerVersion() const {return m_compilerVersion;
}// 编译单个文件
bool CompilerManager::CompileSingleFile(const std::string sourceFile, const std::string outputFile, std::string outputMessage) {// 检查源文件是否存在if (!FileExists(sourceFile)) {outputMessage Error: Source file not found: sourceFile;return false;}// 构建编译命令std::vectorstd::string sourceFiles { sourceFile };std::string compileCommand BuildCompileCommand(sourceFiles, outputFile, outputMessage);if (compileCommand.empty()) {outputMessage Error: Failed to build compile command.;return false;}std::cout CompileCommand Output: compileCommand std::endl;// 执行编译命令return ExecuteCompileCommand(compileCommand, outputMessage);
}// 编译多个文件
bool CompilerManager::CompileMultipleFiles(const std::vectorstd::string sourceFiles, const std::string outputFile, std::string outputMessage) {// 检查每个源文件是否存在for (const auto file : sourceFiles) {if (!FileExists(file)) {outputMessage Error: Source file not found: file;return false;}}// 构建编译命令std::string compileCommand BuildCompileCommand(sourceFiles, outputFile, outputMessage);if (compileCommand.empty()) {outputMessage Error: Failed to build compile command.;return false;}// 执行编译命令return ExecuteCompileCommand(compileCommand, outputMessage);
}// 检查文件是否存在
bool CompilerManager::FileExists(const std::string path) {std::ifstream file(path);return file.good();
}// 查找 MinGW 编译器
bool CompilerManager::FindMinGW(std::string outputMessage) {// 查找工具路径std::string gccPath FindInPath(gcc.exe);std::string gppPath FindInPath(g.exe);std::string gdbPath FindInPath(gdb.exe);std::string ldPath FindInPath(ld.exe);// 如果任何一个工具未找到则返回错误if (gccPath.empty() || gppPath.empty() || gdbPath.empty()) {outputMessage Error: MinGW tools not found in PATH.\n;if (gccPath.empty()) outputMessage C Compiler (gcc.exe) not found.\n;if (gppPath.empty()) outputMessage C Compiler (g.exe) not found.\n;if (gdbPath.empty()) outputMessage Debugger (gdb.exe) not found.\n;if (ldPath.empty()) outputMessage Linker (ld.exe) not found.\n;return false;}// 设置工具路径m_cCompilerPath gccPath;m_cppCompilerPath gppPath;m_debuggerPath gdbPath;m_linkerPath ldPath;// 验证工具是否在同一目录中std::size_t gccPos gccPath.find_last_of(\\/);std::size_t gppPos gppPath.find_last_of(\\/);std::size_t gdbPos gdbPath.find_last_of(\\/);std::size_t ldPos ldPath.find_last_of(\\/);if (gccPath.substr(0, gccPos) ! gppPath.substr(0, gppPos) ||gccPath.substr(0, gccPos) ! gdbPath.substr(0, gdbPos) ||gdbPath.substr(0, gdbPos) ! ldPath.substr(0, ldPos)) {m_workspacePath ;outputMessage Error: MinGW tools are not located in the same directory.\n;return false;}else {if (gccPos ! std::string::npos) {m_workspacePath gccPath.substr(0, gccPos);}}// 获取编译器版本信息m_compilerVersion ExecuteCommand(gccPath --version);if (m_compilerVersion.empty()) {outputMessage Error: Failed to retrieve MinGW compiler version.;return false;}// 输出成功信息outputMessage \n;outputMessage MinGW Tools Found\n;outputMessage \n;outputMessage C Compiler : m_cCompilerPath \n;outputMessage C Compiler : m_cppCompilerPath \n;outputMessage Debugger : m_debuggerPath \n;outputMessage Linker : m_linkerPath \n;outputMessage Workspace : m_workspacePath \n;outputMessage \nVersion:\n m_compilerVersion;return true;
}// 查找 Microsoft 调试器路径
bool CompilerManager::FindMicrosoftDebugger(std::string debuggerPath) {// 从注册表获取 Windows SDK 根目录std::vectorstd::pairstd::string, std::string registryKeys {{SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots, KitsRoot10}, // Windows 10 SDK{SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots, KitsRoot81}, // Windows 8.1 SDK{SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots, KitsRoot} // Windows 8 SDK};for (const auto item : registryKeys) {std::string sdkRoot GetDebuggerPathFromRegistry(item.first, item.second);if (!sdkRoot.empty()) {// 构造调试器路径std::vectorstd::string potentialPaths {sdkRoot Debuggers\\x64\\cdb.exe,sdkRoot Debuggers\\x86\\cdb.exe};// 检查是否存在for (const auto path : potentialPaths) {if (FileExists(path)) {debuggerPath path;return true;}}}}debuggerPath ;return false; // 未找到调试器
}// 查找 LLDB 调试器路径
bool CompilerManager::FindLLDBDebugger(std::string debuggerPath, std::string errorMessage) {// 从环境变量获取 LLVM 安装路径std::string llvmPath GetEnvironmentVariableSafe(LLVM_DIR);if (!llvmPath.empty()) {std::string lldbPath llvmPath \\bin\\lldb.exe;if (FileExists(lldbPath)) {debuggerPath lldbPath;return true;}else {errorMessage Error: LLDB not found in LLVM_DIR ( llvmPath ).;}}else {errorMessage Error: LLVM_DIR environment variable is not set.;}// 默认路径检查std::vectorstd::string defaultPaths {C:\\Program Files\\LLVM\\bin\\lldb.exe,C:\\Program Files (x86)\\LLVM\\bin\\lldb.exe};for (const auto path : defaultPaths) {if (FileExists(path)) {debuggerPath path;return true;}}errorMessage LLDB not found in default installation paths.;debuggerPath ;return false;
}bool CompilerManager::FindVisualStudioLinker(const std::string vsPath, std::string linkerPath) {std::string clPath vsPath \\VC\\Tools\\MSVC;std::istringstream stream(ExecuteCommand(dir \ clPath \ /b /s));std::string line;while (std::getline(stream, line)) {if (line.find(link.exe) ! std::string::npos) {linkerPath line;return true; // 找到链接器}}linkerPath ;return false; // 未找到链接器
}std::string CompilerManager::FindVswhereFromRegistry() {HKEY hKey;char value[512];DWORD valueLength sizeof(value);// 打开注册表路径if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,SOFTWARE\\Microsoft\\VisualStudio\\Setup,0,KEY_READ,hKey) ! ERROR_SUCCESS) {return ;}// 读取 SharedInstallationPathif (RegQueryValueExA(hKey, SharedInstallationPath, nullptr, nullptr, (LPBYTE)value, valueLength) ERROR_SUCCESS) {RegCloseKey(hKey);return std::string(value) \\vswhere.exe;}RegCloseKey(hKey);return ;
}// 调用 vcvarsall.bat 初始化 Visual Studio 环境
bool CompilerManager::InitializeEnvironment(std::string outputMessage) {// 定义临时文件路径std::string tempFilePath vcvars_output.txt;// 构造命令将 vcvarsall.bat 执行结果和环境变量输出到文件std::string vcvarsCommand cmd /c \\ m_workspacePath \\VC\\Auxiliary\\Build\\vcvarsall.bat\ x64 set tempFilePath \;// 执行命令int commandResult system(vcvarsCommand.c_str());if (commandResult ! 0) {outputMessage Error: Failed to execute vcvarsall.bat.;return false;}// 打开临时文件std::ifstream inputFile(tempFilePath);if (!inputFile.is_open()) {outputMessage Error: Failed to open temporary output file.;return false;}// 逐行解析文件内容std::string line;bool includeSet false, libSet false, pathSet false;while (std::getline(inputFile, line)) {size_t equalsPos line.find();if (equalsPos ! std::string::npos) {std::string key line.substr(0, equalsPos);std::string value line.substr(equalsPos 1);// 设置环境变量到当前进程_putenv_s(key.c_str(), value.c_str());// 将 key 转换为大写std::string upperKey key;std::transform(upperKey.begin(), upperKey.end(), upperKey.begin(), ::toupper);// 检查关键变量是否设置if (upperKey INCLUDE) includeSet true;if (upperKey LIB) libSet true;if (upperKey PATH) pathSet true;}}// 关闭文件并删除临时文件inputFile.close();remove(tempFilePath.c_str());// 验证关键变量是否设置成功if (!includeSet || !libSet || !pathSet) {outputMessage Error: INCLUDE, LIB, or PATH not set.;return false;}outputMessage Environment initialized successfully.;return true;
}// 查找 Visual Studio 编译器
bool CompilerManager::FindVisualStudio(std::string outputMessage) {std::string vswherePath C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe;if (!FileExists(vswherePath)) {vswherePath FindVswhereFromRegistry();if (!FileExists(vswherePath)) {std::string vswherePath FindInPath(vswhere.exe);if (vswherePath.empty()) {outputMessage Error: vswhere.exe not found. Please ensure it is installed and accessible.;return false;}}}// 调用 vswhere.exe 获取 Visual Studio 的安装路径std::string vsPath ExecuteCommand(\ vswherePath \ -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath);if (vsPath.empty()) {outputMessage Error: Failed to locate Visual Studio installation.;return false;}// 去掉返回路径的末尾换行符if (!vsPath.empty() vsPath.back() \n) {vsPath.pop_back();}// 拼接路径到 cl.exestd::string clPath vsPath \\VC\\Tools\\MSVC;std::string clExePath;// 遍历目录寻找 cl.exestd::istringstream stream(ExecuteCommand(dir \ clPath \ /b /s));std::string line;while (std::getline(stream, line)) {if (line.find(cl.exe) ! std::string::npos) {clExePath line;break;}}if (clExePath.empty()) {outputMessage Error: Failed to find cl.exe in Visual Studio installation.;return false;}// 获取调试器路径优先查找 Microsoft 调试器std::string debuggerPath;if (!FindMicrosoftDebugger(debuggerPath)) {// 如果未找到 Microsoft 调试器尝试查找 LLDB 调试器if (!FindLLDBDebugger(debuggerPath, outputMessage)) {outputMessage Error: Failed to find a compatible debugger (cdb.exe or lldb.exe).;return false;}}// 获取链接器路径std::string linkerPath;if (!FindVisualStudioLinker(vsPath, linkerPath)) {outputMessage Error: Failed to find link.exe in Visual Studio installation.;return false;}m_workspacePath vsPath;m_cCompilerPath clExePath;m_cppCompilerPath clExePath;m_linkerPath linkerPath;m_debuggerPath debuggerPath;if (!InitializeEnvironment(outputMessage)) {return false;}m_compilerVersion ExecuteCommand(\ m_cCompilerPath \ /?);if (m_compilerVersion.empty()) {outputMessage Error: Failed to retrieve Visual Studio compiler version.;return false;}// 输出成功信息outputMessage \n;outputMessage Visual Studio compilers found:\n;outputMessage \n;outputMessage C Compiler : m_cCompilerPath \n;outputMessage C Compiler : m_cppCompilerPath \n;outputMessage Debugger : m_debuggerPath \n;outputMessage Linker : m_linkerPath \n;outputMessage Workspace : m_workspacePath \n;outputMessage \nVersion:\n m_compilerVersion;return true;
}std::string CompilerManager::GetEnvironmentVariableSafe(const std::string variableName) {char* buffer nullptr;size_t size 0;// 使用 _dupenv_s 获取环境变量值if (_dupenv_s(buffer, size, variableName.c_str()) 0 buffer ! nullptr) {std::string value(buffer); // 转换为 std::stringfree(buffer); // 释放分配的内存return value;}return ; // 如果获取失败则返回空字符串
}// 从 PATH 环境变量中查找可执行文件
std::string CompilerManager::FindInPath(const std::string executable) {// 获取 PATH 环境变量值std::string path GetEnvironmentVariableSafe(PATH);if (path.empty()) {return ; // 如果 PATH 为空直接返回}std::istringstream stream(path); // 按分号分割 PATHstd::string directory;std::string fullPath;while (std::getline(stream, directory, ;)) {fullPath directory \\ executable; // 拼接目录和文件名if (FileExists(fullPath)) { // 如果找到可执行文件return fullPath;}}return ; // 如果未找到返回空字符串
}// 执行命令并返回输出
std::string CompilerManager::ExecuteCommand(const std::string command) {char buffer[128];std::string result;FILE* pipe _popen((command 21).c_str(), r); // 将错误输出重定向到标准输出if (!pipe) {return ;}while (fgets(buffer, sizeof(buffer), pipe) ! nullptr) {result buffer;}_pclose(pipe);return result;
}// 构建编译命令
std::string CompilerManager::BuildCompileCommand(const std::vectorstd::string sourceFiles, const std::string outputFile, std::string outputMessage) {std::string command;// 检查输入是否有效if (sourceFiles.empty()) {outputMessage Error: No source files provided for compilation.;return ;}if (outputFile.empty()) {outputMessage Error: No output file specified.;return ;}if (m_compilerType MinGW) {// MinGW 编译命令command \ m_cppCompilerPath \; // gfor (const auto file : sourceFiles) {command \ file \;}command -o \ outputFile \;}else if (m_compilerType VisualStudio) {// Visual Studio 编译命令command \ m_cCompilerPath \; // cl.exe// 添加 C 异常处理选项command /EHsc;// 从环境变量获取 INCLUDE 路径并添加到命令std::string includePaths GetEnvironmentVariableSafe(INCLUDE);if (!includePaths.empty()) {std::istringstream stream(includePaths);std::string path;while (std::getline(stream, path, ;)) {if (!path.empty()) {command /I\ path \;}}}else {outputMessage Warning: INCLUDE environment variable is empty. Compilation may fail.\n;}// 添加源文件路径并为每个源文件生成对应的 .obj 文件for (const auto file : sourceFiles) {std::string objFile file;size_t pos objFile.find_last_of(.);if (pos ! std::string::npos) {objFile objFile.substr(0, pos) .obj; // 替换后缀为 .obj}else {objFile .obj; // 默认添加 .obj 后缀}command \ file \ /Fo\ objFile \;}// 添加调试信息可选command /Zi;}else {outputMessage Error: Unsupported compiler type.;return ;}return command;
}std::string CompilerManager::BuildLinkCommand(const std::vectorstd::string objectFiles, const std::string outputFile, std::string outputMessage) {std::string command;if (m_compilerType MinGW) {// MinGW 链接命令command \ m_cppCompilerPath \; // 使用 gfor (const auto objFile : objectFiles) {command \ objFile \; // 添加对象文件}command -o \ outputFile \; // 指定输出文件}else if (m_compilerType VisualStudio) {// Visual Studio 链接命令command \ m_linkerPath \; // 使用 link.exe// 添加输入对象文件if (objectFiles.empty()) {outputMessage Error: No object files provided for linking.;return ;}for (const auto objFile : objectFiles) {command \ objFile \;}// 添加输出文件路径if (outputFile.empty()) {outputMessage Error: No output file specified.;return ;}command /OUT:\ outputFile \;// 从环境变量获取 LIB 路径并添加到命令std::string libPaths GetEnvironmentVariableSafe(LIB);if (!libPaths.empty()) {std::istringstream stream(libPaths);std::string path;while (std::getline(stream, path, ;)) {if (!path.empty()) {command /LIBPATH:\ path \;}}}else {outputMessage Warning: LIB environment variable is empty. Linking may fail.;}// 添加默认库文件例如常用的 runtime 库command kernel32.lib user32.lib gdi32.lib;}return command;
}// 执行编译命令
bool CompilerManager::ExecuteCompileCommand(const std::string command, std::string outputMessage) {STARTUPINFOA si;PROCESS_INFORMATION pi;ZeroMemory(si, sizeof(si));si.cb sizeof(si);ZeroMemory(pi, sizeof(pi));// 创建一个缓冲区用于捕获输出HANDLE hReadPipe, hWritePipe;SECURITY_ATTRIBUTES sa { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };if (!CreatePipe(hReadPipe, hWritePipe, sa, 0)) {outputMessage Failed to create pipe.;return false;}// 重定向子进程的输出si.dwFlags | STARTF_USESTDHANDLES;si.hStdOutput hWritePipe;si.hStdError hWritePipe;// 执行命令if (!CreateProcessA(NULL,(LPSTR)command.c_str(),NULL,NULL,TRUE,0,NULL,NULL,si,pi)) {outputMessage Failed to execute command.;CloseHandle(hReadPipe);CloseHandle(hWritePipe);return false;}// 关闭写入端句柄CloseHandle(hWritePipe);// 从管道中读取输出CHAR buffer[128];DWORD bytesRead;outputMessage.clear();while (ReadFile(hReadPipe, buffer, sizeof(buffer) - 1, bytesRead, NULL)) {buffer[bytesRead] \0; // 确保缓冲区以 NULL 结尾outputMessage buffer;}// 等待子进程结束WaitForSingleObject(pi.hProcess, INFINITE);// 获取退出码DWORD exitCode;GetExitCodeProcess(pi.hProcess, exitCode);// 关闭句柄CloseHandle(hReadPipe);CloseHandle(pi.hProcess);CloseHandle(pi.hThread);return exitCode 0;
}