QT项目使用CMake

QT项目使用CMake

1. Basic Cmake Based Project

# 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

# 将${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 不推荐的做法:

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):

├── CMakeLists.txt
├── BasicUsageEnvironment
│   └── CMakeLists.txt
│   └── BasicHashTable.cpp
│   └── xxx.cpp
│   └── include
│       └── BasicHashTable.hh
│       └── xxx.hh
├── groupsock
│   └── CMakeLists.txt
│   └── GroupEId.cpp
│   └── xxx.cpp
│   └── include
│       └── GroupEId.hh
│       └── xxx.hh
├── liveMedia
│   └── CMakeLists.txt
│   └── AC3AudioRTPSink.cpp
│   └── xxx.cpp
│   └── include
│       └── GroupEId.hh
│       └── xxx.hh
├── mediaServer
│   └── CMakeLists.txt
│   └── DynamicRTSPServer.cpp
│   └── xxx.cpp
└── UsageEnvironment
    └── CMakeLists.txt
    └── HashTable.cpp
    └── xxx.cpp
    └── include
        └── Boolean.hh
        └── xxx.hh

其中,BasicUsageEnvironment, groupsock, liveMedia, UsageEnvironment都是live555项目的子模块,mediaServer是集成所有子模块打包成为可执行文件的部分。

作为项目入口,推荐的CMakeList.txt可以如下:

cmake_minimum_required(VERSION 3.15)
project(live555 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# add all sub folders as modules
ADD_SUBDIRECTORY(UsageEnvironment)
ADD_SUBDIRECTORY(BasicUsageEnvironment)
ADD_SUBDIRECTORY(groupsock)
ADD_SUBDIRECTORY(liveMedia)
ADD_SUBDIRECTORY(mediaServer)

模块内部CMakeList.txt推荐如下:

# 这里指定当前模块名,这里推荐用文件名作为模块名
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如下:

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里加入: 
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的常用变量:

变量	意义
CMAKE_SOURCE_DIR	工程项目跟目录
CMAKE_CURRENT_SOURCE_DIR	CMakeList.txt所在的目录
CMAKE_MODULE_PATH	如果工程复杂,可能需要编写一些cmake模块,这里通过SET指定这个变量
LIBRARY_OUTPUT_DIR	库最终存放目录
BINARY_OUTPUT_DIR	可执行的最终存放目录
PROJECT_NAME	当前CMakeList.txt里设置的project_name
CMAKE_PROJECT_NAME	项目跟目录配置的project_name

6. 在CMake里区分操作系统

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")

简化版亦可:

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里打印信息

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 错误, 终止所有处理过程
一般用于排错打印日志,或者打印编译过程信息及步骤

常用命令:

target_include_directories

在 CMake 中,target_include_directories() 命令用于为指定的目标(如库或可执行文件)添加包含目录。这些包含目录会在编译目标时告诉编译器去哪里查找头文件。

target_include_directories(${PROJECT_NAME}  
    PUBLIC  
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>  
        $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>  
        $<INSTALL_INTERFACE:include>)  
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
PUBLIC: 这表示以下指定的目录是公共的,意味着它们不仅对该目标有用,而且对其依赖项也有用。当其他目标链接到这个目标时,它们也会继承这些包含目录。

$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>: 在构建目标时,这个目录将被添加到包含路径中。${CMAKE_CURRENT_SOURCE_DIR} 是当前处理的 CMakeLists.txt 文件所在的目录,因此这个路径指向项目源代码中的 include 目录。
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>: 同样,在构建目标时,这个目录也将被添加到包含路径中。${PROJECT_BINARY_DIR} 是构建过程中生成的文件(如编译的输出)所在的目录,因此这个路径指向构建输出目录中的 include 目录。
$<INSTALL_INTERFACE:include>: 当目标被安装时,这个目录将被添加到包含路径中。这意味着,如果其他项目安装了这个目标并链接到它,它们将在 include 目录下查找头文件。
PRIVATE: 这表示以下指定的目录是私有的,仅对该目标有用,不会传递给它的依赖项。

${CMAKE_CURRENT_SOURCE_DIR}/include: 在构建目标时,这个目录将被添加到包含路径中,并且只对该目标可见。这意味着,尽管它位于源代码目录中,但它不会传递给任何链接到这个目标的其他目标。
总结起来,这些命令确保了在构建和安装项目时,编译器能够找到必要的头文件。公共目录会被传递给链接到该项目的其他项目,而私有目录则不会。这种做法有助于组织和管理项目的依赖关系,并确保在不同环境中构建的一致性。

execute_process

execute_process(                                # 执行一个子进程
  COMMAND git rev-parse --short HEAD            # 命令
  OUTPUT_VARIABLE ${PROJECT_NAME}_COMMIT        # 输出字符串存入变量
  OUTPUT_STRIP_TRAILING_WHITESPACE              # 删除字符串尾的换行符
  ERROR_QUIET                                   # 对执行错误静默
  WORKING_DIRECTORY                             # 执行路径
    ${CMAKE_CURRENT_SOURCE_DIR}
)

详细解释:

COMMAND git rev-parse --short HEAD: 这是要执行的命令本身。git rev-parse --short HEAD 会返回当前 Git 仓库的 HEAD 提交的简短哈希值(通常是前七个字符)。

OUTPUT_VARIABLE ${PROJECT_NAME}_COMMIT: 这告诉 CMake 将命令的输出存储在一个变量中。${PROJECT_NAME}_COMMIT 是一个变量名,${PROJECT_NAME} 是 CMake 预定义的变量,代表当前项目的名称。因此,如果项目名称是 MyProject,那么输出变量将会是 MyProject_COMMIT。

OUTPUT_STRIP_TRAILING_WHITESPACE: 这个选项告诉 CMake 在将输出存储到变量之前去除任何尾随的空白字符,包括换行符。这对于确保变量中只包含纯哈希值非常重要。

ERROR_QUIET: 这个选项告诉 CMake 如果命令执行失败(例如,如果 Git 没有安装或当前目录不是一个 Git 仓库),则不要显示任何错误消息。这通常用于在构建过程中避免不必要的警告或错误,特别是当 Git 信息不是构建所必需的时候。

WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}: 这指定了命令执行的工作目录。${CMAKE_CURRENT_SOURCE_DIR} 是一个 CMake 预定义的变量,表示当前处理的 CMakeLists.txt 文件所在的目录。设置工作目录对于确保命令在正确的上下文中执行很重要,特别是当构建系统包含多个子目录和源文件时。

add_library

add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
这不是一个常规的 add_library 调用,而是使用了 ALIAS 关键字来创建一个别名目标。别名目标是一种特殊的 CMake 目标,它并不实际构建任何东西,而是简单地提供了对另一个已存在目标的引用。

这里的 ${PROJECT_NAME}::${PROJECT_NAME} 创建了一个名为 <项目名>::<项目名> 的别名目标,该别名目标简单地引用了 ${PROJECT_NAME} 所代表的原始目标。${PROJECT_NAME} 通常是在 CMakeLists.txt 文件的顶部通过 project() 命令设置的项目的名称。

别名目标在几个场景中可能很有用:

目标导入:当你在一个 CMake 项目中使用其他项目生成的库时,这些库可能使用不同的命名约定或命名空间。通过使用别名,你可以提供一个统一的、符合你项目命名约定的目标名称。

兼容性:随着时间的推移,项目的构建系统可能会发生变化。通过创建别名,你可以在不更改引用该目标的其他代码的情况下,更改底层实现的目标。

条件构建:在某些情况下,你可能希望在满足特定条件时构建不同的库,但希望提供一个一致的目标名称给其他代码引用。通过使用别名,你可以在不更改引用代码的情况下切换底层实现。
  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用cmake-gui工具,在Qt中添加asio库:首先打开cmake-gui,然后添加asio源文件路径,然后添加asio库路径,最后点击“Configure”和“Generate”按钮,即可完成添加asio库的步骤。 ### 回答2: 在Qt使用CMake添加ASIO库,可以按照以下步骤进行: 1. 首先,确保已经安装了ASIO库。可以从官方网站(https://think-async.com/Asio/)下载ASIO库,并进行安装。 2. 打开Qt创建一个新的C++项目。 3. 在项目目录中创建一个名为"CMakeLists.txt"的文件。 4. 在"CMakeLists.txt"文件中添加以下内容: ``` cmake_minimum_required(VERSION 3.5) project(MyProject) set(CMAKE_CXX_STANDARD 11) add_executable(MyProject main.cpp) target_link_libraries(MyProject asio) ``` 5. 将ASIO库的路径添加到CMake中。在上述代码中,`target_link_libraries`行后面添加以下内容: ``` target_include_directories(MyProject PRIVATE /path/to/asio/headers) ``` 将“/path/to/asio/headers”替换为ASIO库的实际路径。 6. 保存并关闭"CMakeLists.txt"文件。 7. 在Qt的"项目"视图中,右键单击项目文件夹,然后选择"重新加载CMake项目"。 8. 在Qt的"项目"视图中,右键单击项目文件夹,然后选择"构建项目"。 9. 构建成功后,使用Qt的"运行"按钮运行项目。 通过上述步骤,你就可以在Qt使用CMake添加并使用ASIO库了。请注意,确保配置正确的ASIO库路径以及正确的CMakeQt版本,以避免可能遇到的问题。 ### 回答3: 在Qt使用CMake添加Asio库需要以下步骤: 1. 首先,确保你的Qt项目目录中包含了CMakeLists.txt文件。如果没有,请新建一个。 2. 在CMakeLists.txt文件的开头,添加以下内容,以指定CMake的最低版本和项目名称: ``` cmake_minimum_required(VERSION 3.5) project(YourProjectName) ``` 3. 在CMakeLists.txt文件中,添加以下内容来引入Qt的相关模块: ``` find_package(Qt5 COMPONENTS Core Network REQUIRED) ``` 4. 添加Asio库的路径,可以是相对路径或绝对路径。比如,如果Asio库的源代码位于项目目录中的"third_party/asio"文件夹下,添加以下内容: ``` add_subdirectory(third_party/asio) ``` 5. 在CMakeLists.txt文件中,添加以下内容,来设置与Asio库的链接选项,同时链接Qt模块: ``` target_link_libraries(YourProjectName PRIVATE Qt5::Core Qt5::Network asio) ``` 6. 最后,在你的Qt项目中,使用CMake来构建项目。打开终端,切换到项目目录,执行以下指令: ``` mkdir build cd build cmake .. make ``` 这将会生成你的Qt应用程序,并自动链接Asio库。你可以通过运行生成的可执行文件来启动应用程序。 这样,你就成功地在Qt使用CMake添加了Asio库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值