当前位置: 首页 > news >正文

自定义 C++ 编译器的调用与管理

在 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::vector<std::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::vector<std::string>& sourceFiles, const std::string& outputFile, std::string& outputMessage);// 构建链接命令std::string BuildLinkCommand(const std::vector<std::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;      // 编译器版本信息
};

编译器路径查找

  1. MinGW 查找:通过 PATH 环境变量搜索 gcc.exeg++.exe
  2. Visual Studio 查找:通过调用 vswhere.exe 或解析注册表,获取最新版本 Visual Studio 的安装路径,并找到 cl.exelink.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 方法根据不同编译器生成正确的编译命令:

  • MinGW
    g++ source.cpp -o output.exe
    
  • Visual Studio
    cl.exe /EHsc /I<include_path> source.cpp /Fo<output.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 的使用中仍存在问题:

  1. 编译错误:在 Visual Studio 中,编译时输出错误 STL1001: Unexpected compiler version
    • 原因:可能是环境变量未正确设置,或者调用了错误版本的 cl.exe
    • 改进计划:进一步优化 _putenv 的动态环境配置,确保正确的编译器版本被调用。
  2. 分离编译和链接:对于多文件项目,尚未完全实现分离编译(生成 .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::vector<std::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::vector<std::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::vector<std::pair<std::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::vector<std::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::vector<std::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 + " 2>&1").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::vector<std::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 + "\""; // g++for (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::vector<std::string>& objectFiles, const std::string& outputFile, std::string& outputMessage) {std::string command;if (m_compilerType == MinGW) {// MinGW 链接命令command = "\"" + m_cppCompilerPath + "\""; // 使用 g++for (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;
}
http://www.lryc.cn/news/507350.html

相关文章:

  • 时间管理系统|Java|SSM|JSP|
  • 用SparkSQL和PySpark完成按时间字段顺序将字符串字段中的值组合在一起分组显示
  • Sentinel 学习笔记3-责任链与工作流程
  • Latex 转换为 Word(使用GrindEQ )(英文转中文,毕业论文)
  • 使用Chat-LangChain模块创建一个与用户交流的机器人
  • 国家认可的人工智能从业人员证书如何报考?
  • 【网络云计算】2024第51周-每日【2024/12/17】小测-理论-解析
  • 每日十题八股-2024年12月19日
  • 网络方案设计
  • 学习记录:electron主进程与渲染进程直接的通信示例【开箱即用】
  • 【Java数据结构】ArrayList类
  • HDR视频技术之十:MPEG 及 VCEG 的 HDR 编码优化
  • 71 mysql 中 insert into ... on duplicate key update ... 的实现
  • 计算机网络-GRE Over IPSec实验
  • 你的第一个博客-第一弹
  • 若依启动项目时配置为 HTTPS 协议
  • 学习思考:一日三问(学习篇)之匹配VLAN
  • [WiFi] WiFi 802.1x介绍及EAP认证流程整理
  • 用C#(.NET8)开发一个NTP(SNTP)服务
  • Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
  • ABAP SQL 取日期+时间最新的一条数据
  • 【Rust自学】4.3. 所有权与函数
  • 【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
  • 用docker快速安装电子白板Excalidraw绘制流程图
  • 使用Turtle库实现,鼠标左键绘制路径,用鼠标右键结束绘制,小海龟并沿路径移动
  • 人工智能入门是先看西瓜书还是先看花书?
  • winform中屏蔽双击最大化或最小化窗体(C#实现),禁用任务管理器结束程序,在需要屏蔽双击窗体最大化、最小化、关闭
  • 进程内存转储工具|内存镜像提取-取证工具
  • 数据结构day5:单向循环链表 代码作业
  • (OCPP服务器)SteVe编译搭建全过程