authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
安德烈·斯米尔诺夫的头像

Andrei Smirnov

Andrei在微软这样的公司工作了15年以上, EMC, Motorola, 以及德意志银行(Deutsche Bank)的手机业务, desktop, and web using C++, C#, and JS.

Previously At

Microsoft
Share

Introduction

qmake 是否附带了构建系统工具 Qt library 这简化了跨不同平台的构建过程. Unlike CMake and Qbs, qmake was a part of Qt since the very beginning and shall be considered as a “native” tool. 不用说,Qt的默认IDE -Qt Creator-有最好的支持qmake开箱即用. Yes, 你也可以选择CMake和Qbs构建系统为一个新的项目, 但它们并没有很好地结合在一起. Qt Creator中的CMake支持很可能会随着时间的推移而得到改进, 这将是发布本指南第二版的一个很好的理由, 专门针对CMake的. 即使你不打算使用Qt Creator, you may still want to consider qmake as a second build system in case you are building public libraries or plugins. Virtually all third-party Qt-based libraries or plugins supply qmake files used to integrate into qmake-based projects seamlessly. 它们中只有少数提供双配置,例如.g., qmake and CMake. 如果以下情况适用于您,您可能更喜欢使用qmake:

  • 您正在构建一个跨平台的基于qt的项目
  • 您正在使用Qt Creator IDE及其大部分功能
  • You are building a standalone library/plugin to be used by other qmake projects

This guide describes the most useful qmake features and provides real-world examples for each of them. Readers that are new to Qt can use this guide as a tutorial to Qt’s build system. Qt developers can treat this as a cookbook when starting a new project or can selectively apply some of the features to any of the existing projects with low impact.

qmake构建过程的说明

Basic Qmake Usage

qmake规范是用 .pro (“project”) files. 这是一个最简单的例子 .pro file:

SOURCES = hello.cpp

默认情况下,这将创建一个 Makefile 这将从单个源代码文件构建可执行文件 hello.cpp.

要构建二进制文件(在本例中为可执行文件), 你需要先运行qmake来生成一个Makefile,然后 make (or nmake, or mingw32-make (取决于您的工具链)来构建目标.

In a nutshell, a qmake specification is nothing more than a list of variable definitions mixed with optional control flow statements. 一般来说,每个变量都包含一个字符串列表. 控制流语句允许您包含其他qmake规范文件, 控制条件节, 甚至调用函数.

理解变量的语法

当学习现有的qmake项目, 你可能会惊讶于不同的变量是如何被引用的: \(VAR,\){VAR}或$$(VAR)

在遵守规则的同时,使用这张迷你小抄:

  • VAR = value Assign value to VAR
  • VAR += value 附加值到VAR列表
  • VAR -= value 从VAR列表中删除值
  • $$VAR or $${VAR} 获取运行qmake时VAR的值
  • $(VAR) Makefile(不是qmake)运行时环境VAR的内容
  • $$(VAR) 运行qmake(不是Makefile)时环境VAR的内容

Common Templates

完整的qmake变量列表可以在规范中找到: http://doc.qt.io/qt-5/qmake-variable-reference.html

让我们回顾一些常见的项目模板:

# Windows应用
TEMPLATE = app
CONFIG += windows

# Shared library (.so or .dll)
TEMPLATE = lib
CONFIG += shared

# Static library (.a or .lib) 
TEMPLATE = lib
CONFIG += static

#控制台应用
TEMPLATE = app
CONFIG += console

Just add SOURCES += … and HEADERS += … 列出所有源代码文件,就完成了.

到目前为止,我们已经审查了非常基本的模板. More complex projects usually include several sub-projects with dependencies on each other. 让我们看看如何使用qmake来管理它.

Sub-projects

The most common use case is an application that is shipped with one or several libraries and test projects. 考虑以下结构:

/project
../library
..../include
../library-tests
../application

显然,我们希望能够一次构建所有东西,就像这样:

cd project
qmake && make

要实现这个目标,我们需要在 /project folder:

TEMPLATE = subdirs
SUBDIRS =库库测试应用程序
library-tests.depends = library
application.depends = library

NOTE: using CONFIG += ordered 被认为是不好的做法—更喜欢使用 .depends instead.

This specification instructs qmake to build a library sub-project first because other targets depend on it. Then it can build library-tests 应用的顺序是任意的,因为这两个是相关的.

项目目录结构

Linking Libraries

In the above example, we have a library that needs to be linked to the application. 在C/ c++中,这意味着我们需要配置更多的东西:

  1. Specify -I 为#include指令提供搜索路径.
  2. Specify -L 为链接器提供搜索路径.
  3. Specify -l 提供需要链接的库.

Because we want all sub-projects to be movable, we cannot use absolute or relative paths. 例如,我们不应该这样做: INCLUDEPATH += ../library/include 当然我们不能引用库binary ().(文件)从临时构建文件夹. 遵循“关注点分离”原则, we can quickly realize that the application project file shall abstract from library details. Instead, it is the library’s responsibility to tell where to find header files, etc.

我们来杠杆化qmake include() 指令来解决这个问题. 在图书馆项目中, 我们将在一个扩展名为qmake的新文件中添加另一个qmake规范 .pri (扩展可以是任何东西,但在这里 i stands for include). 因此,库将有两个规范: library.pro and library.pri. 第一个用于构建库, 第二个用于提供消费项目所需的所有细节.

图书馆的内容.Pri文件如下所示:

LIBTARGET = library
BASEDIR   = $${PWD}
INCLUDEPATH *= $${BASEDIR}/include
lib += -L$${DESTDIR} -llibrary

BASEDIR 指定库项目的文件夹(确切地说), 当前qmake规范文件的位置, which is library.pri in our case). As you might guess, INCLUDEPATH 将被评估为 /项目/图书馆/包括. DESTDIR is the directory where the build system is placing the output artifacts, such as (.o .a .so .dll or .exe files). 这通常在您的IDE中配置, 因此,您不应该对输出文件的位置进行任何假设.

In the application.pro file just add include(../library/library.pri) and you are done.

让我们回顾一下在本例中应用程序项目是如何构建的:

  1. Topmost project.pro 是subdirs项目吗. 它告诉我们首先需要建立图书馆项目. 因此,qmake进入库的文件夹并使用 library.pro. At this stage, library.a 生产和放置到 DESTDIR folder.
  2. 然后qmake进入应用程序子文件夹并解析 application.pro file. It finds the include(../library/library.pri) 指令,它指示qmake立即阅读和解释它. 的新定义 INCLUDEPATH and LIBS variables, 现在编译器和链接器知道在哪里搜索包含文件, 库二进制文件, 以及链接哪个图书馆.

We skipped the building of the library-tests project, but it is identical to the application project. Obviously, our test project would also need to link the library that it is supposed to test.

With this setup, you can easily move the library project to another qmake project and include it, 从而引用 .pri file. 这正是社区分发第三方库的方式.

config.pri

It is very common to a complex project to have some shared configuration parameters that are used by many sub-projects. 为了避免重复,您可以再次利用 include() 指令和创建 config.pri 在顶层文件夹中. 您还可以将公用的qmake“实用程序”共享给子项目, 类似于我们接下来在本指南中讨论的内容.

将工件复制到DESTDIR

Often, projects have some “other” files that need to be distributed along with a library or application. 我们只需要能够复制所有这些文件到 DESTDIR 在构建过程中. 考虑下面的代码片段:

defineTest (copyToDestDir) {
    files = $$1

    for(FILE, files) {
        DDIR = $$DESTDIR
		    FILE = $$absolute_path($$FILE)

        #将路径中的斜杠替换为Windows的反斜杠
        win32:FILE ~= s,/,\\,g
        win32:DDIR ~= s,/,\\,g

        QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t)
    }

    出口(QMAKE_POST_LINK)
}

Note: Using this pattern, you can define your own reusable functions that work on files.

Place this code into /项目/ copyToDestDir.pri so you can include() 它要求分项目如下:

include(../copyToDestDir.pri)

MYFILES += \
    parameters.conf \
    testdata.db

这是复制MYFILES变量中列出的所有文件
copyToDestDir myfile美元($)

##这是复制一个文件,在这个例子中是一个必需的DLL
rdparty copyToDestDir ($ $ {3} / openssl / bin /加密.dll)

Note: DISTFILES was introduced for the same purpose, but it only works in Unix.

Code Generation

A great example of code generation as a pre-built step is when a C++ project is using Google protobuf. 我们来看看怎么注射 protoc 在构建过程中执行.

You can easily Google a suitable solution, but you need to be aware of one important corner case. 假设你有两个契约,其中A引用B.

A.proto <= B.proto

如果我们生成代码 A.proto first (to produce A.pb.h and A.pb.cxx)并将其提供给编译器,它将失败,因为依赖关系 B.pb.h does not exist yet. To solve this, we need to pass all proto code generation stage prior to building the resulting source code.

我在这里找到了一个很好的片段: http://github.com/jmesmon/qmake-protobuf-example/blob/master/protobuf.pri

这是一个相当大的脚本,但你应该已经知道如何使用它:

PROTOS = A.proto B.proto
include(protobuf.pri)

When looking into protobuf.pri, you may notice the generic pattern that can be easily applied to any custom compilation or code generation:

my_custom_compiler.Name =我的自定义编译器名称
my_custom_compiler.输入=输入变量(列表)
my_custom_compiler.输出=输出文件路径+模式
my_custom_compiler.命令=自定义编译命令
my_custom_compiler.Variable_out =输出变量(列表)
QMAKE_EXTRA_COMPILERS += my_custom_compiler

范围和条件

Often, we need to define declarations specifically for a given platform, such as Windows or MacOS. Qmake提供了三个预定义的平台指示器:win32、macx和unix. Here is the syntax:

win32 {
    添加Windows应用程序图标,不适用于unix/macx平台
    RC_ICONS += icon.ico
}

作用域可以嵌套,可以使用操作符 !, | and even wildcards:

macx:debug {
    # include仅适用于Mac和调试版本
    HEADERS += debugging.h
}

win32|macx {
    HEADERS += windows_or_macx.h
}

win32-msvc* {
    #同win32-msvc|win32-mscv.net
}

注:Unix是在Mac OS上定义的! 如果您想测试Mac OS(而不是一般的Unix),那么使用 unix:!macx condition.

在Qt Creator中,作用域条件 debug and release 没有像预期的那样工作. 要使它们正常工作,请使用以下模式:

配置(调试,调试|释放){
    LIBS += ...
}

配置(释放,调试|释放){
    LIBS += ...
}

Useful Functions

Qmake有许多嵌入式功能,增加了更多的自动化.

第一个例子是 files() function. Assuming you have a code generation step that produces a variable number of source files. 以下是如何将它们全部包含在内 SOURCES:

SOURCES += $$files(生成/* . ..c)

这将找到所有扩展名为这个的文件 .c in sub-folder generated and add them to the SOURCES variable.

第二个示例与前一个示例类似, but now the code generation produced a text file containing output file names (list of files):

来源+= $$cat(生成/文件列表,行)

这将读取文件内容,并将每行视为的条目 SOURCES.

注:完整的嵌入式函数列表可以在这里找到: http://doc.qt.io/qt-5/qmake-function-reference.html

将警告视为错误

下面的代码片段使用了前面描述的条件作用域特性:

*g++*: QMAKE_CXXFLAGS += -Werror
*msvc*: QMAKE_CXXFLAGS += /WX

The reason for this complication is because MSVC has a different flag to enable this option.

生成Git版本

The following snippet is useful when you need to create a preprocessor definition containing the current SW version obtained from Git:

定义+= SW_VERSION=\\\"$$system(git describe——always——abbrev=0)\\\"

这适用于任何平台,只要 git 命令可用. If you use Git tags, then this will peek the most recent tag, even though the branch went ahead. Modify the git describe 命令,以获得您选择的输出.

Conclusion

Qmake is a great tool that is focused on building your cross-platform Qt-based projects. In this guide, we reviewed the basic tool usage and the most commonly used patterns that will keep your project structure flexible and build specification easy to read and maintain.

想学习如何使你的Qt应用程序看起来更好? Try: How to Get Rounded Corner Shapes In C++ Using Bezier Curves and QPainter: A Step by Step Guide

了解基本知识

  • Qt是跨平台的吗?

    Qt is a cross-platform application development framework for desktop, embedded, and mobile. 支持的平台包括Linux, OS X, Windows, VxWorks, QNX, Android, iOS, BlackBerry, Sailfish OS, and others.

  • ​​What is Qt C++?

    Qt是用c++编写的, and therefore, C++ is the best programming language that allows it to gain all benefits of the framework. 但是,也有针对Python (http://wiki)等其他语言的绑定.python.org/moin/PyQt).

  • 什么是Qt开源?

    Qt has a rich licensing model that includes Commercial, LGPL3, and GPLv2 (aka open-source).

  • 什么是Qt应用程序?

    Qt应用程序是使用Qt框架构建的软件.

  • CMake和Make的区别是什么?

    ​CMake and Make have many ideas in common, but CMake is a much more advanced tool. 但本质上这两者的目的是一样的.

  • CMake是编译器吗?

    CMake是另一个被社区广泛使用的构建工具.CMake本身不是编译器.

  • 我该怎么办?

    Qmake根据项目文件中的信息生成Makefile. 项目文件由开发人员创建,通常很简单, 但是可以为复杂的项目创建更复杂的项目文件.

就这一主题咨询作者或专家.
Schedule a call
安德烈·斯米尔诺夫的头像
Andrei Smirnov

Located in Ankara, Turkey

Member since December 11, 2014

About the author

Andrei在微软这样的公司工作了15年以上, EMC, Motorola, 以及德意志银行(Deutsche Bank)的手机业务, desktop, and web using C++, C#, and JS.

Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

Previously At

Microsoft

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.