CMake
is a group of tools that allow to build, test, and package applications. Just like Qt, it is available on all major development platforms. It is also supported by various IDE's, including
Qt Creator
.
In this section we will show the most basic way to use Qt in a CMake project. First, we create a basic console application. Then, we extend the project into a GUI application that uses Qt Widgets .
If you want to know how to build an existing CMake project with Qt, see the documentation on how to build projects with CMake on the command line .
A
CMake
project is defined by files written in the CMake language. The main file is called
CMakeLists.txt
, and is usually placed in the same directory as the actual program sources.
Here is a typical
CMakeLists.txt
file for a console application written in C++ using Qt:
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)
Let's go through the content.
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required()
specifies the minimum CMake version that the application requires. Qt itself requires at least CMake version 3.16. If you use a Qt that was built statically - the default in
Qt for iOS
and
Qt for WebAssembly
- you need CMake 3.21.1 or newer.
project(helloworld VERSION 1.0.0 LANGUAGES CXX)
project()
sets a project name and the default project version. The
LANGUAGES
argument tells CMake that the program is written in C++.
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
Qt 6 requires a compiler supporting C++ version 17 or newer. Enforcing this by setting the
CMAKE_CXX_STANDARD
,
CMAKE_CXX_STANDARD_REQUIRED
variables will let CMake print an error if the compiler is too old.
find_package(Qt6 REQUIRED COMPONENTS Core)
This tells CMake to look up Qt 6, and import the
核心
module. There is no point in continuing if
CMake
cannot locate the module, so we do set the
REQUIRED
flag to let CMake abort in this case.
If successful, the module will set some CMake variables documented in
模块变量
. It furthermore imports the
Qt6::Core
target that we use below.
For
find_package
要取得成功,
CMake
must find the Qt installation. There are different ways you can tell
CMake
about Qt, but the most common and recommended approach is to set the CMake cache variable
CMAKE_PREFIX_PATH
to include the Qt 6 installation prefix. Note that
Qt Creator
will handle this transparently for you.
qt_standard_project_setup()
The qt_standard_project_setup command sets project-wide defaults for a typical Qt application.
Among other things, this command sets the
CMAKE_AUTOMOC
变量到
ON
, which instructs CMake to automatically set up rules so that Qt's
MOC (元对象编译器)
is called transparently, when required.
见 qt_standard_project_setup 's reference for details.
qt_add_executable(helloworld
main.cpp
)
qt_add_executable()
tells CMake that we want to build an executable (so not a library) called
helloworld
as a target. It is a wrapper around the built-in
add_executable()
command, and provides additional logic to automatically handle things like linking of Qt plugins in static Qt builds, platform-specific customization of library names, and so on.
The target should be built from the C++ source file
main.cpp
.
Typically, you do not list header files here. This is different from qmake , where header files need to be explicitly listed so that they are processed by the MOC (元对象编译器) .
For creating libraries, see qt_add_library .
target_link_libraries(helloworld PRIVATE Qt6::Core)
最后,
target_link_libraries
tells CMake that the
helloworld
executable makes use of
Qt Core
by referencing the
Qt6::Core
target imported by the
find_package()
call above. This will not only add the right arguments to the linker, but also makes sure that the right include directories, compiler definitions are passed to the C++ compiler. The
PRIVATE
keyword is not strictly necessary for an executable target, but it is good practice to specify it. If
helloworld
was a library rather than an executable, then either
PRIVATE
or
PUBLIC
should be specified (
PUBLIC
if the library mentions anything from
Qt6::Core
in its headers,
PRIVATE
otherwise).
在 last section we showed the CMakeLists.txt file for a simple console application. We will now extend it to create a GUI application that uses the Qt Widgets 模块。
This is the full project file:
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
)
Let's walk through the changes we have made.
find_package(Qt6 REQUIRED COMPONENTS Widgets)
在
find_package
call, we replace
核心
with
小部件
. This will locate the
Qt6Widgets
module and provide the
Qt6::Widgets
targets we later link against.
Note that the application will still link against
Qt6::Core
,因为
Qt6::Widgets
depends on it.
qt_standard_project_setup()
除了
CMAKE_AUTOMOC
,
qt_standard_project_setup
sets the
CMAKE_AUTOUIC
变量到
ON
. This will automatically create rules to invoke Qt's
uic (用户界面编译器)
on
.ui
源文件。
qt_add_executable(helloworld
mainwindow.ui
mainwindow.cpp
main.cpp
)
我们添加
Qt Designer
file (
mainwindow.ui
) and its corresponding C++ source file (
mainwindow.cpp
) to the application target's sources.
target_link_libraries(helloworld PRIVATE Qt6::Widgets)
在
target_link_libraries
command, we link against
Qt6::Widgets
而不是
Qt6::Core
.
set_target_properties(helloworld PROPERTIES
WIN32_EXECUTABLE ON
MACOSX_BUNDLE ON
)
Finally, we set properties on our application target with the following effects:
见 CMake Documentation for more information about these target properties.
Projects that contain more than just one target will benefit from a clear project file structure. We will use CMake's subdirectory feature .
As we plan to extend the project with more targets, we move the source files of the application into a subdirectory and create a new
CMakeLists.txt
in there.
<project root>
├── CMakeLists.txt
└── src
└── app
├── CMakeLists.txt
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
└── mainwindow.ui
The top-level
CMakeLists.txt
contains the overall project setup,
find_package
and
add_subdirectory
calls:
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)
Variables that are set in this file are visible in subdirectory project files.
The application's project file
src/app/CMakeLists.txt
contains the executable target:
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
)
Such a structure will make it easy to add more targets to the project such as libraries or unit tests.
As the project grows, you may want to turn parts of your application code into a library that is used by the application and possibly unit tests. This section shows how to create such a library.
Our application currently contains business logic directly in
main.cpp
. We extract the code into a new static library called
businesslogic
in the subdirectory
"src/businesslogic"
as explained in the
previous section
.
For the sake of simplicity, the library consists of just one C++ source file and its corresponding header file that is included by the application's
main.cpp
:
<project root>
├── CMakeLists.txt
└── src
├── app
│ ├── ...
│ └── main.cpp
└── businesslogic
├── CMakeLists.txt
├── businesslogic.cpp
└── businesslogic.h
Let's have a look at the library's project file (
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})
Let's go through the content.
qt_add_library(businesslogic STATIC
businesslogic.cpp
)
The
add_library
command creates the library
businesslogic
. Later, we will let the application link against this target.
The
STATIC
keyword denotes a static library. If we wanted to create a shared or dynamic library, we would use the
SHARED
关键词。
target_link_libraries(businesslogic PRIVATE Qt6::Core)
We have a static library and don't actually have to link other libraries. But as our library uses classes from
QtCore
, we add a link dependency to
Qt6::Core
. This pulls in the necessary
QtCore
include paths and preprocessor defines.
target_include_directories(businesslogic INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
The library API is defined in the header file
businesslogic/businesslogic.h
. By calling
target_include_directories
, we make sure that the absolute path to the
businesslogic
directory is automatically added as an include path to all targets using our library.
This frees us in
main.cpp
from using relative paths to locate
businesslogic.h
. Instead, we can just write
#include <businesslogic.h>
Last, we must add the library's subdirectory to the top-level project file:
add_subdirectory(src/app) add_subdirectory(src/businesslogic)
To use the library we created in the previous section , we instruct CMake to link against it:
target_link_libraries(helloworld PRIVATE
businesslogic
Qt6::Widgets
)
This ensures that
businesslogic.h
is found when main.cpp is compiled. Furthermore, the businesslogic static library will become a part of the
helloworld
executable.
In CMake terms, the library
businesslogic
specifies
usage requirements
(the include path) that every consumer of our library (the application) has to satisfy. The
target_link_libraries
command takes care of that.
We want to display some images in our application, so we add them using the Qt 资源系统 .
qt_add_resources(helloworld imageresources
PREFIX "/images"
FILES logo.png splashscreen.png
)
The qt_add_resources command automatically creates a Qt resource containing the referenced images. From the C++ source code, you can access the images by prepending the specified resource prefix:
logoLabel->setPixmap(QPixmap(":/images/logo.png"));
The qt_add_resources command takes as the first argument either a variable name or a target name. We recommend to use the target-based variant of this command as shown in the example above.
Translations of strings in a Qt project are encoded in
.ts
files. See
Qt 国际化
了解细节。
To add
.ts
files to your project, use the
qt_add_translations
命令。
The following example adds a German and a French translation file to the
helloworld
目标:
qt_add_translations(helloworld
TS_FILES helloworld_de.ts helloworld_fr.ts)
This creates build system rules to automatically generate
.qm
files from the
.ts
files. By default, the
.qm
files are embedded into a resource and are accessible under the
"/i18n"
resource prefix.
To update the entries in the
.ts
file, build the
update_translations
目标:
$ cmake --build . --target update_translations
To trigger the generation of the
.qm
files manually, build the
release_translations
目标:
$ cmake --build . --target release_translations
For more information about how to influence the handling of
.ts
files and the embedding into a resource, see the
qt_add_translations documentation
.
The
qt_add_translations
command is a convenience wrapper. For more fine-grained control of how
.ts
files and
.qm
files are handled, use the underlying commands
qt_add_lupdate
and
qt_add_lrelease
.
The official CMake Documentation is an invaluable source for working with CMake.
The official CMake Tutorial covers common build system tasks.
The book Professional CMake: A Practical Guide provides a great introduction to the most relevant CMake features.