Files
VoiletCStudio/src/cmakegenerator.cpp
虾哥 1f7328174b v1.1: 修复 Windows 跨平台兼容性 & 按钮高对比度配色
【跨平台修复】
- 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
2026-04-28 18:26:10 +08:00

317 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}