1. 添加新配置特性
qmake 允许我们通过将自己的名称添加到 CONFIG 变量指定的值列表中来创建自己的 features
,这些特性可以包含在项目文件中。特性是 .prf
文件中的自定义函数和定义的集合,可以驻留在许多标准目录中的一个目录中。这些目录的位置在多个位置定义,并且 qmake 在查找 .prf
文件时按以下顺序检查每个标准目录:
- 在 QMAKEFEATURES 环境变量中列出的目录中,其中包含由平台的路径列表分隔符分隔的目录列表(Unix 为冒号,Windows 为分号)
- 在 QMAKEFEATURES 属性变量中列出的目录中,其中包含由平台的路径列表分隔符分隔的目录列表
- 位于 mkspecs 目录中的 features 目录中。mkspecs 目录可以位于 QMAKEPATH 环境变量中列出的任何目录之下,QMAKEPATH 环境变量包含由平台的路径列表分隔符分隔的目录列表。例如:
$QMAKEPATH/mkspecs/
- 位于 QMAKESPEC 环境变量提供的目录下的 features 目录中。例如:
$QMAKESPEC/
- 位于 data_install/mkspecs 目录中的 features 目录中。例如:
data_install/mkspecs/
- 在 features 目录中,该目录作为 QMAKESPEC 环境变量指定的目录的兄弟目录而存在。例如:
$QMAKESPEC/../
以下 features 目录被用于搜索 features 文件:
-
features/unix
、features/win32
或features/macx
,取决于使用的平台 features/
例如,在项目文件中添加以下代码:
CONFIG += myfeatures
附加属性到 CONFIG 变量,qmake 将在完成项目文件的解析后,在上面列出的位置中搜索 myfeatures.prf 文件。以 Unix 系统为例,它将查找以下文件:
-
$QMAKEFEATURES/myfeatures.prf
(对于 QMAKEFEATURES 环境变量中列出的每个目录) -
$$QMAKEFEATURES/myfeatures.prf
(对于 QMAKEFEATURES 属性变量中列出的每个目录) -
myfeatures.prf
(在项目的根目录中)。项目根由顶级.pro
文件确定。但是,如果我们放置 .qmake.cache 在子目录或子项目目录下,然后项目根目录也会成为子目录。 -
$QMAKEPATH/mkspecs/features/unix/myfeatures.prf
和$QMAKEPATH/mkspecs/features/myfeatures.prf
(对于 QMAKEPATH 环境变量中列出的每个目录) -
$QMAKESPEC/features/unix/myfeatures.prf
和$QMAKESPEC/features/myfeatures.prf
-
data_install/mkspecs/features/unix/myfeatures.prf
和data_install/mkspecs/features/myfeatures.prf
-
$QMAKESPEC/../features/unix/myfeatures.prf
和$QMAKESPEC/../features/myfeatures.prf
注意: .prf
文件的名称必须小写
2. 安装文件
在 Unix 上使用构建工具来安装应用程序和库是很常见的;例如,通过调用 make install
。由于这个原因,qmake 具有 install set
(安装集)的概念,即包含有关如何安装项目的一部分的说明的对象。例如,一组文档文件可以用以下方式描述:
documentation.path = /usr/local/program/doc
documentation.files = docs/*
path
成员通知 qmake 文件应该安装在 /usr/local/program/doc
(path 成员)中,而 files
成员指定应该复制到安装目录的文件。在本例中,docs
目录中的所有内容都将被复制到 /usr/local/ program/doc
中。
一旦一个安装集已经完全描述,我们可以通过如下方式附加它到安装列表:
INSTALLS += documentation
qmake 将确保将指定的文件复制到安装目录。如果需要对这个过程进行更多控制,还可以为对象的 extra
(额外)成员提供定义。例如,下面的行告诉 qmake 为这个安装集执行一系列命令:
unix:documentation.extra = create_docs; mv master.doc toc.doc
unix 条件域 确保这些特定的命令只在 unix 平台上执行。可以使用其他范围规则为其他平台定义适当的命令。
在 extra
(额外)成员中指定的命令将在对象的其他成员中的指令执行之前执行。
如果我们将内置安装集附加到 INSTALLS 变量,并且没有指定 files
或 extra
成员,qmake 将决定需要为我们复制什么。目前,支持 target
和 dlltarget
安装集。例如:
target.path = /usr/local/myprogram
INSTALLS += target
在上面的代码行中,qmake 知道需要复制什么,并将自动处理安装过程。
3. 添加自定义目标
qmake 尝试实现跨平台构建工具的所有功能。当我们真正需要运行与平台相关的特殊命令时,这通常不太理想。这可以通过对不同的 qmake 后端进行特定的指令来实现。
Makefile 输出的定制是通过对象样式的 API 执行的,在 qmake 的其他地方可以找到这种 API。对象是通过指定其成员自动定义的。例如:
mytarget.target = .buildfile
mytarget.commands = touch $$mytarget.target
mytarget.depends = mytarget2
mytarget2.commands = @echo Building $$mytarget.target
上面的代码定义了一个名为 mytarget
的 qmake 目标,其中包含一个名为 .buildfile
的 Makefile 目标,该目标由 touch
命令生成。最后,.depends
成员指定 mytarget 依赖于 mytarget2,这是之后定义的另一个目标。mytarget2 是一个虚拟目标。它只被定义为将一些文本打印到控制台。
最后一步是使用 QMAKE_EXTRA_TARGETS 变量来指示 qmake 这个对象是要构建的目标:
QMAKE_EXTRA_TARGETS += mytarget mytarget2
这就是实际构建自定义目标所需要做的全部工作。当然,我们可能希望将其中一个目标绑定到 qmake 构建目标。要做到这一点,我们只需要在 PRE_TARGETDEPS 列表中包含 Makefile 目标。
自定义目标规范支持以下成员:
成员 | 描述 |
---|---|
commands | 生成自定义构建目标(target)的命令 |
CONFIG | 定制构建目标(target)的特定配置选项。可以设置为 recursive,以指示应该在 Makefile 中创建规则,以调用子目标特定的 Makefile 内部的相关目标。这个成员默认为每个子目标创建一个条目 |
depends | 自定义构建目标(target)所依赖的现有构建目标 |
recurse | 指定在"Makefile"中创建要调用子目标( sub-target)特定 Makefile 中的规则时应使用哪些子目标( sub-target)。此成员仅在 CONFIG 中设置 recursive 时使用。典型的值是“Debug”和“Release” |
recurse_target | 为 Makefile 中的规则指定应该通过子目标( sub-target) Makefile 构建的目标(target)。这个成员添加了类似于 $(MAKE) -f Makefile.[subtarget] [recurse_target] 。此成员仅在 CONFIG 中设置 recursive 时使用 |
target | 自定义构建目标(target)的名称 |
4. 添加编译器
可以自定义 qmake 来支持新的编译器和预处理程序:
new_moc.output = moc_${QMAKE_FILE_BASE}.cpp
new_moc.commands = moc ${QMAKE_FILE_NAME} -o ${QMAKE_FILE_OUT}
new_moc.depend_command = g++ -E -M ${QMAKE_FILE_NAME} | sed "s,^.*: ,,"
new_moc.input = NEW_HEADERS
QMAKE_EXTRA_COMPILERS += new_moc
有了上述定义,如果 moc 可用,我们可以使用 drop-in 替换 moc。该命令对所有给定给 NEW_HEADERS
变量的参数(来自 input
成员)执行,结果被写入由 output
成员定义的文件。这个文件被添加到项目中的其他源文件中。此外,qmake 将执行 depend_command
来生成依赖信息,并将这些信息放到项目中。
自定义编译器规范支持以下成员:
成员 | 描述 |
---|---|
commands | 用于从输入生成输出的命令 |
CONFIG | 自定义编译器的特定配置选项。有关详细信息,请参见配置 |
depend_command | 指定用于为输出生成依赖项列表的命令 |
dependency_type | 指定输出文件的类型。如果它是一种已知的类型(例如 TYPE_C、TYPE_UI、TYPE_QRC),则将其作为这些类型的文件之一处理 |
depends | 指定输出文件的依赖项 |
input | 该变量指定应使用自定义编译器处理的文件 |
name | 自定义编译器正在做什么的描述。这只在一些后端使用 |
output | 从自定义编译器创建的文件名 |
output_function | 指定用于指定要创建的文件名的自定义 qmake 函数 |
variables | 指示在 pro 文件中引用 (QMAKE_COMP_VARNAME) |
variable_out | 应该将从输出创建的文件添加到的变量 |
此时 CONFIG 成员支持以下选项:
选项 | 描述 |
---|---|
combine | 指示将所有输入文件合并到单个输出文件中 |
target_predeps | 指示输出应添加到 PRE_TARGETDEPS 列表中 |
explicit_dependencies | 输出的依赖项只能从 depends 成员生成,而不能从其他任何地方生成 |
dep_existing_only | 检查 .depend_command 的每个依赖项是否存在。不存在的依赖关系将被忽略。这个值是在Qt 5.13.2中引入的 |
dep_lines | .depend_command 的输出被解释为每行一个文件。默认情况下,是空格分开的,只有在向后兼容的情况下才会进行维护 |
no_link | 指示不应将输出添加到要链接的对象列表中 |
5. 库依赖项
通常,当链接到一个库时,qmake 依赖于底层平台来了解这个库链接到哪些其他库,并让平台将它们拉进来。然而,在许多情况下,这是不够的。例如,当静态地链接一个库时,不会链接到其他库,因此不会创建对这些库的依赖关系。但是,以后链接到这个库的应用程序需要知道在哪里可以找到静态库需要的符号。如果显式启用跟踪,则 qmake 尝试在适当的情况下跟踪库的依赖项。
第一步是在库本身中启用依赖项跟踪。要做到这一点,你必须告诉 qmake 保存关于 library 的信息:
CONFIG += create_prl
这只与 lib
模板(template)相关,其他模板将忽略它。当此选项被启用时,qmake 将创建一个以 .prl 结尾的文件,该文件将保存关于库的一些元信息。这个元文件就像一个普通的项目文件,但是只包含内部变量声明。在安装这个库时,通过在 INSTALLS 变量中将其指定为目标,qmake 会自动将 .prl 文件复制到安装路径。
此过程的第二步是启用在使用静态库的应用程序中读取此元信息:
CONFIG += link_prl
启用此功能后,qmake 将处理应用程序链接的所有库并查找其元信息。qmake 将用它来确定相关的链接信息,特别是向应用程序项目文件的
DEFINES 列表以及 LIBS 添加值。一旦 qmake 处理了此文件,它将查看 LIBS 变量中新引入的库,并找到其依赖的 .prl 文件,一直持续到解析完所有库。此时,将像往常一样创建 Makefile,并针对应用程序显式地链接库。
.prl 文件应该只由 qmake 创建,并且不应该跨操作系统传递使用,因为它们可能包含平台相关的信息。