CMake 快速入門

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.

此節將展示在 CMake 工程中,使用 Qt 的最基本方式。首先,創建基本控製颱應用程序。然後,將工程擴展成 GUI 應用程序使用 Qt Widgets .

若想要知道如何采用 Qt 構建現有 CMake 工程,見文檔編製 如何在命令行中采用 CMake 構建工程 .

要學習 CMake 快速入門基礎,接受 采用 Cmake 構建:CMake 和 Qt 快速入門 課程在 Qt 學院。

構建 C++ 控製颱應用程序

A CMake 工程是以 CMake 語言編寫的文件定義。main 文件稱為 CMakeLists.txt ,通常放在實際程序源代碼的相同目錄下。

這裏是典型 CMakeLists.txt 文件用於使用 Qt 以 C++ 編寫的控製颱應用程序:

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() specifies the minimum CMake version that is required to successfully configure the project. See 支持的 CMake 版本 for the minimum version required by Qt.

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++ 第 2017 或更高版本。實施這是通過設置 CMAKE_CXX_STANDARD , CMAKE_CXX_STANDARD_REQUIRED 變量將使 CMake 打印錯誤,若編譯器太舊。

find_package(Qt6 REQUIRED COMPONENTS Core)
					

這告訴 CMake 去查找 Qt 6,並導入 Core 模塊。繼續沒有意義若 CMake 無法定位模塊,因此,設置 REQUIRED 標誌以使 CMake 中止 (在這種情況下)。

若成功,模塊將設置一些 CMake 文檔化變量在 模塊變量 。此外,它還導入 Qt6::Core 目標 (使用於下文)。

For find_package to be successful, CMake must find the Qt installation. Qt Creator and qt-cmake ( qt-cmake.bat on Windows) handle this transparently.

When using vanilla CMake directly, 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. This is a directory where Qt binaries are installed. On Linux, it usually looks something like this: "~/Qt/6.8.5/gcc_64" and you would pass it on the command line as -DCMAKE_PREFIX_PATH=$HOME/Qt/6.8.5/gcc_64 .

注意: When cross-compiling (compiling for a platform other than the one you are on, such as WebAssembly or Android) and when using vanilla cmake , set CMAKE_TOOLCHAIN_FILE 而不是 CMAKE_PREFIX_PATH . On Linux, the toolchain file (specific to a particular target platform) is typically located at a path similar to this: "~/Qt/6.8.5/wasm_singlethread/lib/cmake/Qt6/qt.toolchain.cmake" . It sets the required variables like CMAKE_PREFIX_PATH , CMAKE_FIND_ROOT_PATH ,和 QT_HOST_PATH .

qt_standard_project_setup()
					

The qt_standard_project_setup command sets project-wide defaults for a typical Qt application.

除其它事情外,此命令設置 CMAKE_AUTOMOC 變量到 ON ,指導 CMake 自動設置規則,以便 Qt 的 MOC (元對象編譯器) is called transparently, when required.

qt_standard_project_setup 參考瞭解細節。

qt_add_executable(helloworld
    main.cpp
)
					

qt_add_executable() 告訴 CMake 想要構建的可執行文件 (因此不是庫) 稱為 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.

應構建目標從 C++ 源文件 main.cpp .

Typically, you do not list header files here. This is different from qmake ,需要明確列齣頭文件,以便處理它們通過 MOC (元對象編譯器) .

為創建庫,見 qt_add_library .

target_link_libraries(helloworld PRIVATE Qt6::Core)
					

最後, target_link_libraries 告訴 CMake helloworld 可執行文件利用 Qt Core 通過引用 Qt6::Core 目標,導入通過 find_package() 調用 (見上文)。這不僅將正確自變量添加到鏈接器,且還確保將正確包括目錄、編譯器定義傳遞給 C++ 編譯器。 PRIVATE 關鍵字對於可執行目標不是嚴格必要的,但指定它是良好實踐。若 helloworld 是庫而不是可執行文件,那麼 PRIVATE or PUBLIC 應該指定 ( PUBLIC 若庫提到的任何東西來自 Qt6::Core 在其頭中, PRIVATE 否則)。

構建 C++ GUI 應用程序

last section we showed the CMakeLists.txt file for a simple console application. We will now create a GUI application that uses the 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 with Widgets 。這會定位 Qt6Widgets 模塊並提供 Qt6::Widgets 目標,我們稍後鏈接到。

注意,應用程序仍會鏈接 Qt6::Core ,因為 Qt6::Widgets 從屬它。

qt_standard_project_setup()
					

除瞭 CMAKE_AUTOMOC , qt_standard_project_setup 設置 CMAKE_AUTOUIC 變量到 ON 。這會自動創建規則以援引 Qt 的 uic (用戶界麵編譯器) on .ui 源文件。

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

我們添加 Qt Widgets Designer 文件 ( mainwindow.ui ) 及其相應 C++ 源文件 ( mainwindow.cpp ) 到應用程序目標的源。

In specific cases, such as the example below where the include directive uses a relative path, qt_add_ui can be used to generate the ui_calculatorform.h file instead of relying on AUTOUIC .

When to prefer qt_add_ui over AUTOUIC

#include "src/files/ui_mainwindow.h"
					
qt_add_ui(calculatorform SOURCES mainwindow.ui INCLUDE_PREFIX src/files)
					

qt_add_ui is used, no need to pass mainwindow.uiqt_add_executable 命令。

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 文檔編製 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 子目錄特徵 .

由於我們計劃采用更多目標擴展工程,所以將應用程序源文件移到子目錄並創建新的 CMakeLists.txt in there.

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

頂層 CMakeLists.txt 包含整體工程設置, find_package and 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)
					

Variables that are set in this file are visible in subdirectory project files.

應用程序工程文件 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
)
					

Such a structure will make it easy to add more targets to the project such as libraries or unit tests.

注意: Add your project build directory to the list of excluded directories of any anti-virus application that runs on your system.

構建庫

隨著工程增長,可能想要將部分應用程序代碼轉換成用於應用程序且可能單元測試的庫。此節展示如何創建這種庫。

我們的應用程序目前將業務邏輯直接包含在 main.cpp 。我們將代碼提取到的新靜態庫稱為 businesslogic 在子目錄 "src/businesslogic" as explained in the 上一章節 .

為簡單起見,庫僅僅包含一個 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
)
					

The add_library command creates the library businesslogic 。稍後,將讓應用程序鏈接到此目標。

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 ,添加依賴鏈接到 Qt6::Core 。這會壓入必要 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 。通過調用 target_include_directories ,確保絕對路徑 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 。取而代之,可以僅僅編寫

#include <businesslogic.h>
					

最後,必須將庫的子目錄添加到頂層工程文件中:

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

使用庫

To use the library we created in the 上一章節 ,我們指導 CMake 鏈接到它:

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 可執行文件。

在 CMake 術語中,庫 businesslogic specifies usage requirements (include 路徑) 庫 (應用程序) 的每個消費者都必須滿足。 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. These .ts files are compiled into binary .qm files, which are then loaded by the Qt application at run time. See Qt 國際化 瞭解細節。

This section describes how to add a German and French translation to the helloworld 應用程序。

Specify both languages with qt_standard_project_setup :

qt_standard_project_setup(I18N_TRANSLATED_LANGUAGES de fr)
					

Then call qt_add_translations on the target that shall load the .qm files:

qt_add_translations(helloworld)
					

On the first configuration, this command creates the files helloworld_de.ts and helloworld_fr.ts source directory of the project. These files will contain the translated strings and are supposed to be put under version control.

The command also 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
					

對於更多信息有關如何影響處理 .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 .

延伸閱讀

官方 CMake 文檔編製 是使用 CMake 的寶貴資源。

官方 CMake 教程 covers common build system tasks.

書籍 專業 CMake:實踐指南 提供 CMake 最相關特徵的很好介紹。