qmake 手册 007 qmake 语言
qmake 手册 007 qmake 语言
许多 qmake 项目文件使用 name = value 和 name += value 定义列表简单地描述了项目使用的源文件和头文件。qmake 还提供了其他操作符、函数和作用域,可用于处理变量声明中提供的信息。这些高级特性使得从单个项目文件为多个平台生成 makefile 变得简单高效。
1. 运算符
在大多数工程文件中,分配操作符(=
)和添加操作符(+=
)可被用来引入(include)有关于项目的几乎全部信息。典型的使用方式是分配给一个变量的值列表,并且我们可以依据各种测试的结果来添加更多的值。由于 qmake 有时候会使用默认值来初始化某些变量,因此此时使用删除(-=
)操作符来过滤掉不需要的值就是相当必要的了。以下内容将会讲解用操作符来修改变量的内容的方法。
1.1 赋值
我们使用 =
操作符将值指定给一个变量:
TARGET = myapp
在上一行中,设定 TARGET 变量的值为 myapp
,这样我们就可以使用一个 “myapp” 值来覆盖任何以前设置给 TARGET 的值了
1.2 附加
+=
操作符将在一个变量的值列表添加一个新值:
DEFINES += USE_MY_STUFF
在上面一行语句中我们附加 USE_MY_STUFF 到预定义列表,这样我们就可以在 Makefile 中使用 USE_MY_STUFF 这个预定义了
1.3 移除
-=
操作符用于在一个变量的值列表中删除一个值:
DEFINES -= USE_MY_STUFF
在上面一行语句中我们从预定义列表中移除 USE_MY_STUFF 的预定义,这样在 Makefile 中的有关 USE_MY_STUFF 的预定义将会失效
1.4 避免重复的附加
*=
操作符也被用于在一个变量的值列表中添加一个值,但只有当它不是已存在变量的时候才有效。这可以防止变量值被多次的包含在一个变量中列表。例如:
DEFINES *= USE_MY_STUFF
上面的语句中,USE_MY_STUFF 将只有在预定义列表中不存在该定义时才会被添加,友情提示,unique() 函数也可以用来确保一个变量的每个值只包含一个实例
1.5 替换
~=
操作符用于用指定的值替换任何一个相匹配的正则表达式的值:
DEFINES ~= s/QT_[DT].+/QT
上面一行语句中,在预定义列表中的任何以 QT_D 或者 QT_T 开头的预定义都将被替换为 QT
1.6 变量扩展
$$
操作符被用于提取变量的内容,并且也能被用作在变量之间传值,或者传递这些值给函数
EVERYTHING = $$SOURCES $$HEADERS
message("The project contains the following files:")
message($$EVERYTHING)
变量可以用来存储环境变量的内容。这些可以在运行 qmake 时使用,或者在生成项目时生成的 Makefile 中使用。
要在运行 qmake 时获取环境值的内容,请使用 $$(...)
运算符:
DESTDIR = $$(PWD)
message(The project will be installed in $$DESTDIR)
在上面的分配中,当处理项目文件时读取 PWD
环境变量的值。
要在生成的 Makefile 文件被处理时获取环境值的内容,请使用 $(...)
运算符:
DESTDIR = $$(PWD)
message(The project will be installed in $$DESTDIR)
DESTDIR = $(PWD)
message(The project will be installed in the value of PWD)
message(when the Makefile is processed.)
在上面的代码中,处理项目文件时会立即读取 PWD 的值,但在生成的 Makefile 文件中将 $(PWD)
的值分配给 DESTDIR 变量发生在 Makefile 文件被处理时。这使得构建过程更加灵活,只要在处理 Makefile 时正确设置环境变量即可
1.7 访问 qmake 属性
特殊的 $$[...]
操作符可用于访问 qmake 属性:
message(Qt version: $$[QT_VERSION])
message(Qt is installed in $$[QT_INSTALL_PREFIX])
message(Qt resources can be found in the following locations:)
message(Documentation: $$[QT_INSTALL_DOCS])
message(Header files: $$[QT_INSTALL_HEADERS])
message(Libraries: $$[QT_INSTALL_LIBS])
message(Binary files (executables): $$[QT_INSTALL_BINS])
message(Plugins: $$[QT_INSTALL_PLUGINS])
message(Data files: $$[QT_INSTALL_DATA])
message(Translation files: $$[QT_INSTALL_TRANSLATIONS])
message(Settings: $$[QT_INSTALL_CONFIGURATION])
message(Examples: $$[QT_INSTALL_EXAMPLES])
更多内容,大家可以查阅 Configuring qmake 文档
该操作符可访问的属性通常用于允许第三方插件和组件集成到 Qt 中。例如,如果在 Qt Designer 的项目文件中声明如下,则可以将 Qt Designer 插件与 Qt Designer 的内置插件一起安装:
target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS += target
2. 条件域
条件域类似于编程语言中的 if 语句。如果一个特定的条件是真的,在条件域内的声明将会被处理
2.1 条件域的语法
条件域包含一个条件后跟一个在同一行的左花括号,然后是一系列的命令和定义,最后是在新的一行的一个右花括号。就像下面这样:
<condition> {
<command or definition>
...
}
左花括号必须要和条件写在同一行。条件域可以包扩不止一个条件;这些之后就要介绍到。
2.2 条件域和条件
一个条件域被写成一个条件后跟一系列声明包含在一对大括号中,例如:
win32 {
SOURCES += paintwidget_win.cpp
}
如果 qmake 用于 Windows 平台,上面的代码将添加 paintwidget_win.cpp 文件到 Makefile 的资源列表。如果 qmake 用于其他的平台,该条语句将被忽略。
当然我们也可以逆向思维,达到同样的目的,例如我们使用下面的语句:
!win32 {
SOURCES -= paintwidget_win.cpp
}
也可以达到一样的目的。
条件域可嵌套组合多个条件。例如,如果您想要为一个特定的平台中,在满足了调试的被启用后,包含(include)一个特定的文件,然后你就可以写如下代码:
macx {
debug {
HEADERS += debugging.h
}
}
来满足你的需求。
为了简化嵌套条件域,我们可以使用 :
操作符,对于上一个例子中的功能,我们可以用如下代码来简化它:
macx:debug {
HEADERS += debugging.h
}
我们也可以使用 :
操作符来执行单一线条件的操作,例如:
win32:DEFINES += USE_MY_STUFF
上面一行的作用是,仅在 windows 平台上添加 USE_MY_STUFF 定义到 DEFINES 列表。通常,:
操作符很像是逻辑与(&&
)操作符,它会拼接一些条件,并且要求它们都为真。
我们也有 |
操作符,用来实现像逻辑或操作符(||
)一样的功能,它用来连接一些条件,并且仅要求其中至少一个为真。例如:
win32|macx {
HEADERS += debugging.h
}
如果需要混合使用这两个操作符,可以使用 if
函数来指定操作符优先级
if(win32|macos):CONFIG(debug, debug|release) {
# Do something on Windows and macOS,
# but only for the debug configuration.
}
win32|if(macos:CONFIG(debug, debug|release)) {
# Do something on Windows (regardless of debug or release)
# and on macOS (only for debug).
}
条件接受通配符来匹配一系列 CONFIG 值或 mkspec 名称
win32-* {
# Matches every mkspec starting with "win32-"
SOURCES += win32_specific.cpp
}
注意:使用上面的通配符检查 mkspec 名称是 qmake 检查平台的陈旧方法。现在,我们建议使用 QMAKE_PLATFORM 变量中由 mkspec 定义的值
我们也可以编写复杂的测试语句,对条件进行逐一的测试,这主要依靠 “else
” 来完成,例如我们可以像下面这样写我们的代码:
win32:xml {
message(Building for Windows)
SOURCES += xmlhandler_win.cpp
} else:xml {
SOURCES += xmlhandler.cpp
} else {
message("Unknown configuration")
}
2.3 配置和条件域
在 CONFIG 变量中存储的值是由 qmake 特别处理的。每一个可能的值都可以用作条件域的条件。例如,CONFIG 保存的列表的值可以使用 opengl 来扩展:
CONFIG += opengl
如果我们像上面那样做的话,任何测试 opengl
的条件域都将是有效的,并且会被处理,我们可以使用这个功能给最后的可执行文件一个适当的名称:
opengl {
TARGET = application-gl
} else {
TARGET = application
}
该特性使更改项目的配置变得很容易,而不会丢失特定配置可能需要的所有自定义设置,而我们所要做的,可能只是一个特定的配置。在上面的代码中,在第一个条件域中声明的代 码将会被处理,因此最终的可执行文件将会被命名为 “application-gl
”。然而,如果 opengl
没有被指定,声明在第二个条件域内的代码会被处理,最终的可执行文件会被称为 “application
”。
正因为我们可以把自定义的值附加给 CONFIG,我们就可以很方便的定制项目文件和调整 Makefile 文件。
2.4 平台条件域值
除了 win32
,macx
和 unix
这样的常用于条件域条件的值,还有其他各种内置平台和编译器具体值也可以在条件域中用于测试。这些基于平台规范在 Qt 的 mkspecs 目录中被提供。例如,下面的代码用于显示当前使用的规范并且测试 linux-g++
规范。
message($$QMAKESPEC)
linux-g++ {
message(Linux)
}
我们可以测试任何其它平台的编译器组合,只要它的规范在 mkspecs 目录中存在。
3. 变量
项目文件中使用的许多变量是 qmake 在生成 Makefile 时使用的特殊变量,例如 DEFINES、SOURCES 和 HEADERS。此外,我们可以创建供自己使用的变量。qmake 在遇到给定名称的赋值时创建具有该名称的新变量。例如:
MY_VARIABLE = value
对于如何处理自己的变量没有限制,因为 qmake 会忽略它们,除非在处理作用域时需要对它们求值。
还可以通过在变量名前面加上 $$
来将当前变量的值赋给另一个变量。例如:
MY_DEFINES = $$DEFINES
现在 MY_DEFINES 变量包含项目文件中定义变量中的内容。这也等价于:
MY_DEFINES = $${DEFINES}
第二种表示法允许我们将变量的内容追加到另一个值,而不使用空格分隔这两个值。例如,下面的代码将确保最终的可执行文件有一个包含所使用的项目模板的名称:
TARGET = myproject_$${TEMPLATE}
4. 替换函数
qmake 提供了一个内置函数的选择,允许处理变量的内容。这些函数处理提供给它们的参数,并返回一个值或值列表。要将结果赋值给一个变量,可以使用 $$
操作符,就像将一个变量的内容赋值给另一个变量一样:
HEADERS = model.h
HEADERS += $$OTHER_HEADERS
HEADERS = $$unique(HEADERS)
这种类型的函数应该在赋值的右侧使用(即作为操作数)。
我们可以定义自己的函数来处理变量的内容如下:
defineReplace(functionName){
#function code
}
下面的示例函数以 variable(变量名)作为惟一的参数,使用 eval() 内置函数从变量中提取值列表,并编译文件列表:
defineReplace(headersAndSources) {
variable = $$1
names = $$eval($$variable)
headers =
sources =
for(name, names) {
header = $${name}.h
exists($$header) {
headers += $$header
}
source = $${name}.cpp
exists($$source) {
sources += $$source
}
}
return($$headers $$sources)
}
5. 测试函数
qmake 提供了内置函数,可以在编写作用域时将其用作条件。这些函数不返回值,而是指示成功或失败:
count(options, 2) {
message(Both release and debug specified.)
}
这种类型的函数应该只在条件表达式中使用。
可以定义自己的函数来为条件域提供条件。下面的示例测试列表中的每个文件是否存在,如果都存在则返回 true,如果有一个文件不存在则返回 false:
defineTest(allFiles) {
files = $$ARGS
for(file, files) {
!exists($$file) {
return(false)
}
}
return(true)
}