【译】CMake 构建工具

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_libraryifadd_executableadd_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.makeflags.makebuild.make, and DependInfo.cmakedepend.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

https://aosabook.org/en/cmake.h

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一,初识 cmake Cmake不再使你在构建项目时郁闷地想自杀了. 1,背景知识: cmake是kitware公司以及一些开源开发者在开发几个工具套件(VTK)的过程中衍生品,最终形成体系, 成为一个独立的开放源代码项目。项目的诞生时间是2001年。其官方网站是www.cmake.org,可以通 过访问官方网站获得更多关于cmake的信息。cmake的流行其实要归功于KDE4的开发(似乎跟当年的 svn一样,KDE将代码仓库从CVS迁移到SVN,同时证明了SVN管理大型项目的可用性),在KDE 开发者使用了近10年autotools之后,他们终于决定为KDE4选择一个新的工程构建工具,其根本原因 用KDE开发者的话来说就是:只有少数几个“编专家”能够掌握KDE现在的构建体系 (admin/Makefile.common),在经历了unsermake, scons以及cmake的选型和尝试之后,KDE4决定使用cmake作为自己的构建系统。在迁移过程中,进展异常的顺利,并获得了cmake开发者的支持。所 以,目前的KDE4开发版本已经完全使用cmake来进行构建。像kdesvn,rosegarden等项目也开始使用 cmake,这也注定了cmake必然会成为一个主流的构建体系。 2,特点: cmake的特点主要有: 1,开放源代码,使用类BSD许可发布。http://cmake.org/HTML/Copyright.html 2,跨平台,并可生成native编配置文件,在Linux/Unix平台,生成makefile,在苹果平台,可 以生成xcode,在Windows平台,可以生成MSVC的工程文件。 3,能够管理大型项目,KDE4就是最好的证明。 4,简化编构建过程和编过程。Cmake工具链非常简单:cmake+make。 5,高效虑,按照KDE官方说法,CMake构建KDE4的kdelibs要比使用autotools来构建 KDE3.5.6的kdelibs快40%,主要是因为 Cmake工具链中没有libtool。 6,可扩展,可以为cmake编写特定功能的模块,扩充cmake功能。 3,问题,难道就没有问题? 1,cmake很简单,但绝对没有听起来或者想象中那么简单。 2,cmake编写的过程实际上是编程的过程,跟以前使用autotools一样,不过你需要编写的是 CMakeLists.txt(每个目录一个),使用的是”cmake语言和语法”。 3,cmake跟已有体系的配合并不是特别理想,比如pkgconfig,您在实际使用中会有所体会,虽然 有一些扩展可以使用,但并不理想。 4,个人的建议: 1,如果你没有实际的项目需求,那么看到这里就可以停下来了,因为cmake的学习过程就是实践 过程,没有实践,读的再多几天后也会忘记。 2,如果你的工程只有几个文件,直接编写Makefile是最好的选择。 3,如果使用的是C/C++/Java之外的语言,请不要使用cmake(至少目前是这样) 4,如果你使用的语言有非常完备的构建体系,比如java的ant,也不需要学习cmake,虽然有成功 的例子,比如QT4.3的csharp绑定qyoto。 5,如果项目已经采用了非常完备的工程管理工具,并且不存在维护问题,没有必要迁移到cmake 4,如果仅仅使用qt编程,没有必要使用cmake,因为qmake管理Qt工程的专业性和自动化程度比 cmake要高很多。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值