【跨平台修复】 - cmakegenerator.cpp: CMAKE_C_COMPILER / CMAKE_MAKE_PROGRAM 提前到 project() 之前设置,避免 CMake 在 Windows 回退 NMake - cmakegenerator.cpp: MinGW 编译器路径下自动推导 mingw32-make.exe - mainwindow.cpp: generateCMake() 在 Windows 下添加 -G 'MinGW Makefiles' - mainwindow.cpp: compileProject() cmake 添加生成器参数 & mingw32-make 替代 make 【UI 改进】 - 全部操作按钮升级为 Material Design 高对比度配色(Darker 系列) - 按钮添加圆角和内边距,提升可读性和视觉层次 【工程规范】 - 新增 .gitignore,排除编译产物 (.o / moc_*.cpp / Makefile / 二进制) - README.md 重写,补充 Windows 编译指南和技术要点 - 需求规格说明书更新至 v1.1
317 lines
11 KiB
C++
317 lines
11 KiB
C++
#include "cmakegenerator.h"
|
||
#include <QFile>
|
||
#include <QFileInfo>
|
||
#include <QTextStream>
|
||
#include <QDir>
|
||
#include <QDateTime>
|
||
|
||
CMakeGenerator::CMakeGenerator(QObject *parent)
|
||
: QObject(parent)
|
||
{
|
||
}
|
||
|
||
bool CMakeGenerator::generate(const ProjectConfig *config, const QString &outputPath)
|
||
{
|
||
if (!config) {
|
||
emit errorOccurred("配置对象为空");
|
||
return false;
|
||
}
|
||
|
||
QString content = generateContent(config);
|
||
|
||
QFileInfo fileInfo(outputPath);
|
||
QDir dir = fileInfo.absoluteDir();
|
||
if (!dir.exists()) {
|
||
dir.mkpath(".");
|
||
}
|
||
|
||
QFile file(outputPath);
|
||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||
emit errorOccurred("无法创建 CMakeLists.txt: " + file.errorString());
|
||
return false;
|
||
}
|
||
|
||
QTextStream out(&file);
|
||
out.setCodec("UTF-8");
|
||
out << content;
|
||
file.close();
|
||
|
||
m_lastGeneratedPath = outputPath;
|
||
emit cmakeGenerated(outputPath);
|
||
|
||
return true;
|
||
}
|
||
|
||
QString CMakeGenerator::getLastGeneratedPath() const
|
||
{
|
||
return m_lastGeneratedPath;
|
||
}
|
||
|
||
QString CMakeGenerator::generateContent(const ProjectConfig *config)
|
||
{
|
||
QString content;
|
||
|
||
content += generateHeader(config);
|
||
content += "\n";
|
||
content += generateProjectConfig(config);
|
||
content += "\n";
|
||
content += generateSourceFiles(config);
|
||
content += "\n";
|
||
content += generateIncludeDirs(config);
|
||
content += "\n";
|
||
content += generateLibraries(config);
|
||
content += "\n";
|
||
content += generateDefines(config);
|
||
content += "\n";
|
||
content += generateCompilerOptions(config);
|
||
content += "\n";
|
||
content += generateBuildTargets(config);
|
||
|
||
return content;
|
||
}
|
||
|
||
QString CMakeGenerator::generateHeader(const ProjectConfig *config)
|
||
{
|
||
QString header;
|
||
header += "# ========================================\n";
|
||
header += "# CMakeLists.txt - VoiletCStudio 自动生成\n";
|
||
header += "# 工程名称:" + config->getProjectName() + "\n";
|
||
header += "# 生成时间:" + QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss") + "\n";
|
||
header += "# 跨平台支持:Windows (MinGW) / Linux (GCC) / macOS (Clang)\n";
|
||
header += "# ========================================\n\n";
|
||
|
||
header += "cmake_minimum_required(VERSION 3.10)\n\n";
|
||
|
||
return header;
|
||
}
|
||
|
||
QString CMakeGenerator::generateProjectConfig(const ProjectConfig *config)
|
||
{
|
||
QString content;
|
||
|
||
QString projectName = config->getProjectName();
|
||
if (projectName.isEmpty()) {
|
||
projectName = "UntitledProject";
|
||
}
|
||
|
||
// ===== 编译器必须在 project() 之前设置,否则 CMake 会默认用 NMake =====
|
||
content += "# ========================================\n";
|
||
content += "# 编译器配置(必须在 project() 之前)\n";
|
||
content += "# ========================================\n\n";
|
||
|
||
QString compilerPath = config->getCompilerPath();
|
||
QString assemblerPath = config->getAssemblerPath();
|
||
QString linkerPath = config->getLinkerPath();
|
||
|
||
if (!compilerPath.isEmpty() && compilerPath != "gcc") {
|
||
content += "set(CMAKE_C_COMPILER \"" + compilerPath + "\")\n";
|
||
|
||
// 如果编译器是 MinGW 路径,自动推导 make 程序目录
|
||
// 例如:C:/msys2/mingw64/bin/gcc.exe → C:/msys2/mingw64/bin/mingw32-make.exe
|
||
if (compilerPath.contains("mingw", Qt::CaseInsensitive)) {
|
||
QString makeDir = QFileInfo(compilerPath).path(); // path() 正确保留绝对路径的盘符
|
||
content += "set(CMAKE_MAKE_PROGRAM \"" + makeDir + "/mingw32-make.exe\")\n";
|
||
}
|
||
}
|
||
|
||
if (!assemblerPath.isEmpty() && assemblerPath != "gcc") {
|
||
content += "set(CMAKE_ASM_COMPILER \"" + assemblerPath + "\")\n";
|
||
}
|
||
|
||
if (!linkerPath.isEmpty() && linkerPath != "gcc" && linkerPath != compilerPath) {
|
||
content += "set(CMAKE_C_LINK_EXECUTABLE \"" + linkerPath + " <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>\")\n";
|
||
}
|
||
content += "\n";
|
||
|
||
content += "# 项目名称(编译器已设置,不再尝试检测)\n";
|
||
content += "project(" + projectName + " C)\n\n";
|
||
|
||
content += "# C 标准\n";
|
||
content += "set(CMAKE_C_STANDARD 11)\n";
|
||
content += "set(CMAKE_C_STANDARD_REQUIRED ON)\n\n";
|
||
|
||
// 输出目录配置
|
||
QString outputDir = config->getOutputDir();
|
||
if (outputDir.isEmpty()) {
|
||
outputDir = "./build";
|
||
}
|
||
|
||
content += "# 输出目录配置\n";
|
||
content += "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/" + outputDir + ")\n";
|
||
content += "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/" + outputDir + ")\n";
|
||
content += "set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/" + outputDir + ")\n\n";
|
||
|
||
return content;
|
||
}
|
||
|
||
QString CMakeGenerator::generateSourceFiles(const ProjectConfig *config)
|
||
{
|
||
QString content;
|
||
content += "# ========================================\n";
|
||
content += "# 源文件配置(虚拟目录结构)\n";
|
||
content += "# ========================================\n\n";
|
||
|
||
QMap<QString, VirtualDir> virtualDirs = config->getVirtualDirs();
|
||
|
||
if (virtualDirs.isEmpty()) {
|
||
content += "# 无源文件\n";
|
||
} else {
|
||
// 遍历虚拟目录
|
||
for (auto it = virtualDirs.begin(); it != virtualDirs.end(); ++it) {
|
||
content += "# 分组:" + it.key() + "\n";
|
||
QString safeName = it.key();
|
||
safeName.replace(" ", "_");
|
||
safeName.replace("/", "_");
|
||
content += "set(SRCS_" + safeName + "\n";
|
||
for (const QString &file : it->files) {
|
||
content += " " + file + "\n";
|
||
}
|
||
content += ")\n\n";
|
||
}
|
||
|
||
// 合并所有源文件
|
||
content += "# 合并所有源文件\n";
|
||
content += "set(SOURCES\n";
|
||
for (auto it = virtualDirs.begin(); it != virtualDirs.end(); ++it) {
|
||
for (const QString &file : it->files) {
|
||
content += " " + file + "\n";
|
||
}
|
||
}
|
||
content += ")\n\n";
|
||
}
|
||
|
||
content += "# 创建可执行文件\n";
|
||
QString outputName = config->getProjectName();
|
||
if (outputName.isEmpty()) {
|
||
outputName = "output";
|
||
}
|
||
|
||
content += "add_executable(" + outputName + " ${SOURCES})\n\n";
|
||
|
||
return content;
|
||
}
|
||
|
||
QString CMakeGenerator::generateIncludeDirs(const ProjectConfig *config)
|
||
{
|
||
QString content;
|
||
content += "# 包含目录\n";
|
||
|
||
QStringList includeDirs = config->getIncludeDirs();
|
||
if (includeDirs.isEmpty()) {
|
||
content += "# 无额外包含目录\n";
|
||
} else {
|
||
content += "target_include_directories(" + config->getProjectName() + " PRIVATE\n";
|
||
for (const QString &dir : includeDirs) {
|
||
content += " " + dir + "\n";
|
||
}
|
||
content += ")\n";
|
||
}
|
||
|
||
return content;
|
||
}
|
||
|
||
QString CMakeGenerator::generateLibraries(const ProjectConfig *config)
|
||
{
|
||
QString content;
|
||
content += "# 链接库\n";
|
||
|
||
QStringList libraries = config->getLibraries();
|
||
if (libraries.isEmpty()) {
|
||
content += "# 无外部库文件\n";
|
||
} else {
|
||
content += "target_link_libraries(" + config->getProjectName() + " PRIVATE\n";
|
||
for (const QString &lib : libraries) {
|
||
content += " " + lib + "\n";
|
||
}
|
||
content += ")\n";
|
||
}
|
||
|
||
return content;
|
||
}
|
||
|
||
QString CMakeGenerator::generateDefines(const ProjectConfig *config)
|
||
{
|
||
QString content;
|
||
content += "# 预编译宏定义\n";
|
||
|
||
QStringList defines = config->getDefines();
|
||
if (defines.isEmpty()) {
|
||
content += "# 无额外宏定义\n";
|
||
} else {
|
||
content += "target_compile_definitions(" + config->getProjectName() + " PRIVATE\n";
|
||
for (const QString &def : defines) {
|
||
content += " " + def + "\n";
|
||
}
|
||
content += ")\n";
|
||
}
|
||
|
||
return content;
|
||
}
|
||
|
||
QString CMakeGenerator::generateCompilerOptions(const ProjectConfig *config)
|
||
{
|
||
QString content;
|
||
content += "# 自定义编译选项\n";
|
||
|
||
QStringList options = config->getCompilerOptions();
|
||
if (options.isEmpty()) {
|
||
content += "# 无自定义编译选项\n";
|
||
} else {
|
||
content += "target_compile_options(" + config->getProjectName() + " PRIVATE\n";
|
||
for (const QString &opt : options) {
|
||
content += " " + opt + "\n";
|
||
}
|
||
content += ")\n";
|
||
}
|
||
|
||
return content;
|
||
}
|
||
|
||
QString CMakeGenerator::generateBuildTargets(const ProjectConfig * /*config*/)
|
||
{
|
||
QString content;
|
||
|
||
content += "# ========================================\n";
|
||
content += "# 构建类型配置\n";
|
||
content += "# ========================================\n\n";
|
||
|
||
content += "# 默认构建类型:Debug\n";
|
||
content += "if(NOT CMAKE_BUILD_TYPE)\n";
|
||
content += " set(CMAKE_BUILD_TYPE Debug CACHE STRING \"Build type\" FORCE)\n";
|
||
content += "endif()\n\n";
|
||
|
||
content += "# Debug 模式配置(自动添加 DEBUG 宏)\n";
|
||
content += "set(CMAKE_C_FLAGS_DEBUG \"-g -O0 -DDEBUG\" CACHE STRING \"Debug flags\" FORCE)\n\n";
|
||
|
||
content += "# Release 模式配置\n";
|
||
content += "set(CMAKE_C_FLAGS_RELEASE \"-O2 -DNDEBUG\" CACHE STRING \"Release flags\" FORCE)\n\n";
|
||
|
||
content += "# ========================================\n";
|
||
content += "# 自定义 Make 目标:make debug / make release\n";
|
||
content += "# ========================================\n\n";
|
||
|
||
content += "# make debug - 编译 Debug 版本(自动添加 DEBUG 宏)\n";
|
||
content += "add_custom_target(debug\n";
|
||
content += " COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${CMAKE_SOURCE_DIR}\n";
|
||
content += " COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config Debug\n";
|
||
content += " COMMENT \"Building Debug version\"\n";
|
||
content += " VERBATIM\n";
|
||
content += ")\n\n";
|
||
|
||
content += "# make release - 编译 Release 版本\n";
|
||
content += "add_custom_target(release\n";
|
||
content += " COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Release ${CMAKE_SOURCE_DIR}\n";
|
||
content += " COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config Release\n";
|
||
content += " COMMENT \"Building Release version\"\n";
|
||
content += " VERBATIM\n";
|
||
content += ")\n\n";
|
||
|
||
content += "# 使用说明:\n";
|
||
content += "# cmake -B build # 配置(默认 Debug)\n";
|
||
content += "# cmake --build build # 编译(使用当前 CMAKE_BUILD_TYPE)\n";
|
||
content += "# make debug # 编译 Debug 版本(自动添加 DEBUG 宏)\n";
|
||
content += "# make release # 编译 Release 版本\n";
|
||
|
||
return content;
|
||
}
|