开始使用 CMake
CMake
是一组允许构建、测试和打包应用程序的工具。就像 Qt 一样,它适用于所有主要的开发平台。各种集成开发环境也支持 CMake,包括 Qt Creator.
在本节中,我们将展示在 CMake 项目中使用 Qt 的最基本方法。首先,我们创建一个基本的控制台应用程序。然后,我们将该项目扩展为一个 GUI 应用程序,使用 Qt Widgets.
如果您想了解如何使用 Qt 构建现有的 CMake 项目,请参阅有关如何在命令行上使用 CMake 构建项目的文档。
要学习 CMake 的基础入门知识,请参加 "使用 Cmake 构建 "课程:Qt Academy 的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()
设置项目名称和默认项目版本。 参数告诉 CMake 程序是用 C++ 编写的。LANGUAGES
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。
如果成功,模块将设置Module variables 中记录的一些 CMake 变量。此外,它还会导入我们在下文中使用的Qt6::Core
target。
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
会告诉 CMakehelloworld
可执行文件使用了 Qt Core调用find_package()
导入的Qt6::Core
目标。这不仅会向链接器添加正确的参数,还会确保向 C++ 编译器传递正确的包含目录和编译器定义。PRIVATE
关键字对于可执行目标而言并非严格必要,但指定它是一种良好做法。如果helloworld
是库而不是可执行文件,则应指定PRIVATE
或PUBLIC
(如果库的头文件中提到Qt6::Core
中的内容,则应指定PUBLIC
,否则应指定PRIVATE
)。
构建 C++ GUI 应用程序
在上一节中,我们展示了一个简单控制台应用程序的 CMakeLists.txt 文件。现在我们将创建一个 GUI 应用程序,使用 Qt Widgets模块的 GUI 应用程序。
这是完整的项目文件:
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
目标。
请注意,应用程序仍将链接Qt6::Core
,因为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
进行链接。
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 XML 项目中的字符串翻译是以.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。
更多阅读
Professional CMake: A Practical Guide》一书对最相关的 CMake 功能做了很好的介绍。
© 2025 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.