qmake 是 Qt 提供的跨平台构建工具,用于自动化生成 Makefile、项目文件以及在不同平台上的构建系统文件。以下是关于 qmake 的详细介绍:
基本概念
qmake 是 Qt 工具链中的核心组件之一,它能够:
-
根据
.pro文件生成 Makefile -
管理项目中的源文件、头文件和库依赖
-
支持跨平台开发(Windows、Linux、macOS等)
-
简化 Qt 项目的构建过程
qmake 项目文件 (.pro)
qmake 使用 .pro 文件来描述项目配置,基本结构如下:
# 注释以 # 开头
TEMPLATE = app # 项目类型:app(应用程序)、lib(库)、subdirs(子目录)
TARGET = myapp # 生成的目标名称
QT += core gui # 需要的 Qt 模块
# 源文件
SOURCES += main.cpp \
widget.cpp
# 头文件
HEADERS += widget.h
# 资源文件
RESOURCES += resources.qrc
# 配置选项
CONFIG += c++11 warn_on
常用变量和函数
基本变量
-
TEMPLATE: 项目模板 (app, lib, subdirs) -
TARGET: 输出文件名 -
QT: 需要的 Qt 模块 (core, gui, network 等) -
DEFINES: 预处理器宏定义 -
INCLUDEPATH: 头文件搜索路径 -
LIBS: 链接的库文件
文件相关变量
-
SOURCES: C++ 源文件 -
HEADERS: 头文件 -
FORMS: UI 文件 (.ui) -
RESOURCES: 资源文件 (.qrc) -
TRANSLATIONS: 翻译文件 (.ts)
常用函数
-
message(): 输出调试信息 -
include(): 包含其他 .pro 文件 -
exists(): 检查文件是否存在 -
isEmpty(): 检查变量是否为空
条件判断
qmake 支持平台相关的条件判断:
win32 {
# Windows 平台特有配置
LIBS += -luser32
}
unix {
# Unix-like 平台特有配置
LIBS += -L/usr/local/lib
}
macx {
# macOS 平台特有配置
ICON = myapp.icns
}
高级特性
子项目管理
对于大型项目,可以使用 subdirs 模板:
TEMPLATE = subdirs
SUBDIRS = lib core gui
生成 Makefile
使用 qmake 生成 Makefile 的基本命令:
qmake -o Makefile project.pro
自定义构建步骤
# 自定义目标
custom.target = custom
custom.commands = custom_script.sh
QMAKE_EXTRA_TARGETS += custom
# 构建后步骤
win32: QMAKE_POST_LINK = copy $$OUT_PWD/debug/$${TARGET}.exe $$OUT_PWD
qmake vs CMake
虽然 qmake 是 Qt 原生的构建系统,但现在 Qt 也推荐使用 CMake:
-
qmake 优点:简单易用,与 Qt 深度集成
-
CMake 优点:更强大,更灵活,被更广泛的 C++ 项目使用
实际示例
一个完整的 Qt Widgets 应用程序的 .pro 文件示例:
# 项目类型和目标名称
TEMPLATE = app
TARGET = MyQtApp
VERSION = 1.0.0
# 需要的 Qt 模块
QT += core gui widgets
# 大于 Qt 5 时还需要 widgets 模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
# 配置选项
CONFIG += c++11 warn_on
CONFIG += debug_and_release
CONFIG += precompile_header
PRECOMPILED_HEADER = stdafx.h
# 源文件和头文件
SOURCES += main.cpp \
mainwindow.cpp \
dialog.cpp
HEADERS += mainwindow.h \
dialog.h
FORMS += mainwindow.ui \
dialog.ui
# 资源文件
RESOURCES += images.qrc
# 平台特定配置
win32 {
RC_ICONS = app.ico
DEFINES += WIN32
}
macx {
ICON = app.icns
QMAKE_INFO_PLIST = Info.plist
}
总结
qmake 是 Qt 开发中的重要工具,掌握它可以:
-
高效管理 Qt 项目结构
-
简化跨平台构建过程
-
自定义构建步骤
-
管理项目依赖和配置
虽然现代 Qt 项目越来越多地转向 CMake,但了解 qmake 仍然对维护旧项目和理解 Qt 构建系统有很大帮助。
subdirs 多级子工程管理
在大型 Qt 项目中,使用多级子工程是一种常见的组织方式。qmake 通过 subdirs 模板支持这种结构,下面详细介绍如何配置和管理多级子工程。
基本概念
多级子工程允许您:
-
将大型项目分解为多个逻辑模块
-
每个模块可以独立编译
-
支持模块间的依赖关系
-
便于团队协作开发
项目结构示例
典型的项目目录结构:
MyProject/
├── MyProject.pro # 主项目文件
├── app/ # 应用程序目录
│ ├── app.pro
│ └── ...
├── libcore/ # 核心库
│ ├── libcore.pro
│ └── ...
├── libgui/ # GUI库
│ ├── libgui.pro
│ └── ...
└── thirdparty/ # 第三方库
├── thirdparty.pro
└── ...
主项目文件配置
主 .pro 文件通常使用 subdirs 模板:
# MyProject.pro
TEMPLATE = subdirs
# 定义子项目目录(按构建顺序排列)
SUBDIRS = \
thirdparty \
libcore \
libgui \
app
# 可选:设置子项目间的依赖关系
libcore.depends = thirdparty
libgui.depends = libcore
app.depends = libgui libcore
# 配置所有子项目共用的设置
CONFIG += ordered # 确保按顺序构建
CONFIG += debug_and_release
子项目配置示例
静态库子项目
# libcore/libcore.pro TEMPLATE = lib CONFIG += staticlib QT += core TARGET = CoreLib # 生成的库名称 DESTDIR = $$PWD/../build # 输出目录 HEADERS += coreclass.h SOURCES += coreclass.cpp
动态库子项目
# libgui/libgui.pro TEMPLATE = lib CONFIG += shared QT += core gui widgets TARGET = GuiLib DESTDIR = $$PWD/../build HEADERS += guiclass.h SOURCES += guiclass.cpp # 依赖libcore LIBS += -L$$DESTDIR -lCoreLib DEPENDPATH += $$PWD/../libcore INCLUDEPATH += $$PWD/../libcore
应用程序子项目
# app/app.pro TEMPLATE = app QT += core gui widgets TARGET = MyApplication DESTDIR = $$PWD/../bin HEADERS += mainwindow.h SOURCES += main.cpp mainwindow.cpp FORMS += mainwindow.ui # 依赖其他库 LIBS += -L$$PWD/../build -lCoreLib -lGuiLib DEPENDPATH += $$PWD/../libcore $$PWD/../libgui INCLUDEPATH += $$PWD/../libcore $$PWD/../libgui
高级配置技巧
1. 共享配置
创建 common.pri 文件存放共享配置:
# common.pri
# 公共编译选项
CONFIG += c++14 warn_on
# 公共包含路径
INCLUDEPATH += $$PWD/include
# 公共库路径
unix {
LIBS += -L/usr/local/lib
}
win32 {
LIBS += -L"C:/Program Files/MyLibs"
}
在子项目中包含:
include(../common.pri)
2. 条件编译子项目
# MyProject.pro
SUBDIRS = thirdparty libcore
# 只在启用GUI时编译libgui和app
contains(CONFIG, with_gui) {
SUBDIRS += libgui app
}
3. 自定义构建顺序
# 显式指定依赖关系 libgui.depends = libcore app.depends = libgui # 或者使用ordered配置 CONFIG += ordered
4. 输出目录统一管理
# MyProject.pro
BUILD_DIR = $$PWD/build
BIN_DIR = $$PWD/bin
for(subdir, SUBDIRS) {
# 为每个子项目设置输出目录
eval($${subdir}.DESTDIR = $${BUILD_DIR})
}
# 应用程序输出到bin目录
app.DESTDIR = $$BIN_DIR
实际构建流程
-
生成 Makefile:
qmake -r # -r 表示递归处理子项目
-
构建所有子项目:
make # 或 nmake/ninja 取决于生成器
-
构建特定子项目:
make app # 只构建app及其依赖
常见问题解决
1. 循环依赖问题
如果子项目间存在循环依赖,qmake 会报错。解决方法:
-
重构代码消除循环依赖
-
将公共部分提取到新模块
2. 路径问题
建议使用 qmake 提供的路径变量:
-
$$PWD- 当前项目文件所在目录 -
$$OUT_PWD- 输出目录 -
$$_PRO_FILE_PWD_- 当前处理的 pro 文件所在目录
3. 跨平台问题
处理平台差异:
win32 {
LIBS += -luser32
}
unix:!macx {
LIBS += -lpthread
}
macx {
LIBS += -framework Cocoa
}
总结
qmake 的多级子工程管理为大型 Qt 项目提供了良好的组织结构:
-
使用
subdirs模板作为主项目 -
明确定义子项目间的依赖关系
-
共享公共配置减少重复
-
合理管理输出目录
-
注意处理平台差异和路径问题
这种结构特别适合模块化开发,可以让团队成员专注于各自模块,同时保持项目的整体一致性。
qmake 中的 $$PWD 变量详解
$$PWD 是 qmake 提供的一个重要内置变量,用于获取当前正在处理的 .pro 或 .pri 文件所在的绝对路径。
基本含义
-
PWD:Print Working Directory 的缩写
-
$$PWD:表示当前项目文件所在的完整目录路径
-
特点:
-
始终是绝对路径(从根目录开始的完整路径)
-
自动处理不同操作系统的路径分隔符(Windows 用
\,Unix-like 用/) -
在包含文件时(
.pri文件),$$PWD会相应变化为被包含文件所在的目录
-
典型用法
1. 引用当前目录下的文件
SOURCES += $$PWD/main.cpp HEADERS += $$PWD/mainwindow.h
2. 构建相对路径
# 引用上级目录的文件 LIBS += -L$$PWD/../libs # 引用子目录的文件 RESOURCES += $$PWD/resources/images.qrc
3. 在包含文件中使用
# 在 /path/to/config.pri 中
message("This file is located at: $$PWD")
# 包含后输出:This file is located at: /path/to
4. 定义项目根目录
# 通常在顶级 .pro 文件中定义 PROJECT_ROOT = $$PWD # 然后在子项目或 .pri 文件中使用 INCLUDEPATH += $$PROJECT_ROOT/include
与其他路径变量的比较
| 变量 | 含义 | 是否绝对路径 | 是否随包含变化 |
|---|---|---|---|
$$PWD | 当前文件所在目录 | 是 | 是 |
$$_PRO_FILE_PWD_ | 当前处理的 .pro 文件目录 | 是 | 否 |
$$OUT_PWD | 输出目录(Makefile 生成位置) | 是 | - |
$$shell_path() | 转换为系统原生路径格式 | - | - |
实际应用示例
示例1:跨平台资源路径
# 在不同平台下正确引用资源
win32 {
ICON = $$PWD/images/app.ico
}
macx {
ICON = $$PWD/images/app.icns
}
示例2:子项目引用父项目资源
project/ ├── common.pri ├── libs/ │ └── mylib.pro └── app.pro
# libs/mylib.pro include($$PWD/../common.pri) # 正确引用上级目录的公共配置
示例3:多级目录结构
# 定义模块路径 MODULE_A_DIR = $$PWD/modules/module_a MODULE_B_DIR = $$PWD/modules/module_b # 包含子模块 include($$MODULE_A_DIR/module_a.pri) include($$MODULE_B_DIR/module_b.pri)
常见问题解答
Q1: $$PWD 和 ./ 有什么区别?
-
$$PWD始终是绝对路径 -
./是相对路径,基于当前工作目录(可能不同于项目文件目录)
Q2: 在包含的 .pri 文件中 $$PWD 指向哪里?
-
指向被包含的
.pri文件所在目录,而不是主.pro文件目录
Q3: 如何获取主项目文件目录?
-
使用
$$_PRO_FILE_PWD_变量
Q4: Windows 路径中的反斜杠问题?
-
qmake 会自动处理路径分隔符,但可以使用
$$shell_path()确保兼容性:WIN_PATH = $$shell_path($$PWD)
实践建议
-
优先使用
$$PWD而非相对路径:避免因工作目录变化导致的问题 -
在顶层定义项目根变量:
PROJECT_ROOT = $$PWD
-
路径拼接时注意斜杠:
# 正确 INCLUDEPATH += $$PWD/include # 错误(多了一个斜杠) INCLUDEPATH += $$PWD//include
-
调试路径时使用 message():
message("Current path: $$PWD")
总结
$$PWD 是 qmake 项目配置中最常用、最重要的路径变量,正确理解和使用它可以:
-
避免路径相关的构建错误
-
创建更可移植的项目配置
-
简化多目录项目的管理
-
提高构建脚本的可维护性
在编写复杂的多级项目时,结合使用 $$PWD、$$_PRO_FILE_PWD_ 和自定义路径变量,可以创建出清晰可靠的构建系统。
qtCopy 详解
这个 qtCopy 是一个自定义的 qmake 函数,用于在不同平台(主要是 Windows)下复制文件和目录。下面我将详细解析这个函数的实现和用法。
函数定义
qcopy.pri中定义
qmake
defineTest(qtCopy) {
source = $$system_path($$1)
target = $$system_path($$2)
win32:system(xcopy $$source $$target /S /H /D /F /Y)
return(true)
}
组成部分
-
函数定义:
qmake
defineTest(qtCopy) { ... }-
defineTest用于定义一个可测试的函数 -
qtCopy是函数名
-
-
参数处理:
-
$$1表示第一个参数(源路径) -
$$2表示第二个参数(目标路径)
-
-
路径转换:
qmake
source = $$system_path($$1) target = $$system_path($$2)
-
$$system_path()将路径转换为系统原生格式(Windows 用反斜杠)
-
-
执行复制命令:
win32:system(xcopy $$source $$target /S /H /D /F /Y)
-
只在 Windows 平台执行
-
使用
xcopy命令进行复制 -
参数说明:
-
/S复制目录和子目录(空目录除外) -
/H复制隐藏和系统文件 -
/D只复制源比目标新的文件 -
/F显示完整的源和目标文件名 -
/Y禁止提示确认覆盖
-
-
-
返回值:
return(true)
-
总是返回 true,表示函数执行成功
-
使用方法
基本用法
qmake
qtCopy($$PWD/config, $$OUT_PWD/config)
实际应用场景
-
部署资源文件:
# 将资源目录复制到构建输出目录 qtCopy($$PWD/resources, $$OUT_PWD/resources)
-
安装第三方库:
# 将第三方库复制到项目lib目录 qtCopy($$THIRDPARTY_DIR/libs, $$PWD/libs)
-
构建后操作:
# 构建完成后复制生成的文件 build_all.target = build_all build_all.depends = all build_all.commands = $$QMAKE_QMAKE) -r && qtCopy($$OUT_PWD/release, $$DESTDIR) QMAKE_EXTRA_TARGETS += build_all
跨平台扩展
当前实现仅支持 Windows,可以扩展为跨平台版本:
qmake
defineTest(qtCopy) {
source = $$system_path($$1)
target = $$system_path($$2)
win32 {
system(xcopy $$source $$target /S /H /D /F /Y)
} else {
unix {
system(cp -Rf $$source $$target)
}
}
return(true)
}
高级用法
1. 条件复制
qmake
# 只在调试模式下复制调试资源
contains(CONFIG, debug) {
qtCopy($$PWD/debug_resources, $$OUT_PWD/resources)
}
2. 复制前检查目录存在
qmake
defineTest(safeQtCopy) {
source = $$1
target = $$2
exists($$source) {
qtCopy($$source, $$target)
} else {
message("Source path $$source does not exist - skipping copy")
}
return(true)
}
3. 与 QMAKE_POST_LINK 结合
# 链接完成后自动复制文件
win32 {
QMAKE_POST_LINK = $$QMAKE_POST_LINK) && qtCopy($$OUT_PWD/*.dll, $$DESTDIR)
}
注意事项
-
路径分隔符:
-
在 qmake 中使用
/作为路径分隔符 -
$$system_path()会自动转换为平台特定的分隔符
-
-
权限问题:
-
确保目标目录有写入权限
-
可能需要管理员权限复制到系统目录
-
-
符号链接:
-
xcopy默认不保留符号链接 -
Unix 的
cp -Rf会跟随符号链接
-
-
错误处理:
-
当前实现没有错误处理
-
可以扩展为检查
system()命令的返回值
-
替代方案
对于更复杂的文件操作,可以考虑:
-
使用 Python 脚本:
system(python copy_files.py $$source $$target) -
Qt 的部署工具:
///---qmake----------- include(deployment.pri) deployqt.target = deploy deployqt.commands = $$[QT_INSTALL_BINS]/windeployqt $$OUT_PWD/myapp.exe -
CMake 的 file(COPY...):
如果使用 CMake,有更强大的文件操作命令
总结
这个 qtCopy 函数是一个实用的 qmake 工具函数,特别适合:
-
Windows 平台下的文件复制操作
-
简单的构建后文件部署
-
资源文件的自动化管理
通过扩展可以实现更强大的跨平台文件操作功能,但对于复杂的部署需求,可能需要考虑更专业的构建后处理工具或脚本。

1555

被折叠的 条评论
为什么被折叠?



