第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 知识体系图

qmake条件与作用域知识体系 qmake 条件与作用域 平台判断 win32 unix macx 作用域语法 取反 ! 或运算 | 与运算 : 自定义变量与 $$ 变量定义 变量拼接 环境变量 实际应用:跨平台构建配置 三者结合使用,实现灵活、可维护的跨平台构建系统 win32:debug { LIBS += -lwin_debug_lib DEFINES += DEBUG_MODE }

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 和 $$ 操作符,等用熟了再慢慢加多条件组合。一口吃不成胖子,构建系统也是一样。

可可精品资料库