CMake教程--QT项目使用CMake

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,不需要调用此宏,因为构建过程会自动完成。

相关推荐
袁袁袁袁满8 分钟前
Python之MoviePy视频编辑模块介绍与应用
开发语言·python·音视频·moviepy·视频编辑模块
曼巴UE511 分钟前
UE C++ 多镜头设置缩放 平移
开发语言·c++
不爱学习的啊Biao13 分钟前
C# 下sendmessage和postmessage的区别详解与示例
开发语言·c#
悄悄敲敲敲14 分钟前
栈的实现详解
c语言·开发语言·数据结构·c++·算法·链表·线性回归
空青7261 小时前
ChatGPT在Java后端开发中的应用与影响
java·开发语言·人工智能·后端·神经网络·机器学习·chatgpt
搁浅小泽1 小时前
C 语言总复习
c语言·开发语言
五月阳光暖洋洋1 小时前
SpringBoot2.2.6使用spring-boot-validation读取不到自定义配置文件中的属性
java·开发语言·spring boot
小悟空GK2 小时前
Http介绍
开发语言
DS小龙哥2 小时前
QT+OpenCV在Android上实现人脸实时检测与目标检测
android·人工智能·qt·opencv·目标检测
502胶水2052 小时前
腾讯地图异步调用
开发语言·ios·swift