【译】CMake 构建工具
CMake 是开源的跨平台的构建工具,在开发社区非常的出名,以及各种项目都有用到,本文来了解下 CMake 为何厉害。内容来源:https://aosabook.org/en/cmake.html原文标题:CMake作者:Bill Hoffman && Kenneth Martin译者:流左沙注:翻译较原文会有一定的精简、重排和添删,主要是为了提取重要内容以达到更好的理解,请以原文
CMake 是开源的跨平台的构建工具,在开发社区非常的出名,以及各种项目都有用到,本文来了解下 CMake 为何厉害。
内容来源: https://aosabook.org/en/cmake.html
原文标题:CMake
作者:Bill Hoffman && Kenneth Martin
译者:流左沙
注:翻译较原文会有一定的精简、重排和添删,主要是为了提取重要内容以达到更好的理解,请以原文为准。
In addition to a build system, over the years CMake has evolved into a family of development tools: CMake, CTest, CPack, and CDash. CMake is the build tool responsible for building software. CTest is a test driver tool, used to run regression tests. CPack is a packaging tool used to create platform-specific installers for software built with CMake. CDash is a web application for displaying testing results and performing continuous integration testing.
除了构建系统,CMake 还发展出一系列工具:CMake 是构建工具,CTest 是用于回归测试的测试工具,CPack 是用于为用 CMake 构建的软件创建指定平台安装器的打包工具,CDash 是展示测试结构和执行持续集成测试的 web 端应用。
Notion
The build tree is the directory hierarchy in which all generated files are placed. Generated files consist of the makefile, the compiled object files, and a dependency file (with a . d extension) for each source file.
构建树是放置所有生成文件的目录层级结构。生成文件包括 makefile、编译出来的目标文件、每个源文件的依赖文件。
Requirements
When CMake was being developed, the normal practice for a project was to have a configure script and Makefiles for Unix platforms, and Visual Studio project files for Windows. This duality of build systems made cross-platform development very tedious for many projects: the simple act of adding a new source file to a project was painful. The obvious goal for developers was to have a single unified build system. The developers of CMake had experience with two approaches of solving the unified build system problem.
在 CMake 被开发之时,常规的项目操作是:一个配置脚本,以及 Makefiles (Unix 平台) 或 Visual Studio 项目文件(Windows 平台)。这样的构建系统导致跨平台开发非常地难受:简单地添加一个文件到项目的操作都很痛苦。对于开发者而言,一个显然的目标就是一个统一的构建系统。
The basic constraints of the new build system would be as follows: 对于新的构建系统的限制如下
- Depend only on a C++ compiler being installed on the system. 只依赖系统安装的 c++ 编译器
- It must be able to generate Visual Studio IDE input files. 可以生成 VS IDE 的输入文件
- It must be easy to create the basic build system targets, including static libraries, shared libraries, executables, and plugins. 很容易创建目标文件
- It must be able to run build time code generators. 可以运行编译时代码生成器
- It must support separate build trees from the source tree. 可以支持独立于源树的构建树
- It must be able to perform system introspection, i.e., be able to determine automatically what the target system could and could not do. 反映系统问题
- It must do dependency scanning of C/C++ header files automatically. 自动地进行 C/C++ 头文件的依赖扫描
- All features would need to work consistently and equally well on all supported platforms. 所有特性需要协调工作,且良好运行在所有支持的平台
In order to avoid depending on any additional libraries and parsers, CMake was designed with only one major dependency, the C++ compiler (which we can safely assume we have if we're building C++ code). This did limit CMake to creating its own simple language, which is a choice that still causes some people to dislike CMake. However, at the time the most popular embedded language was Tcl. If CMake had been a Tcl-based build system, it is unlikely that it would have gained the popularity that it enjoys today.
为了避免依赖额外的库或解析器,CMake 只存在一种主要的依赖:C++ 编译器。这限制了 CMake 创建属于自己的语言,引起大家的不满。
The ability to generate IDE project files is a strong selling point for CMake, but it also limits CMake to providing only the features that the IDE can support natively. However, the benefits of providing native IDE build files outweigh the limitations. Although this decision made the development of CMake more difficult, it made the development of ITK and other projects using CMake much easier. Developers are happier and more productive when using the tools they are most familiar with. By allowing developers to use their preferred tools, projects can take best advantage of their most important resource: the developer.
可以生成 IDE 项目文件的能力是 CMake 的一大卖点,但同样也限制了 CMake 只去提供 IDE 本地支持的一些特性。但是,利大于弊。
Another early CMake requirement also came from autotools: the ability to create build trees that are separate from the source tree. This allows for multiple build types to be performed on the same source tree. It also prevents the source tree from being cluttered with build files, which often confuses version control systems.
支持创建独立于源树的构建树。这个可以使相同的源树拥有众多的构建类型,同时也防止源树被构建文件污染,导致版本控制系统的混乱。
Implementation
Environment Variables (or Not)
The trouble with this approach is that for the build to work, all of these external variables need to be set each time a build is performed. To solve this problem CMake has a cache file that stores all of the variables required for a build in one place. These are not shell or environment variables, but CMake variables. The first time CMake is run for a particular build tree, it creates a CMakeCache.txt
file which stores all the persistent variables for that build. Since the file is part of the build tree, the variables will always be available to CMake during each run.
难点在于在每次编译时,所有的外部变量都需要设置一遍。为了解决这个问题,CMake 用一个 cache 文件保存所有编译所需的变量。这些不是 shell 或环境变量,只是 CMake 的变量。第一次 CMake 运行时,它会创建 CMakeCache.txt 文件来保存这些变量。这个文件就是构建树的一部分,所以对于 CMake 的每次运行都是有效的。
The Configure Step
During the configure step, CMake first reads the CMakeCache.txt
if it exists from a prior run. It then reads CMakeLists.txt
, found in the root of the source tree given to CMake. During the configure step, the CMakeLists.txt
files are parsed by the CMake language parser. Each of the CMake commands found in the file is executed by a command pattern object. Additional CMakeLists.txt
files can be parsed during this step by the include
and add_subdirectory
CMake commands. CMake has a C++ object for each of the commands that can be used in the CMake language. Some examples of commands are add_library
, if
, add_executable
, add_subdirectory
, and include
. In effect, the entire language of CMake is implemented as calls to commands. The parser simply converts the CMake input files into command calls and lists of strings that are arguments to commands.
在配置阶段,CMake 首先读取 CMakeCache.txt 文件(如果存在的话),然后读取 CMakeLists.txt 来查找源树的根。在配置阶段,CMakeLists.txt 文件会被 CMake 语言解析器解析。每当有 CMake 命令在这个文件中被找到就会被参数模版对象执行。额外的 CMakeLists.txt 文件会在处理 include 和 add_subdirectory 命令之时解析。
The configure step essentially "runs" the user-provided CMake code. After all of the code is executed, and all cache variable values have been computed, CMake has an in-memory representation of the project to be built. This will include all of the libraries, executables, custom commands, and all other information required to create the final build files for the selected generator. At this point, the CMakeCache.txt
file is saved to disk for use in future runs of CMake.
配置阶段本质上会运行用户提供的 CMake 的代码。在所有代码执行完成,以及所有变量值计算完成之后,CMake 会有一个项目的内存表示需要构建,它会包含所有的库、可执行文件、指定的命令和所有其他需要去创建最终的构建文件的信息。在此,CMakeCache.txt 文件会被保存到磁盘中,作为 CMake 之后的使用。
The Generate Step
Once the configure step has been completed, the generate step can take place. The generate step is when CMake creates the build files for the target build tool selected by the user. At this point the internal representation of targets (libraries, executables, custom targets) is converted to either an input to an IDE build tool like Visual Studio, or a set of Makefiles to be executed by make
. CMake's internal representation after the configure step is as generic as possible so that as much code and data structures as possible can be shared between different built tools.
一旦配置完成,就到生成阶段了。这个阶段 CMake 会创建用户选择的目标构建工具的构建文件。在此,目标的内部表示会被转成 IDE 的构建工具,或者 make 用到的一系列 Makefiles。CMake 的内部表示需要尽可能通用,这样代码和数据结构可以被不同的构建工具所共享。
The Code
CMake is an object-oriented system using inheritance, design patterns and encapsulation
CMake 是使用了继承的面向对象的系统
The results of parsing each CMakeLists.txt
file are stored in the cmMakefile
object. In addition to storing the information about a directory, the cmMakefile
object controls the parsing of the CMakeLists.txt
file. The parsing function calls an object that uses a lex/yacc-based parser for the CMake language. Since the CMake language syntax changes very infrequently, and lex and yacc are not always available on systems where CMake is being built, the lex and yacc output files are processed and stored in the Source
directory under version control with all of the other handwritten files.
解析 CMakeLists.txt 文件的结果被保存到 cmMakefile 对象当中。除了保存目录信息,它还控制文件的解析。解析函数可以使用 CMake 语言的解析器。CMake 语法不怎么变化,解析器在 CMake 构建时并非总是活跃的,解析的结果会被保存在 Source 目录下。
Another important class in CMake is cmCommand
. This is the base class for the implementation of all commands in the CMake language. Each subclass not only provides the implementation for the command, but also its documentation.
另一个重要的类是 cmCommand。这是 CMake 命令的基类。每个子类不仅提供命令的实现,还有它们的文档说明。
Dependency Analysis
Since Integrated Development Environments (IDEs) support and maintain file dependency information, CMake skips this step for those build systems. For IDE builds, CMake creates a native IDE input file, and lets the IDE handle the file level dependency information. The target level dependency information is translated to the IDE's format for specifying dependency information.
由于 IDE 本身支持和持有文件依赖信息,CMake 就可以跳过这个步骤。IDE 构建时,CMake 创建一个本地的 IDE 输入文件,然后让 IDE 去处理文件级别的依赖信息。目标级别的依赖信息会被转成 IDE 的格式来指明依赖信息。
With Makefile-based builds, native make programs do not know how to automatically compute and keep dependency information up-to-date. For these builds, CMake automatically computes dependency information for C, C++ and Fortran files. Both the generation and maintenance of these dependencies are automatically done by CMake. Once a project is initially configured by CMake, users only need to run make
and CMake does the rest of the work.
在使用 Makefile 构建,本地 make 程序不需要知道怎么自动计算和保存依赖信息为最新。构建时,CMake 自动的计算这些依赖信息,包括生成和保存。一旦 CMake 初始配置好了项目,用户只需要去运行 make,然后 CMake 会把剩下的工作完成。
Although users do not need to know how CMake does this work, it may be useful to look at the dependency information files for a project. This information for each target is stored in four files called depend.make
, flags.make
, build.make
, and DependInfo.cmake
. depend.make
stores the dependency information for all the object files in the directory. flags.make
contains the compile flags used for the source files of this target. If they change then the files will be recompiled. DependInfo.cmake
is used to keep the dependency information up-to-date and contains information about what files are part of the project and what languages they are in. Finally, the rules for building the dependencies are stored in build.make
. If a dependency for a target is out of date then the depend information for that target will be recomputed, keeping the dependency information current. This is done because a change to a .h file could add a new dependency.
依赖信息会被保存在 4 个文件当中,depend.make, flags.make, build.make, DependInfo.cmake 文件。depend.make 保存所有目录下的目标文件的依赖信息。flags.make 含有从源文件到目标的编译参数,如果参数被更改,这个文件也会被重新编译。DependInfo.cmake 是用来保证依赖信息是最新的,保存有哪些文件是项目的一部分以及它们是什么语言的信息。最后,构建这些依赖的规则会被保存在 build.make 文件中。如果目标的依赖过期了,那么依赖信息会被重新计算,保证依赖信息最新。这样做是因为头文件可能会添加新的依赖。
CTest and CPack
The ctest
executable is used to run regression tests. A project can easily create tests for CTest to run with the add_test
command. The tests can be run with CTest, which can also be used to send testing results to the CDash application for viewing on the web. CTest and CDash together are similar to the Hudson testing tool. They do differ in one major area: CTest is designed to allow a much more distributed testing environment. Clients can be setup to pull source from version control system, run tests, and send the results to CDash. With Hudson, client machines must give Hudson ssh access to the machine so tests can be run.
ctest 可执行文件用于跑回归测试。一个项目可以通过 add_test 命令很方便地创建测试。由 CTest 来执行,所以也可以把测试结果发给 CDash 来做 web 端展示。
The cpack
executable is used to create installers for projects. CPack works much like the build part of CMake: it interfaces with other packaging tools. For example, on Windows the NSIS packaging tool is used to create executable installers from a project. CPack runs the install rules of a project to create the install tree, which is then given to a an installer program like NSIS. CPack also supports creating RPM, Debian .deb
files, .tar
, .tar.gz
and self-extracting tar files.
cpack 可执行文件用来创建项目的安装器。
Reference
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)