#cmake
- 原文:[https://aosabook.org/en/v1/cmake.html](https://aosabook.org/en/v1/cmake.html)
- CMake 已经发展出了一系列开发工具:
- CMake
- CTest
- CPack
- CDash(web应用,展示测试结果、持续集成测试)
------
# 1. CMake History and Requirements
- CMake 设计上只有一个主要的依赖:C++ compiler
- CMake Language
- 编译和安装如 Tcl 等 scripting languages,在一些系统上很困难
- 不想引入其他额外的依赖
- 因此,这限制了 CMake 只能创造自己的语言
- CMake 的一大卖点:可以生成 IDE project files
- 跨平台支持不同的 builiding blocks:
- executables
- static libraries
- shared libraries
- plugins
- build trees 和 source tree 分离
- 相同源代码可以执行多种不同的 build types
- 管理依赖
- 例如源文件有变动,触发重写编译
----------
# 2. How CMake Is Implemented
- CMake 的开发语言是 C 和 C++
## 2.1 The CMake Process
- CMake 有两个主要的阶段:
1. **configure** 步骤:处理给定的所有输入、创建需要执行的构建的内部表示( an internal representation of the build to be performed)
2. **generate** 步骤:创建实际的 build files
### Environment Variables(Or Not)
- 许多 build system 在 build 时都使用了 shell 级别的环境变量
- 缺点:每次 build 时需要先设置环境变量
- CMake:使用一个 **cache file** 来存储 build 时需要的所有变量
- not shell or environment variables,but CMake variables
- `CMakeCache.txt`
### The Configure Step
- CMake 首先读取 `CMakeCache.txt`(如果之前运行生成了该文件)
- 然后读取 `CMakeLists.txt`(在给定 source tree 的根目录下)
- 在 configure 阶段,CMake language parer 会解析 `CMakeLists.txt`
- 文件内的每个 commands 都由一个 command pattern object 执行。
- 期间也会解析 `include` 和 `add_subdirectory` 引入的 commands
- 每个 command 都有一个 C++ object:
- 例如:`add_library`, `if`, `add_executable`, `include` 等
- 效果上,CMake 的整个语言就是对 commands 的调用
- pareser 解析出 command calls 以及 commands 的参数列表
- configure 步骤实质上就是执行用户提供的 CMake code:
- 当所有 code 执行完后,所有 cache variables values 都计算好,CMake就有了要构建的项目的内存表示( in-memory representation)。
- 包括所有的 libraries,executables,custom commands 以及为指定 generator 创建最终 build files 所需的其他信息。
- 此时将 `CMakeCache.txt` 保存到磁盘以便下次运行 CMake。
- project 的 in-memory representation 是 targets 的集合
- 如 libraries 和 executables, 同时也支持 custom targets
- CMake 将每个 target 存储在一个 `cmTarget` 对象中
- 这些对象依次存储在 `cmMakefile` 对象中(它存储了给定目录 source tree 中的所有 targets)
- 即最终结果:a tree of `cmMakefile` objects containing maps of `cmTarget` objects.
### The Generate Step
- 当 configure 步骤完成后,就可以执行 generate 步骤,为用户指定的 build tool 生成 build files。
- targets 的内部表示被转换成:
- an input to an IDE build tool like Visual Studio
- 或者:a set of Makefiles to be executed by `make`
- configure 步骤生成的 CMake's internal representation 需要尽可能通用,来兼顾不同的 build tools。

## 2.2 CMake: The Code
### CMake Objects
- CMake 是一个面向对象的系统,使用了继承、设计模式和封装。
- 
- 每个 `CMakeLists.txt` 文件的解析结果存储到 `cmMakefile` 对象中
- 为了存储关于一个目录的信息,`cmMakefile`还控制 `CMakeLists.txt` 的解析
- Parser 基于 lex/yacc
- 另一个重要的类是 `cmCommand` ,它是所有 commands 的基类。
- 每个子类不仅提供了 command 的实现,也提供了文档。
```cpp
/// cmUnsetCommand 类
virtual const char* GetTerseDocumentation()
{
return "Unset a variable, cache variable, or environment variable.";
}
/**
* More documentation.
*/
virtual const char* GetFullDocumentation()
{
return
" unset(<variable> [CACHE])\n"
"Removes the specified variable causing it to become undefined. "
"If CACHE is present then the variable is removed from the cache "
"instead of the current scope.\n"
"<variable> can be an environment variable such as:\n"
" unset(ENV{LD_LIBRARY_PATH})\n"
"in which case the variable will be removed from the current "
"environment.";
}
```
### Dependency Analysis
- 为单个 Fortran,C 和 C++ 源文件内置了强大的依赖分析能力。
- 每个 target 的依赖信息存储在四个文件中:
- **`depend.make`:** 存储了目录中所有 object files 的依赖信息
- **`flags.make`:** 保护了了该 target 源文件的 compile flags
- **`DependInfo.cmake`:** 用于使依赖信息保持最新, 包含了哪些文件是 project 的一部分、它们是什么语言等信息
- **`build.make`:** 存储了构建依赖项目的规则
- 如果 target 的某个依赖 out-of date 了, target的依赖信息会重写计算,保持最新。
- 这么做是因为修改 `.h` 文件可能会添加新的依赖。
### Ctest and CPack
- ctest 可执行文件用于运行回归测试。
- 使用 `add_test` 来创建测试用例。
- 测试结果可以发往 CDash 应用
- cpack 可执行文件用于创建 installers
- 工作原理类似 CMake,借助于其他 packing tools(RPM、WIndows NSIS packaging)