CMake —— 构建编译过程相关函数及项目示例

CMake不仅用于生成跨平台的编译文件,还能管理编译过程、测试和安装。从简单到复杂,通过CMakeLists.txt文件,可以定义项目结构、创建可执行文件和库、管理依赖、指定输出目录。大型项目中,CMake能简化多文件、多目录的构建,提高开发效率。
摘要由CSDN通过智能技术生成

提示: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 规则

  1. CMake 会将涉及到的 CMakeLists.txt 文件都遍历完,再执行命令
  2. CMakeLists.txt 中很多语句的顺序可以调整,并不影响执行
  3. CMake 函数以()为结束,换行并不影响
  4. CMake 函数参数中间用空格隔开即可
  5. CMake 变量不需要声明
  6. 用一张不讲人话的大图来描述基本过程
    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 工具来构建大型项目,在保持灵活性的同时可用省下很多劳动,并且该工具可用跨平台使用,给开发带来了很大的好处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值