在 Linux 中,经常使用 GNU 构建系统,也就是利用脚本和 make 程序在特定平台上构建软件,对于简单的程序来说是可以的,当项目变得复杂之后会很难进行维护。
而目前在维护 C/C++ 项目时,使用比较多的是 CMake ,这里就详细介绍下 CMake 的使用。
简介
CMake 最开始是开发 ITK、VTK 几个工具时所产生的衍生品,而在 KDE4 使用 CMake 替换原有的编译系统之后,越来越多的项目开始使用 CMake ,到目前为止,应该是最为流行的 C/C++ 构建工具。
安装
最简单的是使用 OS 的安装工具,例如 CentOS 的 YUM 安装,不过一般来说 OS 的原生 CMake 版本比较低,而很多源码包会依赖更高版本,那么就可能会出现类似 CMake 3.5 or higher is required. You are running version 2.8.12
的报错信息,这时候就需要升级版本。
可以从官网 cmake.org 下载所需版本,包括源码,也可以是二进制,不过建议直接使用二进制,这样更方便些。
根据平台下载相关的二进制包,这里以 Linux 的为例,可以将老版本卸载,或者添加到 /opt
目录下,这里采用后者,如果想卸载新版本直接删除文件即可。
----- 直接解压并将二进制文件复制到/opt/cmake目录下
# mkdir /opt/cmake && cp cmake-3.17.5-Linux-x86_64/* /opt/cmake
----- 添加到PATH环境变量中
$ export PATH=/opt/cmake/bin:${PATH}
在安装时,可能会遇到类似 CMake Error: Could not find CMAKE_ROOT !!!
的报错,通常是非标准安装引起的,例如上述安装到 /opt
非标准目录,以 3.17.5 版本为例,需要确保执行 cmake
的用户可以访问 CMake 提供的基础模块。
可能会在访问模块时因为权限不足引起,可以通过 ls -alh /opt/cmake/share/cmake-3.17/Modules/CMake.cmake
命令查看是否有权限访问。
简单介绍
在使用时通过 CMakeLists.txt
文件配置如何进行编译,其基本结构可以简单描述为:
- 依赖主
CMakeLists.txt
文件,项目主目标只有一个,主目录中可指定包含的子目录; - 在项目
CMakeLists.txt
中使用PROJECT
指定项目名称,通过ADD_SUBDIRECTORY
添加子目录; - 子目录
CMakeLists.txt
将从父目录CMakeLists.txt
继承设置。
另外,上述通过 PROJECT
设置好工程后,可以通过 ${hello_SOURCE_DIR}
引用,注意大小写。
因为 CMakeLists.txt
可执行脚本并通过脚本生成一些临时文件,因此 CMake 无法跟踪到底产生了那些临时文件,因此,没有办法提供一个可靠的 make distclean
方案,为此可以使用外部编译。
另外,CMake 没提供类似 make uninstall
的卸载命令,可以通过 cat install_manifest.txt | sudo xargs rm
命令执行卸载或者删除操作。
变量
可以用 SET(name value)
设置变量,并通过 ${name}
使用变量,一般来说无需释放,但是可以通过 UNSET(name)
提前释放,而且可以使用环境变量,只是添加了 ENV
作为开头,例如 SET(ENV{PATH} "$ENV{PATH}:/opt/bin")
修改 PATH
路径。
注意,这样设置的环境变量仅影响当前 CMake 执行,当运行结束后环境变量也就跟着失效,而且不影响 build 阶段。
另外,可以同过 SET(name value... CACHE type "doc string" [FORCE])
将变量缓存到 CMakeCache.txt
文件中,而且为了方便,可以使用 OPTION(opt help-string [initial])
设置 BOOL
类型变量,其等价于 CACHE BOOL
。
在命令行中就可以通过如下方式使用。
cmake -D foo=ON ...
cmake -D foo:BOOL=ON ...
内置变量
如下是常见的系统变量,可以直接使用。
CMAKE_SOURCE_DIR 最顶层的CMake文件所在路径
CMAKE_CURRENT_SOURCE_DIR 当前CMake所在路径,可通过这两个变量判断是否为add_subdirectory添加的子模块
PROJECT_SOURCE_DIR 通过PROJECT()指定的CMake所在路径,一般也就是顶层CMake路径
CMAKE_BUILD_TYPE 编译类型,例如 Debug Release
使用时同样可以通过命令行覆盖,例如 cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Debug
。
除此之外,还有与编译器相关的参数,如下是设置 C 编译器的参数,对于 CPP 则将 C
替换为 CXX
即可。
set(CMAKE_C_COMPILER "gcc" ) # 显示指定使用的编译器
set(CMAKE_C_FLAGS "-std=c99 -Wall") # 设置编译选项,也可以通过add_definitions添加编译选项
set(CMAKE_C_FLAGS_DEBUG "-O0 -g" ) # 调试包不优化
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG " ) # release包优化
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-compare") # 忽略某些告警
Build Type
除了上述 CMAKE_C_FLAGS_DEBUG
指定不同类型的编译选项外,还可以通过如下方式指定。
SET(CMAKE_BUILD_TYPE Debug CACHE STRING "set build type to debug")
IF(NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug")
SET(CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}")
SET(LIBRARIES Irrlicht_S.lib)
ELSE()
SET(LIBRARIES Irrlicht.lib)
ENDIF()
可以在命令行中通过如下方式编译 cmake -DCMAKE_BUILD_TYPE=Debug ..
,最终的编译选项可以查看 CMakeFiles/SRCFILE.dir/flags.make
中的 C_FLAGS
选项,一般是 CMAKE_C_FLAGS+CMAKE_C_FLAGS_MODE
。
模块使用
可以通过模块进行扩展,标准模块保存在 /usr/share/cmake-X.YY/Modules
目录下,其中 X.YY
为 CMake 的版本。
另外,还可以自己扩展模块,例如 C 的覆盖率,除了添加到上述目录中,最好使用 CMAKE_MODULE_PATH
变量指定保存路径,改变量默认为空。
简单示例
CMake 有内部和外部构建两种方式,所谓的内部构建直接在源码当前目录下编译,直接执行 cmake .
命令即可;而外部构建则是在一个新目录下编译,这样不会影响源码的结构,通常使用后者。
最简单示例单文件输出 Hello World!
,只需创建两个文件 main.c
和 CMakeLists.txt
即可。
$ cat CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(hello) # 项目名称
SET(SRC_LIST main.c) # 添加源码,可忽略
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR}) # 打印信息
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
$ cat main.c
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
$ mkdir build && cd build && cmake .. && make
如上的编译方式就是外部构建,也可以在源码目录下执行 cmake . && make
命令编译,要查看详细信息可以 make VERBOSE=1
或者 VERBOSE=1 make
。
在上述 MESSAGE()
中,会打印变量用于调试,该变量是通过 PROJECT()
默认设定的变量,详见 PROJECT()
的使用。调试的话可以使用 cmake . -DCMAKE_BUILD_TYPE=Debug
,不过此时使用的绝对地址。
常见问题
这里简单列举常见的文件所包含内容,遇到问题时就可以按需查看。
CMakeFiles/CMakeOutput.log
包含了详细的编译日志,可以查看编译选项、头文件等
3.5 兼容性
从 3.27
版本开始,如果配置了 CMake 最小版本要求,那么可能会有 Compatibility with CMake < 3.5 will be removed from a future version of CMake.
的报错,主要是版本的兼容性问题提示。
可以通过如下方式修改。
----- 查看当前的版本
$ cmake --version
----- 修改 CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
只要修改到 3.5
版本以上就可以。
参考
- 关于 CMake 相关的文档可以参考 《CMake 实践》,或者 CMake 入门实战 以及 CMake Tutorial;搭建 GTest 环境可以参考 Unit testing with GoogleTest and CMake 。
- 常见的命令参考 cmake-commands 。