CMake是一个开源、跨平台的工具系列,用于构建、测试和打包软件。CMake使用简单的独立配置文件来控制软件编译过程。与许多跨平台系统不同,CMake被设计为与本地构建环境结合使用。
下面我们在CMake项目中使用Qt的最基本方法。首先,创建一个基本的控制台应用程序。然后,将该项目扩展为使用Qt Widgets的GUI应用程序。
一、用CMake构建控制台应用程序,构建系统选择CMake(Qt6支持)
Qt Creator为我们自动生成了一个CMakeList.txt,如图:
CMakeList.txt是CMake项目的核心配置文件,位于项目根目录下,用于定义项目的构建规则和配置选项。通过编写CMake语法,可以在CMakeList.txt中定义项目名称、需要的源文件、依赖项、编译标志、安装规则等。这样可以实现跨平台的项目构建和管理,提供了灵活性和可扩展性。工程中自动生成的CMakeList内容如下:
cmake_minimum_required(VERSION 3.14) #指定构建应用程序最低的CMake版本,通常放在CMakeList最开始
project(TestCMakeConsole LANGUAGES CXX) #设定项目名称和默认版本,Languages参数用来告诉CMake程序是用C++写的
#用来告诉CMake工具自动处理UI文件、MOC文件和资源文件的指令
set(CMAKE_AUTOUIC ON) #开启自动生成UI头文件的功能,即对应UI文件(.ui)会生成对应的UI头文件,无需手动编写
set(CMAKE_AUTOMOC ON) #开启自动生成MOC文件的功能,即处理带有Q_OBJECT宏的QObject派生类的头文件,生成相应的MOC文件。MOC (Meta-Object Compiler) 是用于处理信号槽机制的工具
set(CMAKE_AUTORCC ON) #开启自动生成资源文件的功能,即处理.qrc文件,生成相应的资源文件
set(CMAKE_CXX_STANDARD 17) #指定需要C++17或者高的编译器支持
set(CMAKE_CXX_STANDARD_REQUIRED ON) #强制指定编译器支持,如果编译器太旧,CMake将打印错误
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) #查找Qt5或Qt6并导入Core模块,Required参数标志是告诉CMake这种查找是强制的,如果没找到则中止
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) #强制指定到的Qt版本是由QT_VERSION_MAJOR参数指定的版本,找到后导入Core模块
add_executable(TestCMakeConsole #告诉CMake我们想要构建一个名为TestCMakeConsole的可执行文件作为目标。目标应该从c++源文件main.cpp构建。注意,这里通常不列出头文件。
main.cpp
)
target_link_libraries(TestCMakeConsole Qt${QT_VERSION_MAJOR}::Core) #告诉CMake, TestCMakeConsole可执行文件通过引用上面find_package()调用导入的Qt6::Core目标来使用Qt Core。
include(GNUInstallDirs) #包含GNUInstallDirs模块,它定义了一些安装目录变量,方便在后续的安装过程中使用
#指定了一个名为TestCMakeConsole的目标,指定了在安装时将该目标生成的库文件安装到${CMAKE_INSTALL_LIBDIR}目录下,将生成的可执行文件安装到${CMAKE_INSTALL_BINDIR}目录下。
#这样可以确保在安装项目时,生成的文件会被安装到正确的目录下。
install(TARGETS TestCMakeConsole
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
二、用CMake构建GUI应用程序
同样,我们继续分析Qt Creator为Demo工程自动生成的CMakeList.txt。
cmake_minimum_required(VERSION 3.5) #指定构建应用程序最低的CMake版本为3.5,通常放在CMakeList最开始
project(testCMakeGui VERSION 0.1 LANGUAGES CXX) #设定项目名称和默认版本,Languages参数用来告诉CMake程序是用C++写的
set(CMAKE_AUTOUIC ON) #见上文
set(CMAKE_AUTOMOC ON) #见上文
set(CMAKE_AUTORCC ON) #见上文
set(CMAKE_CXX_STANDARD 17) #见上文
set(CMAKE_CXX_STANDARD_REQUIRED ON) #见上文
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) #见上文,不同的是基于QWidget的GUI应用我们需要导入Widgets模块
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) #见上文
set(PROJECT_SOURCES #通过设置PROJECT_SOURCES变量来指定项目所需的所有源文件。这些源文件将在项目构建时被编译并链接到可执行文件中。
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
)
#首先检查 Qt 版本是否大于或等于 6。如果是,则使用 qt_add_executable() 函数来创建一个名为 testCMakeGui 的可执行文件,指定 MANUAL_FINALIZATION,并包含之前定义的项目源文件列表。
#如果 Qt 版本是 6,还会有一个注释部分,说明如何在 Android 下为 testCMakeGui 定义属性。该注释提供了一个 set_property() 调用的示例,用于指定 QT_ANDROID_PACKAGE_SOURCE_DIR。
#如果 Qt 版本小于6,代码将进入 else 分支。在此分支中,会检查是否为 Android 平台。如果是 Android 平台,将使用 add_library() 函数创建一个名为 testCMakeGui 的共享库(SHARED),并包含项目源文件列表。
#同样也有一个注释,用于定义 Qt 5 中 Android 平台的属性设置。
#如果不是 Android 平台,将使用 add_executable() 函数创建一个名为 testCMakeGui 的可执行文件,并包含项目源文件列表。
#这段代码根据 Qt 版本和平台类型选择不同的构建方式,并为每种情况定义了相应的属性。这样可以确保项目在不同的环境和版本下都能正确构建。
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(testCMakeGui
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET testCMakeGui APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(testCMakeGui SHARED
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(testCMakeGui
${PROJECT_SOURCES}
)
endif()
endif()
#确保了 testCMakeGui 目标可以访问并使用 Qt Widgets 模块中的功能,这样在构建和运行项目时就能正确地链接和使用 Qt 组件
target_link_libraries(testCMakeGui PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
#在 Qt 6.1 及以后的版本中,Qt for iOS会自动设置MACOSX_BUNDLE_GUI_IDENTIFIER,但如果Qt版本低于6.1.0,则需要手动设置一个固定的Bundle Identifier,以确保应用程序在macOS上正确识别和管理。
#默认的Bundle Identifier设置为com.example.testCMakeGui,可以根据需要修改。
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.testCMakeGui)
endif()
#通过set_target_properties函数设置了testCMakeGui目标的属性。其中,${BUNDLE_ID_OPTION}是之前定义的用于设置Bundle Identifier的变量。
#另外,MACOSX_BUNDLE_BUNDLE_VERSION被设置为项目版本,MACOSX_BUNDLE_SHORT_VERSION_STRING被设置为项目主版本号和次版本号的组合。MACOSX_BUNDLE属性被设置为TRUE,表示当前目标是一个macOS bundle。
#WIN32_EXECUTABLE属性被设置为TRUE,表示当前目标是一个Win32可执行程序。
set_target_properties(testCMakeGui PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
#见上文
include(GNUInstallDirs)
install(TARGETS testCMakeGui
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
#如果主版本号等于6,则调用qt_finalize_executable函数来完成testCMakeGui可执行文件的最终配置。
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(testCMakeGui)
endif()