【C++开发】CMake构建工具
目录
1,CMake介绍
2,配置文件CMakeLists.txt
1,CMake介绍
CMake 是一个开源的、跨平台的自动化构建系统生成工具,广泛用于 C 和 C++ 项目的构建管理。它使用一个名为 CMakeLists.txt 的配置文件来定义如何构建项目,并能够生成适用于不同编译器和操作系统的构建文件(如 Makefile、Visual Studio项目等),从而实现跨平台的一致性构建。
2,配置文件CMakeLists.txt
CMakeLists.txt 是 CMake 的核心配置文件,存在于每个需要构建的目录中。配置指令常用的如下:
1,构建项目程序的简单配置指令。
# 声明cmake使用的最低版本
cmake_minimum_required(VERSION 3.20)# 构建项目的信息。下面对应名称、版本、描述、项目使用的语言
project(TestProject VERSION 1.1.1 DESCRIPTION "This is a TestProject" LANGUAGES CXX)# 设置使用C++11版本(14, 17, 20版本类似)
set(CMAKE_CXX_STANDARD 11)# 指定头文件的搜索路径:有target_include_directories和include_directories
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)# CMAKE_CURRENT_SOURCE_DI宏是表示CMakeLists.txt的路径
2,set() 和 file() 设置变量的值。它们用于将一个或多个值组合起来赋给自定义的变量。用途如下:
# 指定编译的源文件(头文件不需要加入,cmake它不会让它参与编译)
# 方法一:指定有限个参与编译。自定义SRC保存找到源文件路径的变量名
set(SRC fun1.cpp fun2.cpp fun3.cpp main.cpp)# 方法二:将指定目录下的所有源文件参与编译(目录下非递归式,即子目录不包含)
# PROJECT_SOURCE_DIR宏本质是cmake后面根的路径名
# 自定义SRC保存找到的所有源文件路径的变量名
aux_source_directories(${PROJECT_SOURCE_DIR}/*.cpp ${PROJECT_SOURCE_DIR}/*.cc SRC)
# 方法三:目前最流行的file。GLOB 表示非递归查找;GLOB_RECURSE递归式查找
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp *.cc)# 指定库的存放路径。其实这里就是把指定的传输路径的字符串设置到定义库路径的宏中
# CMAKE_LIBRARY_OUTPUT_DIRECTORY动态库(.so,.dll)输出路径
# CMAKE_ARCHIVE_OUTPUT_DIRECTORY静态库(.a, .lib)输出路径
# 这里可以使用debug或release构建方式,即CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG 或 CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) # 将生成的动态库传输到lib下
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) # 将生成的静态库传输到lib下# 指定可执行文件的输出路径。CMAKE_RUNTIME_OUTPUT_DIRECTORY(现代写法)和EXECUTABLE_OUTPUT_PATH(过时写法)
set(EXECUTABLE_OUTPUT_PATH /root/Test)#可选:分别设置 Debug/Release 输出路径
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG /root/Test/debug)
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE /root/Test/release)# 拼接字符串。将字符串赋给tmp和tmp1
set(tmp Hello Cmake)
set(tmp1 ${tmp} ${CMAKE_CURRENT_SOURCE_DIR})
3,动静态库的相关指令配置。
# 把指定的源文件编译成一个库(静态库或动态库)
file(GLOB LIBS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
add_library(myshared SHARED ${LIBS}) # 定义动态库
add_library(mystatic STATIC ${LIBS}) # 定义静态库# 指定库的存放路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) # 将生成的动态库传输到lib下
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) # 将生成的静态库传输到lib下# 说明程序链接时,自定义库的路径
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib) # 指定自定义库的路径# 链接自定义生成的库
# 方法一:古老用法。链接静态库时可以使用;链接动态库不推荐link_libraries(mystatic) # 链接库mystatic
# 方法二:新用法。非常推荐。
target_link_libraries(test PRIVATE myshared) # 可执行程序test链接库myshared
4,可执行文件的相关指令配置。
# 指定可执行文件的输出路径。CMAKE_RUNTIME_OUTPUT_DIRECTORY(现代写法)和EXECUTABLE_OUTPUT_PATH(过时写法)
set(EXECUTABLE_OUTPUT_PATH /root/Test)# 指定编译的源文件(头文件不需要加入,cmake它不会让它参与编译)
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp *.cc)# 通过源文件列表生成可执行文件目标,即告诉CMake把哪些源文件编译成一个可执行程序
add_executable(test ${SRC})
# add_executable(test fun1.cpp fun2.cpp fun3.cpp main.cpp)
5,message 输出日志。该命令用于在终端输出变量,以提示用户信息。
语法:
message([<mode>] "message text" ...)
参数说明:
<mode>(可选):指定消息的类型或行为。
"message text":要输出的消息内容,可以包含变量 ${VAR}。消息类型(Mode)取值如下:
STATUS:输出状态信息(用 -- 前缀显示),不会中断构建。
WARNING:输出警告信息,不会中断构建,但会高亮显示。
SEND_ERROR:输出错误信息,并中断当前cmake构建阶段,但继续生成项目。
FATAL_ERROR:输出错误信息,并完全中断构建和生成的过程。样例:
set(tmp Hello Cmake)
set(tmp1 ${tmp} ${CMAKE_CURRENT_SOURCE_DIR})
message(STATUS ${tmp})
message(STATUS ${tmp1})message(STATUS "************************************")
6,list处理列表。列表是用分号 “;” 分隔的一系列字符串,尽管你可能会看到使用空格分隔的值,在内部它们会被转换成分号分隔的形式。该命令提供了一系列的操作来操作这些列表,包括添加、删除、搜索和排序等。这里说明添加和删除。
语法:
list(APPEND <list> [<element> ...]) # 将一个或多个元素追加到指定的列表末尾
list(REMOVE_ITEM <list> [<value> ...]) # 从列表中移除所有与给定值匹配的项
样例:
set(MY_LIST a b c) # 初始化一个列表MY_LIST
list(APPEND MY_LIST d e f) # 添加新元素到列表末尾
message("MY_LIST: ${MY_LIST}") # 输出结果MY_LIST: a;b;c;d;e;f
set(MY_LIST a b c b d b) # 初始化一个包含重复项的列表
list(REMOVE_ITEM MY_LIST b) # 移除所有等于 "b" 的项
message("MY_LIST: ${MY_LIST}") # 输出结果MY_LIST: a;c;d
list还有很多个模式运用,这里不做过多说明。明白原理后,在所需要的场景下自行查找即可
7,link_libraries() 和 target_link_libraries()。这两种指令都是用于链接指定的库。link_libraries()命令设置的是全局链接库,会影响后续定义的所有目标。
link_libraries(mylib)
# 后续所有生成的可执行文件都会链接mylib库add_executable(myapp main.cpp)
target_link_libraries() 是现代 CMake 中用于为特定目标(可执行文件或库)指定链接库的方式。
语法:
target_link_libraries(<target-name> [PRIVATE|PUBLIC|INTERFACE] <library> ...)
参数说明:
<target-name>:之前通过 add_executable() 或 add_library() 定义的目标名称。
PRIVATE:仅当前目标使用此库。PUBLIC:不仅当前目标使用此库,而且任何依赖于这个目标的其他目标也会继承这个 库。
INTERFACE:当前目标本身不需要这些库,但依赖它的目标需要。这点理解若困难可 直接看下面样例。
<library>:链接的库文件。
样例1:
// 可指定程序链接库
target_link_libraries(myapp PRIVATE /path/to/libmylib.a) # 静态库 target_link_libraries(myapp PRIVATE /path/to/libmylib.so) # 动态库
样例2:
# libA 库需要链接 libB,后面的目标不会链接 libB
add_library(libA STATIC a.cpp)
target_link_libraries(libA PRIVATE libB)
# libD 库需要链接 libE,并且它的使用者也需要链接 libE
add_library(libD STATIC d.cpp)
target_link_libraries(libD PUBLIC libE)
# libF 库的使用者需要链接 libG,但 libF 自身并不直接使用 libG
add_library(libF INTERFACE)
target_link_libraries(libF INTERFACE libG)
注意:可执行程序链接库的顺序是先生成可执行程序,在进行动静态库的链接。
8,add_subdirectory 嵌套 CMakeLists.txt 文件。add_subdirectory 用于向构建过程添加一个子目录。这个子目录应该包含自己的 CMakeLists.txt 文件,定义了如何编译该目录下的源代码以及如何将其链接到其他目标(如可执行文件或库,大多数情况是库文件)。
语法:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
参数说明:
source_dir:指定要添加的子目录路径,可以是相对路径也可以是绝对路径。
[binary_dir]可选参数:指定输出文件存放的目录。如果未指定,则默认为 source_dir 对应的构建目录。
[EXCLUDE_FROM_ALL]可选参数:暂时不做说明。
假设你有一个项目结构如下:
/my_project
|—— CMakeLists.txt
|—— /src
| |—— main.cpp
|—— /lib
|—— CMakeLists.txt
|—— /math
|—— math.cpp
|—— math.h/my_project 目录是项目的根目录,其中包含一个顶级 CMakeLists.txt 文件。/src 目录包含了项目的主源文件(如 main.cpp),而 /lib/math 则包含了一个数学库的实现(math.cpp 和 math.h)。
项目根目录下的 CMakeLists.txt 文件内容如下:
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(MyProject)# 添加 lib 目录中的子项目
add_subdirectory(lib)# 添加 src 目录中的可执行文件
add_subdirectory(src)
lib 目录下的 CMakeLists.txt 文件内容如下:
# 定义一个静态库
add_library(MathLib STATIC math/math.cpp)
# 包含头文件路径
target_include_directories(MathLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/math)
在 src 目录下,这里可以创建另一个 CMakeLists.txt 文件来编译可执行文件,并链接之前定义的 MathLib 库:
# 添加可执行文件
add_executable(MyExecutable main.cpp)
# 链接 MathLib 库
target_link_libraries(MyExecutable MathLib)
通过这种方式,add_subdirectory 命令使得能够轻松地组织和管理复杂的项目结构。每个子目录可以有自己的 CMakeLists.txt 文件,从而让项目更加模块化和易于维护。
最后说明下Qt Creator中的CMake配置,Qt Creator环境中CMake配置