本文概述
- 介绍
- Qmake基本用法
- 了解变量的语法
- 通用模板
- 子项目
- 链接库
- config.pri
- 将工件复制到DESTDIR
- 代码生成
- 范围和条件
- 有用的功能
- 将警告视为错误
- 生成Git版本
- 总结
- 你正在构建一个基于Qt的跨平台项目
- 你正在使用Qt Creator IDE及其大多数功能
- 你正在构建供其他qmake项目使用的独立库/插件
文章图片
Qmake基本用法 qmake规范以.pro(“ 项目” )文件编写。这是最简单的.pro文件的示例:
SOURCES = hello.cpp
默认情况下, 这将创建一个Makefile, 该文件将从单个源代码文件hello.cpp构建可执行文件。
要生成二进制文件(在这种情况下是可执行文件), 你需要先运行qmake生成一个Makefile, 然后运行make(或nmake或mingw32-make, 具体取决于你的工具链)来构建目标。
简而言之, qmake规范仅是与可选控制流语句混合的变量定义列表。通常, 每个变量都包含一个字符串列表。控制流语句允许你包括其他qmake规范文件, 控制条件部分, 甚至包括调用函数。
了解变量的语法 学习现有的qmake项目时, 你可能会惊讶于如何引用不同的变量:{VAR}或$$(VAR)…
在遵循规则的同时使用此迷你备忘单:
- VAR =值将值分配给VAR
- VAR + =值将值追加到VAR列表
- VAR-= value从VAR列表中删除值
- $$ VAR或$$ {VAR}获取qmake运行时的VAR值
- $(VAR)运行Makefile(而非qmake)时环境VAR的内容
- qmake(而非Makefile)运行时环境VAR的$$(VAR)内容
让我们回顾一些项目的常用模板:
# Windows application
TEMPLATE = app
CONFIG += windows# Shared library (.so or .dll)
TEMPLATE = lib
CONFIG += shared# Static library (.a or .lib)
TEMPLATE = lib
CONFIG += static# Console application
TEMPLATE = app
CONFIG += console
只需添加SOURCES + =…和HEADERS + =…列出所有源代码文件, 即可完成。
到目前为止, 我们已经回顾了非常基本的模板。较复杂的项目通常包括几个相互依赖的子项目。让我们看看如何使用qmake进行管理。
子项目 最常见的用例是一个或多个库和测试项目附带的应用程序。考虑以下结构:
/project
../library
..../include
../library-tests
../application
【经典教程(Qmake权威学习指南)】显然, 我们希望能够一次构建所有内容, 例如:
cd project
qmake &
&
make
为了实现这个目标, 我们需要在/ project文件夹下有一个qmake项目文件:
TEMPLATE = subdirs
SUBDIRS = library library-tests application
library-tests.depends = library
application.depends = library
注意:使用CONFIG + =有序是一种不好的做法—最好使用.depends。
该规范指示qmake首先构建一个库子项目, 因为其他目标依赖它。然后, 它可以以任意顺序构建库测试和应用程序, 因为这两者是相关的。
文章图片
链接库 在上面的示例中, 我们有一个需要链接到应用程序的库。在C / C ++中, 这意味着我们需要配置一些其他东西:
- 指定-I为#include指令提供搜索路径。
- 指定-L为链接程序提供搜索路径。
- 指定-l以提供需要链接的库。
让我们利用qmake的include()指令解决此问题。在库项目中, 我们将在扩展名为.pri的新文件中添加另一个qmake规范(扩展名可以是任何东西, 但是在这里我代表include)。因此, 该库将具有两个规范:library.pro和library.pri。第一个用于构建库, 第二个用于提供使用项目所需的所有详细信息。
library.pri文件的内容如下:
LIBTARGET = library
BASEDIR= $${PWD}
INCLUDEPATH *= $${BASEDIR}/include
LIBS += -L$${DESTDIR} -llibrary
BASEDIR指定库项目的文件夹(确切地说, 是当前qmake规范文件的位置, 本例中为library.pri)。你可能会猜到, INCLUDEPATH将被评估为/ project / library / include。 DESTDIR是构建系统放置输出工件(例如.o .a .so .dll或.exe文件)的目录。通常, 这是在你的IDE中配置的, 因此你永远不要对输出文件的位置做任何假设。
在application.pro文件中, 只需添加include(../ library / library.pri)就可以了。
让我们回顾一下在这种情况下如何构建应用程序项目:
- 最顶层的project.pro是一个子目录项目。它告诉我们, 必须先构建图书馆项目。因此, qmake进入库的文件夹并使用library.pro进行构建。在此阶段, 将产生library.a并将其放置在DESTDIR文件夹中。
- 然后, qmake进入应用程序子文件夹并分析application.pro文件。它找到include(../ library / library.pri)指令, 该指令指示qmake立即读取和解释它。这为INCLUDEPATH和LIBS变量添加了新的定义, 因此现在编译器和链接器知道在哪里搜索包含文件, 库二进制文件以及要链接的库。
使用此设置, 你可以轻松地将库项目移至另一个qmake项目并包括它, 从而引用.pri文件。这正是社区分发第三方库的方式。
config.pri 对于复杂的项目来说, 拥有一些共享的配置参数是很常见的, 许多子项目都使用这些参数。为避免重复, 你可以再次使用include()指令并在顶级文件夹中创建config.pri。你可能还具有共享给你的子项目的通用qmake” 实用程序” , 类似于我们在本指南中接下来讨论的内容。
将工件复制到DESTDIR 通常, 项目中有一些” 其他” 文件需要与库或应用程序一起分发。我们只需要能够在构建过程中将所有此类文件复制到DESTDIR中。考虑以下代码段:
defineTest(copyToDestDir) {
files = $$1for(FILE, files) {
DDIR = $$DESTDIR
FILE = $$absolute_path($$FILE)# Replace slashes in paths with backslashes for Windows
win32:FILE ~= s, /, \\, g
win32:DDIR ~= s, /, \\, gQMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t)
}export(QMAKE_POST_LINK)
}
注意:使用此模式, 你可以定义自己的可重复使用的函数, 这些函数可用于文件。
将此代码放入/project/copyToDestDir.pri中, 以便可以在要求苛刻的子项目中包括()它, 如下所示:
include(../copyToDestDir.pri)MYFILES += \
parameters.conf \
testdata.db## this is copying all files listed in MYFILES variable
copyToDestDir($$MYFILES)## this is copying a single file, a required DLL in this example
copyToDestDir($${3RDPARTY}/openssl/bin/crypto.dll)
注意:DISTFILES是出于相同目的而引入的, 但仅在Unix中有效。
代码生成 当C ++项目使用Google protobuf时, 将代码生成作为预建步骤的一个很好的例子。让我们看看如何将协议执行注入构建过程。
你可以轻松地通过Google找到合适的解决方案, 但你需要了解一个重要的案例。假设你有两个合同, 其中A引用B。
A.proto <
= B.proto
如果我们首先为A.proto生成代码(以生成A.pb.h和A.pb.cxx)并将其提供给编译器, 则它将失败, 因为依赖项B.pb.h尚不存在。为了解决这个问题, 我们需要在生成结果源代码之前通过所有原始代码生成阶段。
我在这里找到了一个很好的代码片段:https://github.com/jmesmon/qmake-protobuf-example/blob/master/protobuf.pri
这是一个相当大的脚本, 但是你应该已经知道如何使用它:
PROTOS = A.proto B.proto
include(protobuf.pri)
查看protobuf.pri时, 你可能会注意到可以轻松应用于任何自定义编译或代码生成的通用模式:
my_custom_compiler.name = my custom compiler name
my_custom_compiler.input = input variable (list)
my_custom_compiler.output = output file path + pattern
my_custom_compiler.commands = custom compilation command
my_custom_compiler.variable_out = output variable (list)
QMAKE_EXTRA_COMPILERS += my_custom_compiler
范围和条件 通常, 我们需要专门为给定平台(例如Windows或MacOS)定义声明。 Qmake提供了三个预定义的平台指示器:win32, macx和unix。语法如下:
win32 {
# add Windows application icon, not applicable to unix/macx platform
RC_ICONS += icon.ico
}
范围可以嵌套, 可以使用运算符!, |甚至通配符:
macx:debug {
# include only on Mac and only for debug build
HEADERS += debugging.h
}win32|macx {
HEADERS += windows_or_macx.h
}win32-msvc* {
# same as win32-msvc|win32-mscv.net
}
注意:Unix是在Mac OS上定义的!如果要测试Mac OS(非通用Unix), 请使用unix:!macx条件。
在Qt Creator中, 范围条件调试和释放无法按预期方式工作。要使它们正常工作, 请使用以下模式:
CONFIG(debug, debug|release) {
LIBS += ...
}CONFIG(release, debug|release) {
LIBS += ...
}
有用的功能 Qmake具有许多嵌入式功能, 可以提高自动化程度。
第一个示例是files()函数。假设你有一个代码生成步骤, 该步骤生成可变数量的源文件。你可以通过以下方法将它们全部包含在SOURCES中:
SOURCES += $$files(generated/*.c)
这将在生成的子文件夹中找到所有扩展名为.c的文件, 并将它们添加到SOURCES变量中。
第二个示例与前面的示例相似, 但是现在代码生成生成了一个文本文件, 其中包含输出文件名(文件列表):
SOURCES += $$cat(generated/filelist, lines)
这只会读取文件内容, 并将每一行都视为SOURCES的条目。
注意:嵌入式功能的完整列表可在以下位置找到:http://doc.qt.io/qt-5/qmake-function-reference.html
将警告视为错误 以下代码段使用前面描述的条件范围功能:
*g++*: QMAKE_CXXFLAGS += -Werror
*msvc*: QMAKE_CXXFLAGS += /WX
这种复杂的原因是因为MSVC具有不同的标志来启用此选项。
生成Git版本 当需要创建包含从Git获取的当前SW版本的预处理器定义时, 以下代码段很有用:
DEFINES += SW_VERSION=\\\"$$system(git describe --always --abbrev=0)\\\"
只要git命令可用, 它就可以在任何平台上使用。如果你使用Git标签, 那么即使分支继续进行, 这也会偷看最新的标签。修改git describe命令以获取你选择的输出。
总结 Qmake是一个出色的工具, 专注于构建基于Qt的跨平台项目。在本指南中, 我们回顾了基本的工具用法和最常用的模式, 这些模式将使你的项目结构保持灵活, 并易于阅读和维护构建规范。
想学习如何使你的Qt应用看起来更好吗?尝试:如何使用贝塞尔曲线和QPainter在C ++中获得圆角形状:分步指南
推荐阅读
- 在Node.js中创建安全的REST API
- 使用Crystal编程语言创建加密货币
- 有关C++中Qt多线程的缺失文章
- 机器人编程入门教程
- 人工智能的经济效益
- 掌握框架–探索依赖注入模式
- 如何制作Discord机器人(概述和教程)
- 了解Android干净架构的好处
- logback:fileAppender输出到文件