# 构建系统

跨不同平台可靠地构建软件可能是一项复杂的任务。 将遇到具有不同编译器、路径和库变体的不同环境。 Qt的目的是保护应用程序开发人员免受这些跨平台问题的影响。 Qt依靠CMake (opens new window)将项目文件CMakeLists.txt转换为特定于平台的make文件,然后可以使用特定于平台的工具构建这些文件。

提示

Qt 带有三种不同的构建系统。 最初的Qt构建系统被称为qmake;另一个特定于Qt的构建系统是QBS,它使用声明性方法来描述构建序列;从版本6开始,Qt已经从qmake转移到将CMake作为官方构建系统。

Unix下Qt的典型构建流程是:

vim CMakeLists.txt
cmake . // generates Makefile
make

使用Qt,鼓励使用影子构建。 影子构建是在源代码位置之外进行构建。 假设有一个带有CMakeLists.txt文件的myproject文件夹。 流程如下:

mkdir build
cd build
cmake ..

创建一个构建文件夹,然后使用项目文件夹的位置从构建文件夹中调用cmake。 这将以所有构建工件都存储在构建文件夹而不是源代码文件夹中的方式设置生成文件。 这允许同时为不同的qt版本和构建配置创建构建,而且它不会弄乱源代码文件夹,这总是一件好事。

当使用Qt Creator时,它会在后台完成这些事情,在大多数情况下不必担心这些步骤。 对于较大的项目或者想更深入地了解流程,建议学习从命令行构建Qt项目,以确保可以完全控制正在发生的事情。

# CMake

CMake是由Kitware创建的工具。 Kitware以其3D可视化软件VTK和跨平台makefile生成器CMake而闻名。 它使用一系列CMakeLists.txt文件来生成特定于平台的makefile。 CMake被KDE项目使用,因此与Qt社区有着特殊的关系,并且从版本6开始,它是构建Qt项目的首选方式。

CMakeLists.txt是用于存储项目配置的文件。 对于使用Qt Core的简单'hello world'项目,其项目文件如下所示:

// 确保cmake的最低版本是3.16.0
cmake_minimum_required(VERSION 3.16.0)

// 定义一个带有版本的项目
project(foundation_tests VERSION 1.0.0 LANGUAGES CXX)

// 选择使用的C++的标准,这里是C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

// 告诉CMake自动运行Qt工具moc、rcc、uic
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

// 配置Qt6的core、test模块
find_package(Qt6 COMPONENTS Core REQUIRED)
find_package(Qt6 COMPONENTS Test REQUIRED)

// 定义一个从源文件构建的可执行文件 
add_executable(foundation_tests
    tst_foundation.cpp
)

// 告诉cmake将可执行文件链接到Qt 6的core和test模块
target_link_libraries(foundation_tests PRIVATE Qt6::Core Qt6::Test)

这将使用tst_foundation.cpp构建一个名为foundations_tests的可执行文件,并链接到Qt 6中的Core和Test库。可以在本书中找到更多的CMake文件示例,将CMake用于所有基于C++的示例。

CMake是一个强大而复杂的工具,需要花费一些时间来适应它的语法。 CMake非常灵活,在大型复杂项目中表现的非常出色。

# 参考

# QMake

QMake是读取项目文件并生成构建文件的工具。 项目文件是项目配置、外部依赖项和源文件的简化记录。 最简单的项目文件大概是下面这样的:

// myproject.pro

SOURCES += main.cpp

在这里,根据项目文件名构建一个名为myproject的可执行应用程序。 该构建将仅包含main.cpp源文件。 默认情况下,将为此项目使用QtCoreQtGui模块。 如果项目是一个QML应用程序,需要将QtQuickQtQml模块添加到列表中:

// myproject.pro

QT += qml quick

SOURCES += main.cpp

现在构建文件知道要链接到Qt的QtQmlQtQuick模块。 QMake使用=+=-=的概念分别从选项列表中分配、添加、删除元素。 对于没有UI依赖项的纯控制台构建,将删除QtGui模块:

// myproject.pro

QT -= gui

SOURCES += main.cpp

当想要构建库而不是应用程序时,需要更改构建模板:

// myproject.pro
TEMPLATE = lib

QT -= gui

HEADERS += utils.h
SOURCES += utils.cpp

现在,该项目将构建一个没有UI依赖项的库,它使用头文件utils.h和源文件utils.cpp。 库的格式将取决于正在构建项目的操作系统。

通常会有更复杂的设置并且需要构建一组项目。 为此,qmake提供了subdirs模板。 假设有一个mylib和一个myapp项目,那么设置可能是如下的样子:

my.pro
mylib/mylib.pro
mylib/utils.h
mylib/utils.cpp
myapp/myapp.pro
myapp/main.cpp

已经知道mylib.pro和myapp.pro的样子。 作为总体项目文件的my.pro如下所示:

// my.pro
TEMPLATE = subdirs

subdirs = mylib \
    myapp

myapp.depends = mylib

这声明了一个包含两个子项目的项目:mylibmyapp,其中myapp依赖于mylib。 当在此项目文件上运行qmake时,它将为相应文件夹中的每个项目生成文件一个构建文件。 当运行my.pro的makefile时,所有子项目也会被构建。

有时需要根据配置在一个平台上做一件事,而在其他平台上做另一件事。 为此qmake引入了作用域的概念。 当配置选项设置为true时应用作用域。

例如,要使用特定于Unix的utils实现,可以使用:

unix {
    SOURCES += utils_unix.cpp
} else {
    SOURCES += utils.cpp
}

它的意思是如果CONFIG变量包含Unix选项,则应用此作用域,否则使用else路径下作用域。 一个典型的应用就是去掉mac下的应用捆绑:

macx {
    CONFIG -= app_bundle
}

这会将应用程序创建为mac下的普通可执行文件,而不是用于安装应用程序的.app文件夹。

当开始编写Qt应用程序时,基于QMake的项目通常是第一选择。 除此之外,还有其他选择,他们都各有其优点和缺点。 接下来,将很快讨论这些其他选项。

# 参考

最后更新: 2/3/2022, 4:21:42 PM