CMake教程--QT项目使用CMake
- [Chapter1 CMake教程--QT项目使用CMake](#Chapter1 CMake教程--QT项目使用CMake)
-
- [1. Basic Cmake Based Project](#1. Basic Cmake Based Project)
- [2. Executable VS Library](#2. Executable VS Library)
- [3. Every module has its own CMakeList.txt in its folder](#3. Every module has its own CMakeList.txt in its folder)
-
- [3.1 不推荐的做法:](#3.1 不推荐的做法:)
- [3.2 推荐的做法](#3.2 推荐的做法)
- [4. 强制以Debug, Release, RelWithDebInfo, MinSizeRel其中一种作为编译方式](#4. 强制以Debug, Release, RelWithDebInfo, MinSizeRel其中一种作为编译方式)
- [5. CMake高频常用变量](#5. CMake高频常用变量)
- [6. 在CMake里区分操作系统](#6. 在CMake里区分操作系统)
- [7. 在CMake里打印信息](#7. 在CMake里打印信息)
- [Chapter2 使用CMake构建Qt项目](#Chapter2 使用CMake构建Qt项目)
- [Chapter3 CMake+QT使用教程](#Chapter3 CMake+QT使用教程)
- [Chapter4 解析QT6自动生成的CMake文件](#Chapter4 解析QT6自动生成的CMake文件)
-
- [1 新建基于CMake的QT6项目](#1 新建基于CMake的QT6项目)
- [2 解析CMake文件](#2 解析CMake文件)
-
- [2.1 定义CMake以及项目版本号](#2.1 定义CMake以及项目版本号)
- [2.2 启用QT资源自动编译选项](#2.2 启用QT资源自动编译选项)
- [2.3 定义使用到的C++标准](#2.3 定义使用到的C++标准)
- [2.4 查找并加载外部依赖包](#2.4 查找并加载外部依赖包)
- [2.5 导入项目文件](#2.5 导入项目文件)
- [2.6 创建可执行文件](#2.6 创建可执行文件)
- [2.7 链接动态库并设置可执行文件属性](#2.7 链接动态库并设置可执行文件属性)
- [2.8 安装目标可执行文件到指定的目录](#2.8 安装目标可执行文件到指定的目录)
- [2.9 进行最后的可执行文件处理](#2.9 进行最后的可执行文件处理)
Chapter1 CMake教程--QT项目使用CMake
1. Basic Cmake Based Project
bash
# Qt对cmake版本的最小要求(但测试发现低一点的版本似乎也没问题)
cmake_minimum_required(VERSION 3.16.0)
# 项目命名
# VERSION 1.0.0 LANGUAGES CXX: 是可选的
project(helloworld VERSION 1.0.0 LANGUAGES CXX)
# 如果采用非Qt Creator开发,需要通过告知Qt的安装路径,建议把Qt的安装路径设置到环境变量
# 例如:QT_DIR=D:\Qt\6.1.2\msvc2019_64
set(CMAKE_PREFIX_PATH $ENV{QT_DIR})
# 有些项目会动态生成头文件,项目中需要引入它,因此需要将output目录也include进来
# 等同于INCLUDE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Qt6 对C++版本推荐至少17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Qt特有的编译器需要打开,默认是关闭的
set(CMAKE_AUTOMOC ON) # Meta-Object Compiler
set(CMAKE_AUTORCC ON) # Resource Compiler
set(CMAKE_AUTOUIC ON) # User Interface Compiler
# 寻找Qt的库
# Qt6 COMPONENTS Widgets:寻找Qt库中的Widget模块
# REQUIRED: 意味着找不到报错并不会继续下去
find_package(Qt6 COMPONENTS Core Qml Quick LinguistTools REQUIRED)
# 集成源码以及资源并打包
set(TS_FILES TestApp_zh_CN.ts TestApp_en_US.ts)
aux_source_directory(src SRC_LIST)
qt6_add_resources(QML_QRC qml_module_a.qrc qml_module_b.qrc)
set(PROJECT_SOURCES ${SRC_LIST} ${TS_FILES} ${QML_QRC})
# 这里如果不加WIN32,会导致编译的可执行文件运行时候会同时弹出一个命令行终端
# 这是Windows的特性,对于其它平台得去掉WIN32
add_executable(${CMAKE_PROJECT_NAME} WIN32 ${PROJECT_SOURCES})
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
# cmake本身有四种编译模式:`Debug`, `Release`, `RelWithDebInfo`, `MinSizeRel`
# 此操作将`Debug`和`RelWithDebInfo`归类于QML的debug,即这两种模式下QML运行会保留debug信息
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
# 链接库到当前项目
# PRIVATE:项目私有内部链接,只有在开发Library对外公开时候才会使用PUBLIC
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
Qt6::Core
Qt6::Qml
Qt6::Quick)
# 加入新qml文件能自动扫描到并集成到项目
qt_import_qml_plugins(${CMAKE_PROJECT_NAME})
2. Executable VS Library
bash
# 将${SRC_LIST}将编译成可执行文件,如果没有main函数会报错
add_executable(${CMAKE_PROJECT_NAME} ${SRC_LIST})
# 将${SRC_LIST}编译为library,library的类型可选择,默认是静态库
add_library(${CMAKE_PROJECT_NAME} [STATIC|SHARED|MODULE] ${SRC_LIST})
3. Every module has its own CMakeList.txt in its folder
3.1 不推荐的做法:
bash
aux_source_directory(. DIR_SRCS1)
aux_source_directory(. DIR_SRCS2)
aux_source_directory(. DIR_SRCS3)
add_executable(${CMAKE_PROJECT_NAME} ${DIR_SRCS1} ${DIR_SRCS2} ${DIR_SRCS3})
这种做法会导致项目里即便改了一处代码,也会编译所有代码,导致编译时间较长,不能很好利用增量编译,再说C/C++本身编译就很慢
3.2 推荐的做法
以下以一个开源项目live555改版成为cmake项目作为推荐的项目代码组织结构的案例,虽然最终目标是编译成Library给外部使用,但内部同时也包含了打包成可执行文件的模块(mediaServer):
其中,BasicUsageEnvironment, groupsock, liveMedia, UsageEnvironment都是live555项目的子模块,mediaServer是集成所有子模块打包成为可执行文件的部分。
作为项目入口,推荐的CMakeList.txt可以如下:
bash
cmake_minimum_required(VERSION 3.15)
project(live555 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
bash
# add all sub folders as modules
ADD_SUBDIRECTORY(UsageEnvironment)
ADD_SUBDIRECTORY(BasicUsageEnvironment)
ADD_SUBDIRECTORY(groupsock)
ADD_SUBDIRECTORY(liveMedia)
ADD_SUBDIRECTORY(mediaServer)
模块内部CMakeList.txt推荐如下:
bash
# 这里指定当前模块名,这里推荐用文件名作为模块名
project(BasicUsageEnvironment)
# 因为当前模块cpp里使用里其他模块的头文件,因此需要把它们include进来
include_directories(../UsageEnvironment/include)
include_directories(../groupsock/include)
# 当前模块的头文件肯定要include进来
include_directories(./include)
# 当前模块下的cpp跟CMakeList.txt在同级目录
aux_source_directory(./ SRC_LIST)
# 当前只是模块,最终需要把所有的模块合并构建,因此当前需要指定编译对象为STATIC
# ${PROJECT_NAME}的值即为当前模块名,需要注意的是不能用${CMAKE_PROJECT_NAME},因为那是跟目录下CMakeList.txt指定的名字,那是整个项目的名字,即:live555
add_library(${PROJECT_NAME} STATIC ${SRC_LIST})
作为目标是编译为可执行文件的CMakeList.txt如下:
bash
project(mediaServer)
include_directories(../UsageEnvironment/include)
include_directories(../groupsock/include)
include_directories(../liveMedia/include)
include_directories(../BasicUsageEnvironment/include)
# 当前目录下也有代码
aux_source_directory(. SRC_LIST)
# 当前模块是main入口,最终目标是编译出live555的可执行文件,因此不再用add_library()
add_executable(${PROJECT_NAME} ${SRC_LIST})
# 链接所有其他模块到当前模块
target_link_libraries(${PROJECT_NAME} PRIVATE
liveMedia
groupsock
BasicUsageEnvironment
UsageEnvironment)
4. 强制以Debug, Release, RelWithDebInfo, MinSizeRel其中一种作为编译方式
方法一:
在工程build目录下执行
cmake ... -DCMAKE_BUILD_TYPE=Debug|Release|MinSizeRel|RelWithDebInfo
方法二:
或者在顶级CMakeList.txt里加入:
bash
set(CMAKE_BUILD_TYPE Debug|Release|MinSizeRel|RelWithDebInfo)
5. CMake高频常用变量
变量的引用方式是使用${},在IF中,不需要使用这种方式,直接使用变量名亦可
自定义变量:SET(OBJ_NAME xxxx),调用自定义变量: $ {OBJ_NAME}
设置环境变量: SET(ENV{NAME} value), 需要注意的是这里ENV没有$; 调用环境变量: $ENV{NAME}
CMAKE的常用变量:
6. 在CMake里区分操作系统
bash
if(CMAKE_SYSTEM_NAME MATCHES "Linux") // 注意区分大写
message(STATUS "Linux platorm!")
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
if(CMAKE_CL_64)
message(STATUS "Windows Win64 platform!")
else()
message(STATUS "Windows Win32 platform!")
endif(CMAKE_CL_64)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
message(STATUS "FreeBSD platform!")
else()
message(STATUS "other platform!")
endif(CMAKE_SYSTEM_NAME MATCHES "Linux")
简化版亦可:
bash
if (WIN32)
message(STATUS "Now is windows")
elseif (APPLE)
message(STATUS "Now is Apple systens.")
elseif (UNIX)
message(STATUS "Now is UNIX-like OS's.")
endif ()
7. 在CMake里打印信息
bash
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
(无) = 重要消息
STATUS = 非重要消息
WARNING = CMake 警告, 会继续执行
AUTHOR_WARNING = CMake 警告 (dev), 会继续执行
SEND_ERROR = CMake 错误, 继续执行,但是会跳过生成的步骤
FATAL_ERROR = CMake 错误, 终止所有处理过程
一般用于排错打印日志,或者打印编译过程信息及步骤
Chapter2 使用CMake构建Qt项目
原文链接:https://blog.csdn.net/qq_40181592/article/details/101623231
由于要做新的GUI设计加入更多的功能,在windows上已经安装了QtCreator,不想在Ubuntu下再装一个,于是采用在windows下开发,拿到Linux下编译运行,故使用CMake构建Qt项目。
1.Qt中的qmake
- qmake可以利用源文件、头文件、qt的ui文件等生成各种不同类型的工程和工程需要的Makefile文件。
- pro文件,添加各种源文件,还可以设定平台相关的不同源文件,设置各种规则,利用qmake命令生成工程。
QT中编译三步走:
- qmake -project (用于创建.pro文件,将所有的文件编译成一个与平台无关的工程文件)
- qmake (读取本身的Qt设置,生成与库一致的相应的Makefile)
- make (根据生成的Makefile,将文件编译为二进制可执行程序)
2.CMake中的构建
- CMake中使用的参数代表指CMakeLists.txt文件所在的目录。这个CMakeLists.txt文件控制了整个编译的过程。为了更彻底地理解它,我们用以下这个图来看看整个编译的流程。下面这张图表明用户编写的文件(源代码,头文件,.ui文件,.qrc文件)在编译过程中是如何被Qt的工具进行处理,并整合到整个编译流程中的。因为qmake是用于处理这个流程的,它隐藏了这个流程中的很多细节。
-
Qt 不是使用的"标准的" C++ 语言,而是对其进行了一定程度的"扩展"。这里我们从Qt新增加的关键字就可以看出来:signals、slots 或者 emit。所以有人会觉得 Qt 的程序编译速度慢,这主要是因为在 Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。
-
Qt中,如果一个类要使用信号/槽功能,就必须在其中声明Q_OBJECT。只有加入了Q_OBJECT,你才能使用QT中的signal和slot机制。
这时候,就必须在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:
cpp
class MyMainWindow : public QWidget
{
Q_OBJECT
...
}
-
moc是QT的预编译器,用来处理代码中的slot,signal,emit,Q_OBJECT等。moc文件是对应的处理代码,也就是Q_OBJECT宏的实现部分
-
在头文件中如果有使用Q_OBJECT宏的话,则这个文件需要被moc进行处理,.ui文件也必须要由uic处理,.qrc文件需要由rcc程序处理。
3.具体构建过程
-
下面是我在windows下写的扫雷程序文件(包含.ui .qrc)
-
下面是CMakeList.txt的内容。
bash
#cmake版本
cmake_minimum_required(VERSION 3.5.1)
#项目名称
project(Minesweeper)
#让cmake自动去寻找Qt5
FIND_PACKAGE(Qt5 COMPONENTS Widgets REQUIRED)
#采用C++11标准
set(CMAKE_CXX_STANDARD 11)
#添加所有的源文件
SET(Minesweeper_SOURCES
dialog.cpp
main.cpp
mainwindow.cpp
minemap.cpp
myitem.cpp
myscene.cpp
setpro.cpp
)
#添加所有的头文件
SET(Minesweeper_HEADERS
dialog.h
mainwindow.h
minemap.h
myitem.h
myscene.h
setpro.h
)
#添加所有的.ui文件
SET(Minesweeper_FORMS
dialog.ui
mainwindow.ui
setpro.ui
)
#添加资源文件
SET(Minesweeper_RESOURCES
img.qrc
)
#调用预编译器moc,需要使用 QT5_WRAP_CPP宏
QT5_WRAP_CPP(Minesweeper_HEADERS_MOC ${Minesweeper_HEADERS})
#使用uic处理.ui文件
QT5_WRAP_UI(Minesweeper_FORMS_HEADERS ${Minesweeper_FORMS})
#使用rcc处理.qrc文件
QT5_ADD_RESOURCES(Minesweeper_RESOURCES_RCC ${Minesweeper_RESOURCES})
#这些生成的中间文件都会在build目录下,这样的话,编译器则不能定位由uic程序产生的诸如_ui_mainwindow.h等文件。所以,我们需要把build目录添加到包含目录中
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
#生成可执行文件
ADD_EXECUTABLE(Minesweeper
${Minesweeper_SOURCES}
${Minesweeper_HEADERS_MOC}
${Minesweeper_FORMS_HEADERS}
${Minesweeper_RESOURCES_RCC}
)
#为target添加需要链接的共享库
TARGET_LINK_LIBRARIES(Minesweeper ${Qt5Widgets_LIBRARIES})
- 下面是build的结果:
- 下面是运行之后的结果:
4.结尾
有了上面的构建,也可以完成其他资源的添加,结合新的底层就可以开发出好的界面和功能了。
Chapter3 CMake+QT使用教程
Chapter4 解析QT6自动生成的CMake文件
1 新建基于CMake的QT6项目
打开qt-creator,新建一个基于CMake的QT项目,编辑器将自动生成以下CMake文件。
bash
cmake_minimum_required(VERSION 3.5)
project(HeyPlot VERSION 0.1 LANGUAGES CXX)
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 PrintSupport)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets PrintSupport)
set(PROJECT_SOURCES
main.cpp
widget.cpp
widget.h
widget.ui
qcustomplot.cpp
qcustomplot.h
heyplot.cpp
heyplot.h
)
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(HeyPlot
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
else()
if(ANDROID)
add_library(HeyPlot SHARED
${PROJECT_SOURCES}
)
else()
add_executable(HeyPlot
${PROJECT_SOURCES}
)
endif()
endif()
target_link_libraries(HeyPlot PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::PrintSupport)
set_target_properties(HeyPlot PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
install(TARGETS HeyPlot
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(HeyPlot)
endif()
可以看到QT生成的CMake文件和普通项目还是有所不同的,多了挺多奇怪的函数,这里对QT默认生成的CMake脚本文件做一下解析。
2 解析CMake文件
接下来我们按照先上代码,后上解析的方式对CMake文件进行解析。
2.1 定义CMake以及项目版本号
bash
# 设置所需的最低CMake版本为3.5。
cmake_minimum_required(VERSION 3.5)
# 设置项目名称为"HeyPlot",版本号为0.1,使用的编程语言为C++。
project(HeyPlot VERSION 0.1 LANGUAGES CXX)
首先是一些比较基础的东西。配置文件先设置所需的最低CMake版本为3.5,设置项目名称为"HeyPlot",版本号为0.1,使用的编程语言为C++。
2.2 启用QT资源自动编译选项
bash
# 启用自动UI编译(AUTOUIC)
set(CMAKE_AUTOUIC ON)
# 自动元对象编译(AUTOMOC)
set(CMAKE_AUTOMOC ON)
# 自动资源编译(AUTORCC)
set(CMAKE_AUTORCC ON)
然后,就到了比较奇怪的地方了,配置文件分别设置了CMAKE_AUTOUIC、CMAKE_AUTOMOC、CMAKE_AUTORCC三个参数为ON,这些参数都是QT编译时独有的参数。
2.2.1 CMAKE_AUTOUIC
CMAKE_AUTOUIC参数用于启用自动处理Qt User Interface (UI) 文件的功能。当我们使用Qt进行GUI应用程序开发时,通常会创建UI文件(以.ui扩展名结尾),这些文件描述了用户界面的布局和组件。为了将UI文件与应用程序的代码连接起来,我们需要将它们转换为相应的C++代码。通过将CMAKE_AUTOUIC设置为 ON ,CMake将自动在构建过程中查找并处理项目中的UI文件。 具体而言,当我们启用CMAKE_AUTOUIC后,CMake会在构建过程中自动调用uic工具(UI编译器),将UI文件转换为对应的C++代码,并将生成的C++文件添加到构建系统中,以便编译器将其编译为最终的可执行文件。这样,我们就不需要手动执行uic工具来转换UI文件,CMake会在构建时自动完成这个步骤,简化了项目的配置和构建过程。
2.2.2 CMAKE_AUTOMOC
CMAKE_AUTOMOC参数用于启用自动处理启用自动处理Qt元对象编译(MOC)的功能。Qt的元对象编译器(Meta Object Compiler,MOC)是一个工具,用于处理Qt中的特殊C++扩展,例如信号和槽、动态属性和反射机制。MOC会解析源代码中的这些扩展,并生成额外的C++代码,用于支持这些特性的运行时行为。当我们使用到Qt进行应用程序开发时,一般会使用到信号和槽、Q_OBJECT宏或其他需要MOC处理的Qt特性,我们需要确保这些代码被MOC处理后再进行编译。通过将CMAKE_AUTOMOC设置为ON,我们告诉CMake自动查找需要MOC处理的源文件,并在构建过程中自动调用MOC来生成相应的额外C++代码。具体而言,启用CMAKE_AUTOMOC后,CMake会在构建过程中自动检测源文件中的需要MOC处理的特殊Qt扩展,并为这些源文件生成对应的MOC输出文件。然后,这些生成的MOC输出文件将被添加到构建系统中,以便编译器将其编译为最终的可执行文件。
2.2.3 CMAKE_AUTORCC
CMAKE_AUTORCC参数用于启用自动处理Qt资源文件的功能。Qt资源文件(.qrc)是一种用于将非代码资源(如图像、字体、样式表等)集成到应用程序中的方式。资源文件可以在运行时被动态加载和使用,使应用程序的资源管理更加方便和灵活。通过将CMAKE_AUTORCC设置为ON,我们将告诉CMake自动查找并处理项目中的Qt资源文件。 具体而言,启用CMAKE_AUTORCC后,CMake会在构建过程中自动查找项目中的资源文件(.qrc文件),并调用rcc工具(资源编译器)来将这些资源文件编译为二进制数据。然后,生成的二进制资源数据将被添加到构建系统中,以便在应用程序中使用。这样,我们就无需手动调用rcc工具来处理资源文件,CMake会在构建时自动处理这些步骤,简化了项目的配置和构建过程。当我们在代码中使用Qt的资源管理相关函数(如QResource)时,这些资源数据将被加载和使用。
2.3 定义使用到的C++标准
bash
# 设置C++标准为C++17,并要求编译器支持此标准。
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
这和普通的CMake一致,这里直接跳过。
2.4 查找并加载外部依赖包
bash
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets PrintSupport)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets PrintSupport)
接下来时两个连续的find_package。这是一个很奇怪的构建方式,起初并不明白为什么使用两个连续的find_package来查找和加载外部依赖包,后面发现这两个find_package其实各有用处。
在第一个find_package函数中,CMake尝试查找并加载Qt6,如果找不到再尝试Qt5,同时CMake将要求同时加载Widgets和PrintSupport这两个模块,REQUIRED关键字确保这些模块是必需的,如果找不到任何一个模块,将会导致CMake错误并停止构建过程。在这个过程中,CMake将对QT_VERSION_MAJOR变量进行赋值告诉我们使用到的QT版本是什么。
在第二个find_package函数中,根据第一个find_package函数中找到的QT版本来引用具体的模块。这样连续的两个find_package,确保了之后的构建过程中使用到的模型能够和先前找到的Qt版本匹配。
2.5 导入项目文件
bash
# 定义项目的源文件列表
set(PROJECT_SOURCES
main.cpp
widget.cpp
widget.h
widget.ui
qcustomplot.cpp
qcustomplot.h
heyplot.cpp
heyplot.h
)
# 包含源文件目录
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
首先,我们先定义了项目的源文件列表,这个和普通的CMake文件一样没什么好说的。然后我们包含了根目录为头文件搜索路径,在QT默认生成的项目中是没有INCLUDE_DIRECTORIES($ {PROJECT_SOURCE_DIR})这个步骤的,但是我认为这是十分错误的决定。没有INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})步骤意味着CMake编译UI文件后生成的ui_xxxxx.h文件将无法和上述定义的项目源文件联系在一起。导致如果我们希望在qt-creator中对某个空间进行提升操作时无法找到对应的头文件,因此这里我把这句话加上。
2.6 创建可执行文件
bash
# 如果Qt的主要版本号大于等于6,则使用"qt_add_executable"来创建可执行文件。否则,根据目标平台选择创建动态库(Android)或可执行文件。
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(HeyPlot
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
else()
if(ANDROID)
add_library(HeyPlot SHARED
${PROJECT_SOURCES}
)
else()
add_executable(HeyPlot
${PROJECT_SOURCES}
)
endif()
endif()
接下来时创建可执行文件,这里的qt_add_executable函数其实不是QT6独有的。从Qt 5.14版本开始,QT推荐使用
-
qt_add_executable命令代替add_executable,以更好地集成Qt的构建系统。其主要区别如下:
-
qt_add_executable命令在内部自动处理了与Qt相关的构建步骤,例如自动生成moc文件(元对象编译),处理Qt资源文件(qrc),并将这些步骤与目标可执行文件的构建过程进行了整合。这样,可以更简化和统一Qt项目的构建过程,减少手动配置的需求。
-
qt_add_executable命令在生成目标可执行文件时使用了更好的默认设置,例如自动添加预定义宏和链接Qt的模块。
2.7 链接动态库并设置可执行文件属性
bash
# 将Qt模块链接到目标可执行文件或动态库。
target_link_libraries(HeyPlot PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::PrintSupport)
# 设置目标可执行文件的属性,如MacOSX的Bundle标识符、版本号和短版本字符串,以及在Windows下作为可执行文件运行。
set_target_properties(HeyPlot PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
这个部分负责将Qt模块链接到目标可执行文件或动态库,同时也设置了目标可执行文件的属性,如MacOSX的Bundle标识符、版本号和短版本字符串,以及在Windows下作为可执行文件运行。
2.8 安装目标可执行文件到指定的目录
bash
install(TARGETS HeyPlot
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
和普通CMake文件一致,这里略过
2.9 进行最后的可执行文件处理
bash
# 如果使用的是Qt 6,则使用"qt_finalize_executable"进行最后的可执行文件处理。
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(HeyPlot)
endif()
qt_finalize_executable是一个CMake宏,用于在构建Qt应用程序时进行最后的可执行文件处理。在使用Qt6的情况下,qt_add_executable命令会生成一个中间目标文件,该文件需要经过最后的处理步骤才能成为可执行文件。这个处理步骤包括添加额外的Qt相关链接项、处理资源文件等。 qt_finalize_executable宏负责执行这些最后的处理步骤,将中间目标文件转换为最终的可执行文件。它会添加必要的链接库、处理资源文件,并进行其他必要的操作,以确保可执行文件包含了所有所需的Qt组件和功能。在CMake脚本中,通常会在qt_add_executable之后紧接着调用qt_finalize_executable来完成构建过程。需要注意的是,qt_finalize_executable宏仅在使用Qt 6时才需要调用。对于旧版本的Qt,不需要调用此宏,因为构建过程会自动完成。