【Qt】Qt 构建系统详解:qmake 入门到项目实战
Qt 构建系统详解:qmake 入门到项目实战
本文将系统介绍 Qt 构建工具
qmake
的用法,并通过一个完整的项目结构示例,帮助你掌握.pro
文件编写、子项目管理、模块依赖等核心技能。
🧭 一、什么是 qmake?
qmake
是 Qt 提供的跨平台构建系统工具,它能将 .pro
文件(项目描述)转换为对应平台的构建脚本,如 Makefile 或 Visual Studio 工程文件。
✅ 主要作用:
- 自动生成构建脚本,简化项目编译流程
- 跨平台(支持 Windows / Linux / macOS)
- 适用于中小型 Qt 项目,快速上手
🔁 常见使用流程:
qmake myapp.pro # 生成 Makefile
make # 或 mingw32-make / nmake
📘 二、.pro
文件基础语法
1. 基本结构
TEMPLATE = app # 类型:app 或 lib
TARGET = myapp # 输出文件名
QT += core gui # 使用的 Qt 模块
CONFIG += c++11 # 编译配置
SOURCES += main.cpp # 源码文件
HEADERS += mainwindow.h
FORMS += mainwindow.ui # UI 文件
RESOURCES += resources.qrc
2. 常用变量
变量名 | 含义 |
---|---|
QT | 指定使用的 Qt 模块(如 core , gui ) |
CONFIG | 构建配置,如 debug , release , c++11 |
SOURCES | 源文件列表 |
HEADERS | 头文件列表 |
FORMS | Qt Designer 设计的 .ui 文件 |
RESOURCES | Qt 资源文件 .qrc |
INCLUDEPATH | 额外的头文件搜索路径 |
LIBS | 链接外部库,如 -L 和 -l |
DEFINES | 添加宏定义 |
3. 条件语句(平台判断)
unix {LIBS += -lm
}
win32 {CONFIG += console
}
🧩 三、子项目管理(SUBDIRS)
大型项目通常包含多个模块,比如主程序和自定义库。我们可以使用 subdirs
模板组织它们。
🛠️ 四、完整项目结构实战
📁 项目结构
MyProject/
├── MyProject.pro # 顶层项目文件
├── mylib/ # 自定义库
│ ├── mylib.pro
│ ├── mylib.h
│ └── mylib.cpp
└── mainapp/ # 主程序├── mainapp.pro├── main.cpp├── mainwindow.h├── mainwindow.cpp└── mainwindow.ui
🧷 1. 顶层文件 MyProject.pro
TEMPLATE = subdirs
SUBDIRS += mylib mainappmainapp.depends = mylib # 指定构建顺序
🧷 2. mylib/mylib.pro
(静态库)
TEMPLATE = lib
CONFIG += staticlib
TARGET = mylibHEADERS += mylib.h
SOURCES += mylib.cpp
mylib.h
#ifndef MYLIB_H
#define MYLIB_H#include <QString>class MyLib
{
public:static QString getMessage();
};#endif // MYLIB_H
mylib.cpp
#include "mylib.h"QString MyLib::getMessage()
{return "Hello from MyLib!";
}
🧷 3. mainapp/mainapp.pro
(主程序)
TEMPLATE = app
QT += core gui widgets
CONFIG += c++11
TARGET = mainappSOURCES += main.cpp \mainwindow.cpp
HEADERS += mainwindow.h
FORMS += mainwindow.uiINCLUDEPATH += ../mylib
LIBS += -L../mylib -lmylib
main.cpp
#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication app(argc, argv);MainWindow w;w.show();return app.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>class QLabel;class MainWindow : public QMainWindow
{Q_OBJECT
public:MainWindow(QWidget *parent = nullptr);~MainWindow();
private:QLabel *label;
};#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QLabel>
#include "mylib.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{label = new QLabel(MyLib::getMessage(), this);setCentralWidget(label);
}MainWindow::~MainWindow() {}
mainwindow.ui
你可以使用 Qt Designer 可视化编辑此文件,也可以先跳过(不影响构建)。
🔨 五、构建步骤(命令行)
在项目根目录执行:
qmake MyProject.pro
make # Windows 可用 mingw32-make 或 nmake
🎯 六、qmake 与 CMake 对比
特性 | qmake | CMake(Qt 6 推荐) |
---|---|---|
学习曲线 | 简单 | 较复杂 |
项目规模 | 适合中小型项目 | 更适合大型项目 |
官方支持 | Qt 5 推荐使用 | Qt 6 起默认使用 CMake |
功能扩展性 | 限制多一些 | 更强(模块化、测试集成等) |
✅ 总结
qmake
是 Qt 提供的构建系统,简洁、易上手,是管理 Qt 项目的传统利器。- 熟练使用
.pro
文件可以大大提升项目维护效率。 - 对于大型项目,建议使用
subdirs
管理模块。 - Qt 6 官方已推荐使用 CMake,新项目可考虑迁移。
📎 附加建议
- 可以借助 Qt Creator 编辑
.pro
文件并一键构建,开发体验更佳。 - 如果你正在使用 CMake,也可以对照迁移这套结构,后续我也可以提供 CMake 版本的实现。
📘提问环节
❓TEMPLATE = subdirs 什么意思?
TEMPLATE = subdirs
是 Qt 的构建工具 qmake 中的一种特殊模板,表示:
这个
.pro
文件不是一个可执行程序或库,而是一个“项目集合”,用于组织和管理多个子项目(子模块)的构建。
🧩 它的用途是什么?
当你要管理多个 .pro
项目(比如主程序 + 自定义库),就可以用一个 subdirs
类型的 .pro
文件统一管理它们的构建顺序和依赖关系。
📁 举个例子
项目结构:
MyProject/
├── MyProject.pro ← 这是 subdirs 项目
├── mylib/
│ └── mylib.pro
└── mainapp/└── mainapp.pro
MyProject.pro
内容:
TEMPLATE = subdirs # 告诉 qmake:这是一个子项目集合
SUBDIRS += mylib mainapp # 包含两个子项目mainapp.depends = mylib # 设置依赖顺序(先构建 mylib,再构建 mainapp)
🧠 工作机制
qmake MyProject.pro
会读取所有列出的子项目并为它们分别生成 Makefile。- 构建时会 自动递归编译每一个子项目。
- 通过
xxx.depends = yyy
设置依赖顺序(例如主程序依赖库)。
✅ 常见 TEMPLATE 类型对比
TEMPLATE 类型 | 用途 |
---|---|
app | 构建一个可执行程序 |
lib | 构建一个静态或动态库(.a/.so/.lib/.dll) |
subdirs | 管理多个 .pro 项目的集合 |
📌 总结
TEMPLATE = subdirs
通常出现在项目的顶层,用于组织多个子项目。- 它本身不包含代码,只是构建配置的集合。
- 与 CMake 中的
add_subdirectory()
有相似之处。
❓为什么顶层 .pro
文件中 没有 TARGET
?
这是一个非常关键且容易混淆的问题,下面我来系统解释,因为:
当你使用
TEMPLATE = subdirs
时,顶层.pro
文件本身不是一个编译目标,不会生成可执行文件或库文件,因此不需要也不能设置TARGET
。
✅ 解释:
TEMPLATE = subdirs
表示:这个.pro
文件只是一个项目容器,用来组织和管理子项目的构建流程。- 它的职责是告诉
qmake
:我要构建哪些子项目,它们的依赖关系是什么。 - 子项目才是真正参与构建(生成目标文件)的单元。
🧱 对比不同 TEMPLATE
TEMPLATE 类型 | 是否需要 TARGET | 说明 |
---|---|---|
app | ✅ 是 | 表示要生成一个可执行文件,需要指定名称 |
lib | ✅ 是 | 表示要生成一个静态库或动态库 |
subdirs | ❌ 否 | 不生成实际构建产物,仅组织子项目 |
📁 示例补充
# MyProject.pro
TEMPLATE = subdirs
SUBDIRS += mylib mainapp
mainapp.depends = mylib
你看到这里没有:
TARGET = ...
原因是:MyProject.pro 不是一个“要编译的项目”,而是“告诉 qmake 去编译哪些项目”。
✅ 小结
subdirs
类型的.pro
文件不需要TARGET
。- 它只是一个组织者,控制构建顺序和包含的模块。
- 只有真正的可执行程序或库项目,才需要指定
TARGET
。
❓子项目中为啥TARGET的名称要和文件夹同名?
这是一个非常好的问题,涉及到 qmake
中的 TARGET
变量含义 与 模块组织逻辑,我们来仔细说明:
✅ TARGET
的本质含义
在 .pro
文件中:
TARGET = something
表示 生成的可执行文件名或库文件名,不直接等于目录名。
🔎 示例对比说明
设想我们有如下结构:
MyProject/
├── mylib/
│ ├── mylib.pro ← 这里 TARGET = mylib
├── mainapp/
│ ├── mainapp.pro ← 这里 TARGET = mainapp
虽然子项目文件夹分别叫做 mylib
和 mainapp
,但你可以自由设置 TARGET
为任何你想要的名字,比如:
# mylib.pro
TARGET = my_custom_library_name# mainapp.pro
TARGET = awesome_gui_app
构建完成后:
- 在 Linux:会生成
libmy_custom_library_name.a
(静态库)和awesome_gui_app
(可执行文件) - 在 Windows:生成
my_custom_library_name.lib
和awesome_gui_app.exe
🤔 那为什么很多人让 TARGET = 文件夹名
?
这是因为:
- 文件夹名和
TARGET
保持一致可以 减少认知负担 - 例如
mylib/
→TARGET = mylib
,容易管理产物、日志和代码 - 并非语法规定,而是 一种约定俗成的习惯
📌 总结
项 | 含义 |
---|---|
TARGET | 表示“构建产物的名称”(可执行文件或库名) |
子目录名 | 与 TARGET 可以相同,也可以不同 |
建议做法 | 建议保持一致,利于组织与维护 |