提示:CMake 相关功能还是挺多的,主要能构建三方面的内容:编译相关、安装相关、测试相关
文章目录
前言
接触 CMake 工具之前,通过大体了解,以为 CMake 只是用来构建跨平台的编译过程文件,比如:用于生成 Linux 下的 Makefile,用于生成 Windows 下的 VS 工程文件。进一步了解之后,发现 CMake 还是比较强大的一款跨平台工具。
一、CMake 是什么?
CMake 被人所知的最主要作用就是生成跨平台的编译过程文件,可以通过编写 CMakeLists.txt 文件,再执行 cmake 命令来生成适用于不同平台的编译过程文件。但除此之外,CMake 还能引导编译过程,生成测试文件,以及生成编译后的可执行文件的安装文件包。
众多功能中我们最常见的就是用于构建编译过程文件,也就是本文要记录的相关内容。
二、逐步提高
1. 从极简入手
先来单文件 main.c,需要生成可执行文件 main,需要在同一目录下生成 CMakeLists.txt 文件,内容如下:
# CMake 中,“ # ” 后面整行都是注释
add_executable(main main.c)
文件内容仅一句 add_executable() 即可,然后执行 cmake . (这里有个点),如果在 Linux 下,当前目录就会构建出 Makefile 文件,以及 CMake 相关文件,执行 Makefile,或者 cmake --build . (这里还是有个点),即可生成可执行程序 main
2. 基本规范
在上面的例子中,虽然生成了程序,但缺少很多约束,较高版本还会出现警告,所以一般我们这么写
# CMake 版本约束及警告
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
# 项目名称/版本号/编译语言
project(Demo1 VERSION 0.0.1 LANGUAGES CXX)
# 生成一个可执行文件,并指定入口函数 main
add_executable(demo main.c)
同时,我们在执行 cmake 命令前,会新建一个文件件,一般用 build,然后进入该文件夹内,执行 cmake …(这里是2个点) 再进一步执行 cmake --build .
3. CMake 规则
- CMake 会将涉及到的 CMakeLists.txt 文件都遍历完,再执行命令
- CMakeLists.txt 中很多语句的顺序可以调整,并不影响执行
- CMake 函数以()为结束,换行并不影响
- CMake 函数参数中间用空格隔开即可
- CMake 变量不需要声明
- 用一张不讲人话的大图来描述基本过程
4. 常用函数
先列出构建一个项目常用的函数,了解了函数就能进一步的拆解项目
1)生成可执行文件或库
# 生成一个可执行程序,参数:文件名,入口函数 main 所在文件
add_executable(demo main.cpp)
# 生成一个静态库/对象库/动态库,参数:库名,源文件
# 库类型可选 STATIC/OBJECT/SHARED,不写默认静态库
add_library(demo_static_lib STATIC lib.cpp)
# add_executable 也可以用这种方式指定函数实现
# 生成一个库,并用 target_sources 等指定文件包含
add_library(demo_lib_1 "")
# 指定私有的函数实现及共有的头文件,CMAKE_CURRENT_LIST_DIR 变量指代当前目录
# PRIVATE/PUBLIC 不能缺失,否则报错,指代编译后是否可对外调用
target_sources(demo_lib_1
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/demo_lib_1.cpp
PUBLIC demo_lib_1.hpp
)
# 指定包含头文件的文件夹,PUBLIC 不能缺失,否则报错,指代编译后是否可对外调用
target_include_directories(demo_lib_1
PUBLIC ${CMAKE_CURRENT_LIST_DIR}
)
2)将库连接至可执行文件
# 将生成好的库链接至可执行程序,参数:文件名,选项,库名
# 选项可选 PUBLIC/PRIVATE 用于设置共有或私有,不写默认私有
target_link_libraries(demo PRIVATE demo_lib_1 demo_lib_2 demo_lib_3)
# lz 为压缩库 lrt 为实时库 lm 数学库 lc 标准C库 dl 显式加载动态库的动态库
# 功能跟使用 gcc 编译链接外部库一样,较新版本的 gcc 已经默认带了部分库,不再需要额外指定
target_link_libraries(demo lz lrt lm lc dl)
3)引导CMake文件关联
# 引导 CMake 至下一级目录并关联 CMakeLists.txt 文件,不需要额外写文件名,但限制了引导的范围
add_subdirectory(src)
# 引导 CMake 至指定目录的 CMakeLists.txt 文件,需要写文件名
include(src/CMakeLists.txt)
4)指定生成的目录
# 将生成的静态链接库放入指定的目录,链接库时也会从该目录寻找
# CMAKE_BINARY_DIR 指代 CMake 过程文件运行目录,一般为 build
# CMAKE_INSTALL_LIBDIR 指代 CMake 构建后目录字符串,一般为 lib
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
# 将生成的动态链接库放入指定的目录,链接库时也会从该目录寻找
# CMAKE_ARCHIVE_OUTPUT_DIRECTORY 静态库目录,LIBRARY 动态库目录,RUNTIME 可执行文件目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
# 将生成的可执行文件放入指定目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
5)生成含有文件的变量
# 将当前目录的所有文件放入变量 DIR_SRCS,用于其他函数
# 多次调用不会导致覆盖,也就是 DIR_SRCS 变量中会包含多个目录中的文件,而不是只有最后一个
aux_source_directory(${CMAKE_CURRENT_LIST_DIR} DIR_SRCS)
aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/lib_dir_1 DIR_SRCS)
# 如果已经包含了顶层路径,并且源文件中显式指明了下级路径 #include "dir_sub/xxx.h"
# 那么 CMakeLists 文件中可以省略下级路径的包含
# aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/lib_dir_1/dir_sub)
6)生成配置宏
# 生成配置宏,用于条件编译,可以直接影响代码
target_compile_definitions(demo PUBLIC "USER_DEF")
7)打印消息
# 打印消息,用于测试变量内容,提醒用户等
message("print a message. ")
# 打印一个严重错误还能在生成出错时,直接跳出
message(FATAL_ERROR "show a err, and exit cmake")
5. 大型项目构建
大型的项目经常包含较多的文件,文件目录可能较为复杂,或者需要生成一些动态或静态库方便其他项目使用,我们尽量简化代码,使用满足一般项目构建的函数,只展示项目构建所需流程及操作。
1)目录结构
.
├── build // 用于生成编译过程文件,目前为空
├── CMakeLists.txt // 项目入口 CMakeLists.txt,内容主要为项目信息
├── external // 外部文件库
│ ├── CMakeLists.txt // 用于生成一个外部库
│ ├── lib_ext.c
│ └── lib_ext.h
└── src // 主代码库
├── CMakeLists.txt
├── lib_bro.c
├── lib_bro.h
├── lib_dir_1
│ ├── CMakeLists.txt // 常规代码目录
│ ├── lib_init.c
│ └── lib_init.h
├── lib_dir_2 // 较多代码的目录
│ ├── CMakeLists.txt
│ ├── lib_add.c
│ ├── lib_add.h
│ └── lib_dir_2_1 // 并非每个目录下都需要有 CMakeLists.txt
│ ├── lib_sub.c
│ └── lib_sub.h
├── lib_dir_3 // CMakeLists.txt 文件的数量并不影响项目生成
│ ├── lib_io.c
│ └── lib_io.h
└── main.c
2)目标文件关系
项目生成了三个库文件,lib_ext.a 为外部代码生成库,lib_bro.so 为 main 同目录代码生成库,lib_src.a 为主代码库,生成后将他们放入了指定的目录下,最后再链接至 demo 可执行文件
[ 9%] Building C object CMakeFiles/lib_ext.dir/external/lib_ext.c.o
[ 18%] Linking C static library lib/liblib_ext.a
[ 18%] Built target lib_ext
Scanning dependencies of target lib_bro
[ 27%] Building C object src/CMakeFiles/lib_bro.dir/lib_bro.c.o
[ 36%] Linking C shared library ../lib/liblib_bro.so
[ 36%] Built target lib_bro
Scanning dependencies of target lib_src
[ 45%] Building C object src/CMakeFiles/lib_src.dir/lib_dir_1/lib_init.c.o
[ 54%] Building C object src/CMakeFiles/lib_src.dir/lib_dir_2/lib_add.c.o
[ 63%] Building C object src/CMakeFiles/lib_src.dir/lib_dir_2/lib_dir_2_1/lib_sub.c.o
[ 72%] Building C object src/CMakeFiles/lib_src.dir/lib_dir_3/lib_io.c.o
[ 81%] Linking C static library ../lib/liblib_src.a
[ 81%] Built target lib_src
Scanning dependencies of target demo
[ 90%] Building C object src/CMakeFiles/demo.dir/main.c.o
[100%] Linking C executable ../bin/demo
[100%] Built target demo
3)文件代码
文件代码就不贴图了,请移步下载区获取
https://download.csdn.net/download/forlorg/86237363
总结
使用 CMake 工具来构建大型项目,在保持灵活性的同时可用省下很多劳动,并且该工具可用跨平台使用,给开发带来了很大的好处。