CMake 使用简介

2019-02-11 language c/cpp

在 Linux 中,经常使用 GNU 构建系统,也就是利用脚本和 make 程序在特定平台上构建软件,对于简单的程序来说是可以的,当项目变得复杂之后会很难进行维护。

而目前在维护 C/C++ 项目时,使用比较多的是 CMake ,这里就详细介绍下 CMake 的使用。

简介

cmake logo

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 文件配置如何进行编译,其基本结构可以简单描述为:

  1. 依赖主 CMakeLists.txt 文件,项目主目标只有一个,主目录中可指定包含的子目录;
  2. 在项目 CMakeLists.txt 中使用 PROJECT 指定项目名称,通过 ADD_SUBDIRECTORY 添加子目录;
  3. 子目录 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.cCMakeLists.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 版本以上就可以。

参考