第4章:qmake条件与作用域:平台判断与自定义变量
qmake 的威力,其实很大程度上体现在它的条件判断能力上。
说白了,就是让同一个 .pro 文件,能在不同平台、不同编译器下,生成不同的构建规则。我刚开始用 Qt 时,觉得 .pro 文件就是个简单的文件列表,后来项目一复杂,才发现条件判断才是真正的核心。
4.1 平台判断:win32 与 unix
qmake 内置了两个最常用的平台作用域:win32 和 unix。它们不是变量,而是 qmake 在解析时自动识别的关键字。
win32 {
LIBS += -lws2_32
SOURCES += network_win.cpp
}
unix {
LIBS += -lrt
SOURCES += network_unix.cpp
}
这段代码的意思是:在 Windows 下链接 ws2_32 库,并编译 Windows 专属的网络文件;在 Unix/Linux 下则链接 rt 库,编译 Unix 版本。
我的习惯:我会把平台相关的代码单独放到一个文件里,比如 platform_win.cpp 和 platform_unix.cpp。这样 .pro 文件里只需要做文件切换,逻辑清晰很多。
你可能会问:macOS 算 unix 吗?答案是肯定的。macOS 被 qmake 识别为 unix 作用域。如果你需要单独处理 macOS,可以用 macx 作用域。
macx {
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.15
ICON = app.icns
}
4.2 作用域语法:不只是 if-else
qmake 的作用域语法,其实比你想的要灵活。它支持嵌套、取反、多条件组合。
4.2.1 基本作用域
contains(QT, widgets) {
SOURCES += widget_code.cpp
}
这个作用域检查 QT 变量是否包含 widgets 模块。如果包含,就添加对应的源文件。
4.2.2 取反操作
!win32 {
# 非 Windows 平台
DEFINES += LINUX_BUILD
}
用 ! 符号取反,非常直观。我曾经在项目中遇到过一个问题:某个第三方库只在 Windows 下可用,其他平台需要替换成模拟实现。用取反作用域,一行代码就搞定了。
4.2.3 多条件组合
win32|unix {
# 在 Windows 或 Unix 下都执行
DEFINES += CROSS_PLATFORM
}
win32:debug {
# 仅在 Windows 的 Debug 模式下
LIBS += -lwin_debug_lib
}
| 表示「或」,: 表示「与」。注意这里的 : 不是冒号分隔符,而是 qmake 的「与」操作符。
避坑指南:我曾经在项目里写 win32 && debug,结果 qmake 报错。后来才意识到,qmake 的作用域组合用的是 : 而不是 &&。这个坑,我踩过一次就再也没忘过。
4.3 自定义变量与 $$ 操作符
qmake 允许你定义自己的变量,然后用 $$ 操作符来引用它们。这能极大提升 .pro 文件的可维护性。
4.3.1 定义和使用自定义变量
MY_LIB_PATH = /opt/my_libs
MY_SOURCES = core.cpp utils.cpp
INCLUDEPATH += $$MY_LIB_PATH/include
LIBS += -L$$MY_LIB_PATH/lib -lmycore
SOURCES += $$MY_SOURCES
你看,把路径和文件列表抽成变量,后面修改就只需要改一处。我个人习惯把所有的路径定义放在 .pro 文件的开头,像配置文件一样清晰。
4.3.2 变量拼接
VERSION_MAJOR = 2
VERSION_MINOR = 1
VERSION_PATCH = 0
VERSION = $$VERSION_MAJOR.$$VERSION_MINOR.$$VERSION_PATCH
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
这里用 $$ 把三个数字拼成版本号字符串,再传给 C++ 代码。注意转义字符 \\\",这是为了让 qmake 生成正确的字符串字面量。
注意:qmake 的变量展开是文本替换,不是类型计算。所以 $$VERSION_MAJOR + 1 不会得到 3,而是得到字符串 2 + 1。如果你需要做数值运算,得用 qmake 的 eval() 函数。
4.3.3 环境变量引用
# 引用系统环境变量
QMAKE_CXXFLAGS += $$(CUSTOM_CXXFLAGS)
# 引用 qmake 内置变量
message("Qt version: $$QT_VERSION")
message("Build dir: $$OUT_PWD")
$$() 引用系统环境变量,$$ 引用 qmake 变量。这个区别要记清楚,我见过有人把 $$(PATH) 写成 $$PATH,结果死活取不到系统路径。
4.4 知识体系图
4.5 实战技巧与避坑
说了这么多理论,来点实际的。我总结几个项目中常用的模式。
4.5.1 条件编译开关
CONFIG += my_feature
my_feature {
SOURCES += feature_impl.cpp
DEFINES += ENABLE_MY_FEATURE
} else {
SOURCES += feature_stub.cpp
}
用 CONFIG 变量做功能开关,比用注释方便多了。想开启某个功能,在 qmake 命令行加一句 CONFIG+=my_feature 就行。
4.5.2 调试与发布分离
CONFIG(debug, debug|release) {
# Debug 模式
DEFINES += DEBUG_OUTPUT
QMAKE_CXXFLAGS += -g -O0
} else {
# Release 模式
DEFINES += QT_NO_DEBUG_OUTPUT
QMAKE_CXXFLAGS += -O2
}
这个 CONFIG(debug, debug|release) 是 qmake 的「二选一」模式。第一个参数是你要检查的值,第二个参数是候选列表。如果第一个参数在候选列表中,条件成立。
我曾经踩过的坑:在 .pro 文件里用 if(debug) 这种写法,结果 qmake 根本不认。qmake 的作用域不是 C 语言的 if 语句,它是基于变量内容的文本匹配。记住这一点,能省很多调试时间。
4.5.3 自定义函数封装
defineReplace(getVersion) {
return($$system(git describe --tags))
}
VERSION = $$getVersion()
DEFINES += GIT_VERSION=\\\"$$VERSION\\\"
用 defineReplace 定义函数,可以复用逻辑。这里我获取 Git 标签作为版本号,每次构建自动更新。
嗯,qmake 的条件与作用域,说白了就是让你的构建系统「活」起来。不同平台、不同配置、不同功能开关,都能在一个 .pro 文件里优雅地处理。我个人觉得,这是 qmake 最值得花时间掌握的部分。
我的建议:刚开始别追求太复杂的条件嵌套。先学会 win32、unix 和 $$ 操作符,等用熟了再慢慢加多条件组合。一口吃不成胖子,构建系统也是一样。
可可精品资料库