首先需要在源文件所在目录下添加一个新文件 CMakeLists.txt
注释
- 注释行
#
- 注释块
#[[内容]]
属性
cmake_minimum_required
:指定使用的cmake
的最低版本cmake_minimum_required(VERSION 3.0)
project
:定义工程名称,并可指定工程的版本、工程描述、web
主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] // 项目的版本
[DESCRIPTION <project-description-string>] // 项目的描述
[HOMEPAGE_URL <url-string>] // 项目的地址
[LANGUAGES <language-name>...]) // 项目使用的语言
add_executable
:定义工程会生成的可执行程序
add_executable(可执行程序名 源文件名称)
add_executable(main main.cpp a.hpp a.cpp)
执行CMake
命令
cmake CMakeLists.txt
文件的所在路径- 当执行
cmake
命令之后,CMakeLists.txt
中的命令就会被执行 - 执行命令之后,对应的目录下生成了一个
makefile
等文件,此时再执行make
命令,就可以对项目进行构建得到所需的可执行程序了 - 创建一个
build
目录,在里面使用cmake
命令,让生成的文件放在build
里
变量
如果源文件过多,需要反复使用这些源文件的名字,就会太长。可以使用一个变量将文件名对应的字符串存储起来,在
cmake
里定义变量需要使用set
// set(变量名 变量值)
// 方式1: 各个源文件之间使用空格间隔
set(SRC_LIST add.c div.c main.c mult.c sub.c)
// 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app ${SRC_LIST}) // 使用变量${变量名}
- 设置
C++
版本 (变量名是固定的)set(CMAKE_CXX_STANDARD 17)
- 指定输出的路径 在
CMake
中指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH
,它的值还是通过set
命令进行设置
set(HOME /home/robin/Linux/Sort) // 定义一个变量用于存储一个绝对路径
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin) // 将拼接好的路径值设置给EXECUTABLE_OUTPUT_PATH宏
搜索文件
如果一个项目里边的源文件很多,在编写
CMakeLists.txt
文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在CMake
中为我们提供了搜索文件的命令,可以使用aux_source_directory
命令或者file
命令
aux_source_directory(dir, variable)
dir
需要搜索的目录variable
将从dir
目录下搜索的源文件列表存储到该变量中
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app ${SRC_LIST})
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
GLOB
将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中GLOB_RECURSE
递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中
file(GLOB MAIN_SRC ${PROJECT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${PROJECT_SOURCE_DIR}/include/*.h)
- 宏
PROJECT_SOURCE_DIR
项目的根目录CMAKE_CURRENT_SOURCE_DIR
对应的是CMackLists.txt
的路径
- 两种方式的搜索结果都需要一个变量存储,但不需要提前创建。
- 包含头文件
include_directories(headpath)
include_directories(${PROJECT_SOURCE_DIR}/include)
制作库文件
有些时候我们编写的源代码并不需要将他们编译生成可执行程序,而是生成一些静态库或动态库提供给第三方使用
- 制作静态库
add_library(库名称(掐头去尾) STATIC 源文件...)
如果是动态库换成SHARED
- 指定库文件的输出路径
- 使用
LIBRARY_OUTPUT_PATH
宏 set(LIBRARY_OUTPUT_PATH, 输出的路径)
- 使用
- 链接静态库
- 如果要使用制作好的库文件,需要包含这些库中源代码的头文件
- 在
cmake
中,链接静态库的命令link_librarys(静态库名字(掐头去尾)...)
- 如果静态库不是系统提供的,需要指定静态库的路径
- 使用
link_directories(<lib path>...)
- 链接动态库
- 在
cmake
中,链接静态库的命令
- 在
target_link_libraries(
<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
target:指定要加载的库的文件的名字
该文件可能是一个源文件
该文件可能是一个动态库/静态库文件
该文件可能是一个可执行文件
PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC
动态库链接命令一般写在CMakeLists文件最后。先生成执行程序再链接动态库,而不是链接了再生成执行程序
可执行程序链接静态库。程序执行,静态库就被加载到虚拟内存中。
可执行程序连接动态库。程序执行,只有当程序使用到动态库才会被加载到内存中。
- 动态库也叫共享库,只需在内存中加载一次,多个进程就可以贡献这个动态库了。
日志
在CMake中可以用用户显示一条消息,该命令的名字为messages
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
错误, 终止所有处理过程
字符串操作
- 使用
set
拼接字符串set(变量名1 ${变量名1} ${变量名2} ...)
- 关于上面的命令其实就是将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据会对原数据就行覆盖。
- 使用
list
拼接字符串list(APPEND <list> [<element> ...])
list
命令的功能比set
要强大,字符串拼接只是它的其中一个功能,所以需要在它第一个参数的位置指定出我们要做的操作,APPEND
表示进行数据追加,后边的参数和set
就一样了。- 在
CMake
中,使用set
命令可以创建一个list
。一个在list
内部是一个由分号;
分割的一组字符串。 - 例如,
set(var a b c d e)
命令将会创建一个list:a;b;c;d;e
,但是最终打印变量值的时候得到的是abcde
。
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")
- 字符换删除
list(REMOVE_ITEM <list> <value> [<value> ...])
- 通过上面的命令原型可以看到删除和追加数据类似,只不过是第一个参数变成了
REMOVE_ITEM
。
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/*.cpp)
# 移除前日志
message(STATUS "message: ${SRC_1}")
# 移除 main.cpp
list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)
# 移除后日志
message(STATUS "message: ${SRC_1}")
$CMake$ 定义程序中的宏
add_definitions(-D宏名称)
嵌套的$CMake$
如果项目很大,或者项目中有很多的源码目录,在通过
CMake
管理项目的时候如果只使用一个CMakeLists.txt
,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个CMakeLists.txt
文件(头文件目录不需要),这样每个文件都不会太复杂,而且更灵活,更容易维护。
Linux
的目录是树状结构,所以嵌套的CMake
也是一个树状结构,最顶层的CMakeLists.txt
是根节点,其次都是子节点。因此,我们需要了解一些关于CMakeLists.txt
文件变量作用域的一些信息:当前节点CMakeLists.txt 中的变量可以在他的子树节点中使用
- 接下来我们还需要知道在 CMake 中父子节点之间的关系是如何建立的,这里需要用到一个 CMake 命令
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
一般只需指定第一项
source_dir:指定了CMakeLists.txt源文件和代码文件的位置,其实就是指定子目录