自定义 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 Studio
    enum 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.exe
	std::string clPath = vsPath + "\\VC\\Tools\\MSVC";
	std::string clExePath;

	// 遍历目录,寻找 cl.exe
	std::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 "";
	}

	// 读取 SharedInstallationPath
	if (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.exe
	std::string clPath = vsPath + "\\VC\\Tools\\MSVC";
	std::string clExePath;

	// 遍历目录,寻找 cl.exe
	std::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::string
		free(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); // 按分号分割 PATH
	std::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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值