本页内容

CMake 入门

CMake 是一组用于构建、测试和打包应用程序的工具。与 Qt 一样,它可在所有主流开发平台上使用。此外,它还受到多种集成开发环境(IDE)的支持,包括 Qt Creator 和Visual Studio Code。

在本节中,我们将演示在 CMake 项目中使用 Qt 的最基本方法。首先,我们创建一个基本的控制台应用程序。然后,我们将该项目扩展为一个使用 Qt GUI 的应用程序。 Qt Widgets。

如果您想了解如何使用 Qt 构建现有的 CMake 项目,请参阅关于如何在命令行上使用 CMake 构建项目的文档。

若要学习 CMake 的入门基础知识,请在 Qt Academy 中学习《使用 CMake 构建:CMake 与 Qt 入门》课程。

构建 C++ 控制台应用程序

CMake 项目由使用CMake语言编写的文件定义。主文件名为CMakeLists.txt ,通常与实际程序源代码位于同一目录下。

以下是一个使用 Qt XML 编写 C++ 控制台应用程序的典型CMakeLists.txt 文件:

cmake_minimum_required(VERSION 3.16)

project(helloworld VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Core)

qt_standard_project_setup()

qt_add_executable(helloworld
    main.cpp
)

target_link_libraries(helloworld PRIVATE Qt6::Core)

让我们来详细分析一下其内容。

cmake_minimum_required(VERSION 3.16)

cmake_minimum_required() 指定了成功配置项目所需的最低 CMake 版本。有关 Qt 所需的最低版本,请参阅“支持的 CMake 版本”。

project(helloworld VERSION 1.0.0 LANGUAGES CXX)

project() 设置项目名称和默认项目版本。LANGUAGES 参数用于告知CMake该程序是用C++编写的。

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

Qt 6 要求编译器支持 C++ 17 版或更高版本。通过设置CMAKE_CXX_STANDARD 和CMAKE_CXX_STANDARD_REQUIRED 变量来强制执行此要求,如果编译器版本过旧,CMake 将输出错误信息。

find_package(Qt6 REQUIRED COMPONENTS Core)

这会指示 CMake 查找 Qt 6 并使Core 模块可用。如果CMake 无法定位该模块,则继续执行毫无意义,因此我们设置REQUIRED 标志,以便在此情况下让 CMake 终止执行。有关更多信息,请参阅《在 CMake 项目中启用 Qt》。

如果成功,该模块将设置一些 CMake 变量,相关说明详见“模块变量”。此外,它还会导入我们下面将使用的Qt6::Core 目标。

qt_standard_project_setup()

qt_standard_project_setup()命令为典型的 Qt 应用程序设置项目范围内的默认值。

除其他功能外,该命令还将CMAKE_AUTOMOC 变量设置为ON ,这会指示CMake自动配置规则,以便在需要时透明地调用Qt的Meta-Object Compiler (moc)。

详情请参阅qt_standard_project_setup() 的参考文档。

qt_add_executable(helloworld
    main.cpp
)

qt_add_executable()告知 CMake,我们希望将名为helloworld 的可执行文件(而非库)作为目标进行构建。 该函数是对内置命令 `add_executable() ` 的封装,并提供了额外的逻辑,用于自动处理静态 Qt 构建中 Qt 插件的链接、库名的平台特定定制等事项。

该目标应基于 C++ 源文件main.cpp 进行构建。

通常情况下,此处无需列出头文件。这与qmake 不同——在qmake 中,必须显式列出头文件,以便由Meta-Object Compiler (moc)进行处理。

有关创建库的信息,请参阅qt_add_library()。

target_link_libraries(helloworld PRIVATE Qt6::Core)

最后,target_link_libraries 会告知 CMake,helloworld 可执行文件通过 Qt Core ,这是通过引用上述find_package() 调用所导入的Qt6::Core 目标实现的。这不仅会向链接器添加正确的参数,还能确保将正确的包含目录和编译器定义传递给 C++ 编译器。 对于可执行目标而言,PRIVATE 关键字并非严格必要,但指定它是一种良好的编程习惯。如果helloworld 是一个库而非可执行文件,则应指定PRIVATE 或PUBLIC (如果该库的头文件中引用了Qt6::Core 中的内容,则指定PUBLIC ;否则指定PRIVATE )。

构建 C++ GUI 应用程序

在上一节中,我们展示了一个简单控制台应用程序的 CMakeLists.txt 文件。现在,我们将创建一个使用 Qt Widgets 模块。

以下是完整的项目文件:

cmake_minimum_required(VERSION 3.16)

project(helloworld VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Widgets)

qt_standard_project_setup()

qt_add_executable(helloworld
    mainwindow.ui
    mainwindow.cpp
    main.cpp
)

target_link_libraries(helloworld PRIVATE Qt6::Widgets)

set_target_properties(helloworld PROPERTIES
    WIN32_EXECUTABLE ON
    MACOSX_BUNDLE ON
)

让我们逐一回顾我们所做的修改。

find_package(Qt6 REQUIRED COMPONENTS Widgets)

在find_package 调用中,我们将Core 替换为Widgets 。这将定位Qt6Widgets 模块,并提供我们稍后用于链接的Qt6::Widgets 目标。

qt_standard_project_setup()

除了CMAKE_AUTOMOC 之外,qt_standard_project_setup()还会将CMAKE_AUTOUIC 变量设置为ON 。这将自动生成规则,用于对.ui 源文件调用 Qt 的User Interface Compiler (uic)。

qt_add_executable(helloworld
    mainwindow.ui
    mainwindow.cpp
    main.cpp
)

我们向 Qt Widgets Designer 文件(mainwindow.ui )及其对应的 C++ 源文件(mainwindow.cpp )添加到应用程序目标的源文件中。

注意: 将.ui 文件添加到项目中的另一种 方法是使用命令qt_add_ui()代替AUTOUIC 。

target_link_libraries(helloworld PRIVATE Qt6::Widgets)

在target_link_libraries 命令中,我们链接的是Qt6::Widgets 而不是Qt6::Core 。请注意,应用程序仍会链接Qt6::Core ,因为Qt6::Widgets 依赖于它。

set_target_properties(helloworld PROPERTIES
    WIN32_EXECUTABLE ON
    MACOSX_BUNDLE ON
)

最后,我们在应用程序目标上设置了一些属性,其效果如下:

  • 防止在 Windows 上创建控制台窗口。
  • 在 macOS 上创建应用程序包。

有关这些目标属性的更多信息,请参阅CMake 文档。

项目结构

对于包含多个目标的项目,采用清晰的项目文件结构将大有裨益。我们将使用 CMake 的子目录功能。

鉴于我们计划通过添加更多目标来扩展该项目,我们将应用程序的源文件移至一个子目录中,并在其中创建一个新的CMakeLists.txt 文件。

<project root>
├── CMakeLists.txt
└── src
    └── app
        ├── CMakeLists.txt
        ├── main.cpp
        ├── mainwindow.cpp
        ├── mainwindow.h
        └── mainwindow.ui

顶级CMakeLists.txt 文件包含整体项目配置,以及find_package 和add_subdirectory 的调用:

cmake_minimum_required(VERSION 3.16)

project(helloworld VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()

add_subdirectory(src/app)

在此文件中设置的变量在子目录中的项目文件中可见。

应用程序的项目文件src/app/CMakeLists.txt 包含可执行目标:

qt_add_executable(helloworld
    mainwindow.ui
    mainwindow.cpp
    main.cpp
)

target_link_libraries(helloworld PRIVATE Qt6::Widgets)

set_target_properties(helloworld PROPERTIES
    WIN32_EXECUTABLE ON
    MACOSX_BUNDLE ON
)

这种结构使得向项目中添加更多目标(例如库或单元测试)变得非常容易。

注意:请将 您的项目构建目录添加 到系统上运行的任何防病毒软件的“排除目录”列表中。

构建库

随着项目规模的扩大,您可能希望将应用程序代码的一部分转换为库,供应用程序及单元测试使用。本节将介绍如何创建此类库。

目前,我们的应用程序将业务逻辑直接包含在main.cpp 中。我们将代码提取到名为businesslogic 的新静态库中,该库位于"src/businesslogic" 子目录下,具体操作如上一节所述。

为简化起见,该库仅包含一个 C++ 源文件及其对应的头文件,该头文件被应用程序中的main.cpp 文件所包含:

<project root>
├── CMakeLists.txt
└── src
    ├── app
    │   ├── ...
    │   └── main.cpp
    └── businesslogic
        ├── CMakeLists.txt
        ├── businesslogic.cpp
        └── businesslogic.h

让我们来看看该库的项目文件(src/businesslogic/CMakeLists.txt )。

qt_add_library(businesslogic STATIC
    businesslogic.cpp
)
target_link_libraries(businesslogic PRIVATE Qt6::Core)
target_include_directories(businesslogic INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

让我们逐一查看其中的内容。

qt_add_library(businesslogic STATIC
    businesslogic.cpp
)

add_library命令会生成名为businesslogic 的库。稍后,我们将让应用程序与该目标进行链接。

关键字STATIC 表示静态库。如果我们要创建共享库或动态库,则应使用关键字SHARED 。

target_link_libraries(businesslogic PRIVATE Qt6::Core)

我们有一个静态库,实际上并不需要链接其他库。但由于我们的库使用了QtCore 中的类,因此我们向Qt6::Core 添加了一个链接依赖。这会引入必要的QtCore 包含路径和预处理器定义。

target_include_directories(businesslogic INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

该库的 API 在头文件businesslogic/businesslogic.h 中定义。通过调用target_include_directories,我们可以确保businesslogic 目录的绝对路径会自动作为包含路径添加到所有使用该库的目标中。

这使我们在main.cpp 中无需使用相对路径来定位businesslogic.h 。取而代之,我们只需编写

#include <businesslogic.h>

最后,我们必须将库的子目录添加到顶级项目文件中:

add_subdirectory(src/app)
add_subdirectory(src/businesslogic)

使用库

要使用上一节中创建的库,我们需要指示 CMake 将其链接进来:

target_link_libraries(helloworld PRIVATE
    businesslogic
    Qt6::Widgets
)

这可确保在编译 main.cpp 时能找到businesslogic.h 。此外,businesslogic 静态库将成为helloworld 可执行文件的一部分。

在 CMake 的术语中,库businesslogic 规定了使用要求(即包含路径),我们库的每个调用者(应用程序)都必须满足这些要求。target_link_libraries 命令会自动处理这一点。

添加资源

我们希望在应用程序中显示一些图片,因此使用Qt 资源系统来添加它们。

qt_add_resources(helloworld imageresources
    PREFIX "/images"
    FILES logo.png splashscreen.png
)

qt_add_resources()命令会自动创建一个包含所引用图片的 Qt 资源。在 C++ 源代码中,您可以通过在图片前添加指定的资源前缀来访问这些图片:

logoLabel->setPixmap(QPixmap(":/images/logo.png"));

qt_add_resources()命令的第一个参数可以是变量名,也可以是目标名。我们建议使用如上例所示的基于目标的命令形式。

添加翻译

Qt 项目中字符串的翻译内容以.ts 文件的形式编码。这些.ts 文件会被编译成二进制.qm 文件,随后在运行时由 Qt 应用程序加载。详情请参阅《Qt 国际化》。

本节介绍如何为helloworld 应用程序添加德语和法语翻译。

使用qt_standard_project_setup() 同时指定这两种语言:

qt_standard_project_setup(I18N_TRANSLATED_LANGUAGES de fr)

然后在需要加载.qm 文件的目标上调用qt_add_translations():

qt_add_translations(helloworld)

在首次配置时,此命令会在项目的源代码目录下创建文件helloworld_de.ts 和helloworld_fr.ts 。这些文件将包含已翻译的字符串,应纳入版本控制。

该命令还会创建构建系统规则,用于根据.ts 文件自动生成.qm 文件。默认情况下,.qm 文件会被嵌入到资源中,并可通过"/i18n" 资源前缀进行访问。

要更新.ts 文件中的条目,请构建update_translations 目标:

$ cmake --build . --target update_translations

若要手动触发.qm 文件的生成,请构建release_translations 目标:

$ cmake --build . --target release_translations

有关如何影响.ts 文件的处理以及将其嵌入到资源中的更多信息,请参阅qt_add_translations文档。

qt_add_translations()命令是一个便捷的封装函数。若需更精细地控制.ts 文件和.qm 文件的处理方式,请使用底层命令qt_add_lupdate()和qt_add_lrelease()。

进一步阅读

官方CMake 文档是使用 CMake 时不可或缺的资源。

官方CMake 教程涵盖了构建系统中的常见任务。

《专业CMake:实用指南》一书对最相关的CMake功能进行了精彩的介绍。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.