CMake Template
常用 CMake 模板,边学边更新
环境
我个人使用的是 Ubuntu18.04,可以通过 sudo apt install cmake
来安装 CMake,在终端输入 cmake --version
可查看版本。
简单介绍
CMake 是个一个开源的跨平台自动化建构系统,用来管理软件建置的程序,并不依赖于某特定编译器,并可支持多层目录、多个应用程序与多个库。 它用配置文件控制建构过程 (build process) 的方式和 Unix 的 make 相似,只是 CMake 的配置文件取名为 CMakeLists.txt。CMake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix的Makefile 或 Windows Visual C++的projects/workspaces),然后再依一般的建构方式使用。
上面这句话摘自 Wiki,它说的是什么意思?如果你跟我一样在 linux 环境下工作,那么你一定听过 make,make 能够根据 makefile 文件中的说明来一步步地自动构建目标文件;make 是一种工具(可执行程序),makefile 是文件,里面说明了如何去构建目标文件,make 程序会从 makefile 中一条条地读出相关的指令去执行。那么 CMake 和 makefile 有什么关系呢?在没有 CMake 之前,我们需要自己动手写 makefile,然后去运行 make,而这需要你去学习 makefile 的语法,十分费时费精力;而现在有了 CMake,它可以根据你的指示(都写在 CMakeLists.txt 中)自动地去生成 makefile,然后你再用 make 去构建目标文件即可。写 CMakeLists.txt 比写 makefile 可容易太多了!
源文件与头文件都在根目录下
这种是最简单的情况,例如我的目录下有这些文件:
1 | . |
如果在 g++ 中执行时命令是这样的:g++ hello.c main.c -o main
那么我可以在当前目录下编写 CMakeLists.txt:
1 | cmake_minimum_required(VERSION 2.8) |
其中:
cmake_minimum_required(VERSION)
用来表示可接受的 CMake 最低版本project(name)
用来定义项目名称add_executable(name sources)
第一个参数是项目名称,第二个参数是源文件名(多个文件名之间用空格隔开)
编写完 CMakeLists.txt 文件后,执行以下命令:
1 | $ mkdir build |
可以看到目标文件已经构建完成!现在的目录层级是这样的:
1 | . |
这里创建 build 目录是为了更好的层次化管理文件,build 目录内放置所有的二进制文件,而源文件和头文件一般都在项目根目录下
内置变量
CMake 定义了相当丰富的变量,然而,我常用的也就那几个。
name | description |
---|---|
PROJECT_BINARY_DIR | 是指包含最近的 project() 命令的 build 目录 |
PROJECT_SOURCE_DIR | 是指包含最近的 project() 命令的 CMakeLists.txt 的目录 |
CMAKE_CURRENT_BINARY_DIR | 当前处理的 CMakeLists.txt 所在的 build 目录 |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的 CMakeLists.txt 所在的目录 |
CMAKE_SOURCE_DIC | 指定义了顶级 CMakeLists.txt 的目录 |
EXECUTABLE_OUTPUT_PATH | 生成的可执行文件的存储目录 |
LIBRARY_OUTPUT_PATH | 生成的库的存储目录 |
PROJECT_NAME | 项目名称 |
PROJECT_VERSION_MAJOR | 项目主版本号(例如 2.8 的主版本号是 2) |
PROJECT_VERSION_MINOR | 项目次版本号 (例如 2.8 的次版本号是 8) |
PROJECT_VERSION_PATCH | 项目版本的补丁号(例如 2.8.1 的补丁号是 1) |
BUILD_SHARED_LIBS | 用于控制 cmake 的 add_library 指令是否默认生成 动态so(yes if flag=on)还是 静态库.a (if flag=off)。默认是 flag=on |
CMAKE_C_FLAGS | 编译器 gcc 的标志 |
CMAKE_CXX_FLAGS | 编译器 g++ 的标志 |
记不住变量的值时,可以使用 cmake 的 message 函数输出变量值。
1 | cmake_minimum_required(VERSION 2.8) |
源文件和头文件分开存放
现在有以下目录结构:
1 | . |
这种情况下的 CMakeLists.txt 文件应该这样编写:
1 | cmake_minimum_required(VERSION 2.8) |
set(name value) 可以自己定义一个变量,name 是变量名,value 是变量值
这里 PROJECT_SOURCE_DIR 的值就是 ./ (项目根目录)
target_include_directories(
你可以在 make 时开启输出模式 make VERBOSE=1
来看看编译器的参数,应该是:g++ -I 根目录/include 根目录/hello.cc 根目录/main.cc -o 根目录/build/main
找路径
cmake find_path 命令用来寻找包含指定文件名称的目录。
通常,它的签名如下:
1 | find_path ( |
<VAR>
是一个变量,用于存放该命令得到的结果。它有点像 find 命令,如果在某个目录下找到了指定的文件名,目录名将会存到 <VAR>
变量中,并且 find 搜索将会停止!如果没有找到,结果将会是<VAR>-NOTFOUND
。
举个栗子就懂了,例如 muduo-tutorial 项目 cmake 目录下的 CMakeLists.txt 文件中有这样两行:
1 | # set(MUDUO_PATH "/opt/muduo_hdrs_libs") |
1 | $ ls /opt/muduo_hdrs_libs |
因此我这里 Muduo_INCLUDE_DIR 就是 /opt/muduo_hdrs_libs/include
,Muduo_LIBRARY_DIR 就是 /opt/muduo_hdrs_libs/lib
。
生成静态库或共享库文件
可以使用 add_library(libname, srcs) 来生产静态库
参考
1.https://mlog.club/article/1918025
2. https://cmake.org/cmake/help/latest/guide/tutorial/index.html#packaging-debug-and-release-step-12