前言
计算机软件几乎存在于我们日常生活的方方面面:它触发我们的闹钟,推动我们的通信、银行业务、天气预报、公交时刻表、日历、会议、旅行、相册、电视、音乐流媒体、社交媒体、餐饮和电影预订,从黎明到黄昏。
我们周围的软件包含许多层:应用程序建立在框架之上,框架建立在库之上,库使用更小的库和可执行文件,一直到底层更小的软件组件。库和可执行文件反过来需要从源代码构建。我们通常只看到最外层,但所有这些层都需要仔细组织和构建。本书是关于如何使用 CMake 从源代码构建库和可执行文件的。
CMake 及其姊妹工具 CTest、CPack 和 CDash 已经成为从源代码构建软件的领先工具集,在使用和受欢迎程度上超过了许多其他类似工具,如备受尊敬的 GNU Autotools 和较新的基于 Python 的 SCons 构建系统。
(搜索兴趣随时间变化,针对三个流行的构建系统:CMake、Automake 和 SCons。兴趣是通过对相关术语的搜索次数来衡量的。该图表是使用 Google 趋势提供的数据获得的。)
CMake 项目的历史始于 1999 年,当时 Kitware,即其开发背后的公司,受委托设计一套新的工具来简化研究人员日常软件工作。目标很明确:提供一套工具,使得在不同平台上配置、构建、测试和部署同一项目变得更加容易。关于 CMake 项目随后设计选择的精彩叙述可以在www.aosabook.org/en/cmake.html找到。
CMake 是一个构建系统生成器,提供了一个强大的领域特定语言(DSL)来描述构建系统应该实现的目标。在我们看来,这是 CMake 的主要优势之一,因为它允许使用相同的 CMake 脚本生成平台原生构建系统。CMake 软件工具集让开发者完全控制一个项目的整个生命周期:
- CMake 让你描述你的项目,无论是构建可执行文件、库还是两者,都必须如何在所有主要硬件和操作系统上进行配置、构建和安装。
- CTest 允许你定义测试、测试套件以及设置它们应该如何执行。
- CPack 提供了一个 DSL 来满足你所有的打包需求,无论你的项目应该以源代码还是平台原生二进制形式打包和分发。
- CDash 将有助于将项目测试结果报告到在线仪表板上。
一句古老的谚语说,你挖得越深,找到的石头就越多。为了准备这本书,我们深入挖掘了许多软件层,CMake 是我们的矿场。在各种平台上构建各种软件组件和库时,我们遇到了许多石头和文物,每个都有自己的怪癖,有时感到沮丧。但我们相信我们已经清理了许多石头,并很高兴与您,我们的读者分享我们的发现和配方。总会有石头留下,但每块石头都会带来新的见解,我们鼓励您与社区分享这些见解。
本书的目标读者
编写能够在多种平台上原生、可靠且高效运行的软件对于各行各业和社会至关重要。软件构建系统在这一任务中占据中心位置。它们是软件开发生命周期管理的关键部分:从孵化和原型开发到测试,直至打包、部署和分发。CMake 旨在帮助您管理这些操作:如果您是希望使用 CMake 管理构建系统的软件开发者,或者希望理解和修改他人编写的 CMake 代码,那么本书适合您。
本书内容概述
我们编写这本书作为一系列逐步的任务和配方。在每个点,我们介绍足够的 CMake 信息来展示如何实现我们的目标,而不会让您被细节淹没。到本书结束时,您将能够处理越来越复杂的操作,并自信地利用配方中的内容在您自己的实际项目中。
本书将涵盖以下主题:
- 使用 CMake 配置、构建、测试和安装代码项目
- 检测操作系统、处理器、库、文件和程序以进行条件编译
- 提高代码的可移植性
- 借助 CMake 将大型代码库重构为模块
- 构建多语言项目
- 了解如何调整他人编写的 CMake 配置文件
- 打包项目以供分发
- 将项目迁移至 CMake
由 CMake 管理项目的流程发生在多个阶段,我们称之为时刻。这些可以简洁地概括在以下图中:
- CMake 时刻或配置时刻。这是 CMake 运行的时候。在这个阶段,CMake 将处理您项目中的
CMakeLists.txt文件并进行配置。 - 生成时刻。在成功配置后,CMake 将生成由本地构建工具执行项目后续步骤所需的脚本。
- 构建时间。这是在平台上调用本地构建工具的时候,这些工具会使用之前由 CMake 生成的平台和工具本地的构建脚本。在这个阶段,编译器将被调用,目标(可执行文件和库)将在特定的构建目录中构建。注意递归的 CMake 时间箭头:这可能看起来令人困惑,但它是一种机制,我们将在本书中多次使用它来实现真正平台无关的构建。
- CTest 时间或测试时间。这是我们运行项目测试套件以检查目标是否按预期执行的时候。
- CDash 时间或报告时间。这是将测试项目的结果上传到仪表板以与其他开发人员共享的时候。
- 安装时间。这是将项目的目标、源文件、可执行文件和库从构建目录安装到安装位置的时候。
- CPack 时间或打包时间。这是我们打包项目以供分发的时候,无论是作为源代码还是二进制。
- 包安装时间。这是新创建的包被系统全局安装的时候。
本书的组织结构如下:
第一章,从简单的可执行文件到库,展示了如何开始使用 CMake 配置和构建简单的可执行文件和库。
第二章,检测环境,解释了如何使用简单的 CMake 命令与操作系统和处理器架构交互。
第三章,检测外部库和程序,展示了 CMake 如何简化项目依赖项的检测。
第四章,创建和运行测试,解释了如何利用 CMake 和 CTest 的力量来定义和运行测试。
第五章,配置时间和构建时间操作,展示了如何使用跨平台的 CMake 命令在构建过程的不同阶段执行自定义操作。
第六章,生成源代码,讨论了 CMake 命令,用于自动生成源代码。
第七章,项目结构化,展示了强大的 CMake 语法,用于组织项目,使其更易于维护。
第八章,超级构建模式,解释了强大的 CMake 超级构建模式,用于管理关键项目依赖项,同时控制副作用。
第九章,混合语言项目,展示了如何使用 CMake 构建混合不同编程语言的项目。
第十章,编写安装程序,负责使用 CMake 的跨平台能力来安装项目。
第十一章,打包项目,展示了如何使用 CPack 生成源代码和平台原生源代码存档,以及如何构建 Python 和 Conda 包以供分发。
第十二章,构建文档,展示了如何使用 CMake 为你的代码构建文档。
第十三章,替代生成器和交叉编译,展示了如何使用 CMake 在不同平台之间交叉编译项目。
第十四章,测试仪表板,展示了如何将测试结果报告给在线仪表板。
第十五章,将项目移植到 CMake,展示了将项目移植到基于 CMake 的构建系统的最佳实践、技巧和诀窍。
为了从本书中获得最大收益
这是一本由程序员为程序员编写的书。我们假设具备以下基本知识和熟悉度:
- 你最喜欢的操作系统上的命令行
- 你最喜欢的操作系统上用于构建软件的原生工具
- 编译语言 C++、C 或 Fortran,以及你最喜欢的操作系统上的相应编译器
- Python 编程语言
下载示例代码文件
你可以从github.com/dev-cafe/cmake-cookbook下载本书的示例代码。更多详情请参阅设置你的系统部分。
下载彩色图像
我们还提供了一个包含本书中使用的屏幕截图/图表的彩色图像的 PDF 文件。你可以在这里下载:www.packtpub.com/sites/default/files/downloads/CMakeCookbook_ColorImages.pdf。
使用的约定
本书中使用了多种文本约定。
CodeInText:表示文本中的代码命令、文件夹名、文件名、模块名和目标名。
代码块设置如下:
cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(recipe-01 LANGUAGES CXX) add_executable(hello-world hello-world.cpp)
任何命令行输入都以粗体书写,并在要输入的命令前包含一个**$**提示符:
$ mkdir -p build $ cd build $ cmake ..
为了区分命令行输入和输出,我们将输出保持非粗体:
$ ./hello-world Hello World!
重要提示以这种方式出现。
提示和技巧以这种方式出现。
额外的阅读资源
CMake 的在线文档非常全面,我们将在本书中引用它:cmake.org/documentation/
在准备本书时,我们还受到了其他资源的启发:
- 丹尼尔·普法伊费尔的演讲,可在 GitHub 上找到:
github.com/boostcon/cppnow_presentations_2017/blob/master/05-19-2017_friday/effective_cmake__daniel_pfeifer__cppnow_05-19-2017.pdf - Eric Noulard 的 CMake 教程,可在 GitHub 上找到:
github.com/TheErk/CMake-tutorial - Craig Scott 关于 CMake 的博客文章:
crascit.com/tag/cmake/
我们还推荐浏览 Viktor Kirilov 精心收集的 CMake 资源、脚本、模块和示例列表:github.com/onqtam/awesome-cmake。
值得一提的是,我们的书籍并不是市面上唯一涵盖 CMake 的书籍:
- *《Mastering CMake》*由 Ken Martin 和 Bill Hoffman 于 2015 年编写,Kitware Inc.出版。
- *《Professional CMake》*由 Craig Scott 编写:
crascit.com/professional-cmake/
联系我们
我们始终欢迎读者的反馈。
源代码改进和问题:请将拉取请求直接发送到github.com/dev-cafe/cmake-cookbook,并通过github.com/dev-cafe/cmake-cookbook/issues报告特定食谱的问题。
一般反馈:发送电子邮件至feedback@packtpub.com,并在您的消息主题中提及书名。如果您对本书的任何方面有疑问,请发送电子邮件至questions@packtpub.com。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然会发生。如果您在这本书中发现了错误,我们非常感谢您向我们报告。请访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。
盗版:如果您在互联网上遇到任何形式的我们作品的非法副本,我们非常感谢您提供位置地址或网站名称。请通过copyright@packtpub.com与我们联系,并附上材料链接。
如果您有兴趣成为作者:如果您在某个领域拥有专业知识,并且对撰写或参与编写书籍感兴趣,请访问authors.packtpub.com。
评论
请留下评论。在阅读和使用本书后,为何不在购买它的网站上留下评论呢?潜在读者可以看到并使用您的客观意见来做出购买决定,我们 Packt 可以了解您对我们产品的看法,我们的作者可以看到您对他们书籍的反馈。谢谢!
如需了解更多关于 Packt 的信息,请访问packtpub.com。
第一章:设置您的系统
在深入了解 CMake 和本书中的食谱之前,您需要设置您的系统以成功运行所有示例。在本节中,我们将讨论以下主题:
- 如何获取食谱的代码
- 如何在 GNU/Linux、macOS 和 Windows 上安装运行代码示例所需的所有工具
- 仓库的自动化测试是如何工作的
- 如何报告食谱中的问题并提出改进建议
我们努力使本书中主题的讨论尽可能对初学者友好。然而,本书并不是从零开始的。我们假设您对所选平台上的原生软件构建工具有基本的了解。拥有使用 Git 进行版本控制的基本经验也是有帮助的(但不是必需的),以便与保存食谱源代码的仓库进行交互。
获取代码
本书中食谱的源代码可在 GitHub 上找到,地址为github.com/dev-cafe/cmake-cookbook。代码采用标准的开源 MIT 许可证:这是一种宽容的软件许可证,您可以以任何您认为合适的方式重用和混编代码,只要在软件/源代码的任何副本中包含原始版权和许可证通知即可。许可证的全文可在opensource.org/licenses/MIT查看。
为了自己测试食谱,您需要安装 Git,安装方法如下:
- 所有主要的 GNU/Linux 发行版都提供了预打包的 Git,通过它们的包管理器。如果不是您的情况,可以从 Git 项目网站
git-scm.com下载二进制分发版。 - 在 macOS 上,可以使用 Homebrew 或 MacPorts 来安装 Git。
- 在 Windows 上,您可以从 Git 项目网站
git-scm.com下载 Git 可执行文件。
或者,您可以通过 GitHub 桌面客户端访问示例,地址为desktop.github.com。
另一种选择是从github.com/dev-cafe/cmake-cookbook下载并提取 ZIP 文件。
安装 Git 后,您可以将其克隆到本地机器上,如下所示:
$ git clone https://github.com/dev-cafe/cmake-cookbook.git
这将创建一个名为cmake-cookbook的文件夹。本书和仓库按章节和食谱组织。章节编号和仓库中食谱的顺序反映了文本中的顺序。每个食谱进一步组织成示例文件夹。有些食谱有多个示例,通常是在不同的编程语言中说明类似的 CMake 概念时。
食谱在 GNU/Linux、macOS 和 Windows 上使用最先进的持续集成服务进行测试。我们将在稍后讨论测试设置。
我们已经为本书中示例对应的精确版本打上了标签v1.0。为了与书中的文本最大限度地重叠,你可以按照以下方式获取这个特定版本:
$ git clone --single-branch -b v1.0 https://github.com/dev-cafe/cmake-cookbook.git
我们预计会收到错误修复,并且 GitHub 仓库会不断发展。为了获取最新更新,你可能更愿意关注仓库的master分支。
Docker 镜像
你可能会发现,在软件环境中测试本书的配方(该环境包含所有预装的依赖项)最简单的方法是使用我们基于 Ubuntu 18.04 设置的 Docker 镜像。你可以在你喜欢的操作系统上安装 Docker,按照官方文档的指导进行操作,网址为docs.docker.com。
安装 Docker 后,你可以运行我们的镜像,并在完整的软件环境中测试配方,如下所示:
$ docker run -it devcafe/cmake-cookbook_ubuntu-18.04 $ git clone https://github.com/dev-cafe/cmake-cookbook.git $ cd cmake-cookbook $ pipenv install --three $ pipenv run python testing/collect_tests.py 'chapter-*/recipe-*'
安装预置软件
在容器中运行本书配方的替代方法是直接在主机操作系统上安装依赖项。为此,我们组装了一个最小工具栈,可以作为我们所有配方的基本起点。你需要安装以下内容:
- CMake
- 特定语言的工具,即编译器
- 构建自动化工具
- Python
我们还将详细说明如何安装一些配方所需的额外依赖项。
获取 CMake
本书所需的 CMake 最低版本为 3.5。只有少数特定配方和示例会展示在 3.5 版本之后引入的有用功能,这些将需要更新的 CMake 版本。每个配方的介绍中都有一个信息框,指出代码的可用位置、给出的示例以及所需的最低 CMake 版本。信息框将如下所示:
本配方的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-03/recipe-10找到,并包含一个 C 语言示例。该配方适用于 CMake 版本 3.5(及以上),并在 GNU/Linux、macOS 和 Windows 上进行了测试。
尽管大多数配方仍然适用于较旧版本的 CMake,但我们没有尝试测试这一假设,因为我们认为 CMake 3.5 是大多数系统和发行版上的默认版本。我们也认为升级到较新版本的 CMake 是一个简单的步骤。
CMake 可以通过多种方式安装。下载并提取 Kitware 维护的二进制分发版将在所有平台上工作。下载页面位于cmake.org/download/。
大多数 GNU/Linux 发行版在其包管理器中都有 CMake 可用。然而,在某些发行版上,打包的版本可能相当陈旧,因此下载由 Kitware 维护的二进制文件仍然是首选选项。以下命令将从 CMake 打包的版本下载并安装 CMake 3.5.2 到$HOME/Deps/cmake(根据您的喜好调整此路径):
$ cmake_version="3.5.2" $ target_path=$HOME/Deps/cmake/${cmake_version} $ cmake_url="https://cmake.org/files/v${cmake_version%.*}/cmake-${cmake_version}-Linux-x86_64.tar.gz" $ mkdir -p "${target_path}" $ curl -Ls "${cmake_url}" | tar -xz -C "${target_path}" --strip-components=1 $ export PATH=$HOME/Deps/cmake/${cmake_version}/bin${PATH:+:$PATH} $ cmake --version
Homebrew for macOS 可靠地提供最新版本的 CMake:
$ brew upgrade cmake
在 Windows 上,您可以使用提供 CMake 支持的 Visual Studio 2017。Visual Studio 2017 的安装在第十三章,替代生成器和交叉编译,食谱 1,使用 Visual Studio 2017 构建 CMake 项目中有详细记录。
或者,您可以从www.msys2.org下载 MSYS2 安装程序,按照其中的说明更新包列表,然后使用包管理器pacman安装 CMake。以下代码假设我们正在构建 64 位版本:
$ pacman -S mingw64/mingw-w64-x86_64-cmake
对于 32 位版本,请使用以下命令(尽管为了简洁起见,我们将来只会提及 64 位版本):
$ pacman -S mingw64/mingw-w64-i686-cmake
MSYS2 的另一个不错的特点是,它为 Windows 提供了一个终端,感觉和行为类似于 Unix 类操作系统上的终端,提供了一个有用的开发环境。
编译器
我们将需要 C++、C 和 Fortran 的编译器。这些应该相当新,因为我们在大多数食谱中需要对最新语言标准的支持。CMake 对许多编译器提供了非常好的支持,无论是商业的还是非商业的供应商。为了使食谱保持跨平台一致性,并尽可能操作系统独立,我们使用了开源编译器:
- 在 GNU/Linux 上,GNU 编译器集合(GCC)是显而易见的选择。它是免费的,适用于所有发行版。例如,在 Ubuntu 上,您可以按照以下方式安装编译器:
$ sudo apt-get install g++ gcc gfortran
- Clang,属于 LLVM 家族,也是 C++和 C 的一个好选择:
$ sudo apt-get install clang clang++ gfortran
- 在 macOS 上,随 XCode 一起提供的 LLVM 编译器适用于 C++和 C。在我们的 macOS 测试中,我们使用了 GCC 的 Fortran 编译器。这需要单独安装,使用包管理器。例如,Homebrew 的命令如下:
$ brew install gcc
- 在 Windows 上,您可以使用 Visual Studio 进行 C++和 C 食谱。或者,您可以使用 MSYS2 安装程序并安装整个工具链,包括 C++、C 和 Fortran 编译器,使用以下单个命令在 MSYS2 环境中(对于 64 位版本):
$ pacman -S mingw64/mingw-w64-x86_64-toolchain
构建自动化工具
这些构建自动化工具将为构建和链接本食谱中介绍的项目提供基础设施。您最终安装和使用的工具很大程度上取决于您的操作系统和个人喜好:
- 在 GNU/Linux 上,安装编译器时,GNU Make 很可能会自动安装。
- 在 macOS 上,XCode 将提供 GNU Make。
- 在 Windows 上,Visual Studio 将为你提供完整的基础设施。在 MSYS2 环境中,GNU Make 作为
mingw64/mingw-w64-x86_64-toolchain包的一部分安装,这是我们之前安装的。
为了最大程度的可移植性,我们尽可能地使配方对这些系统依赖细节保持中立。这种方法的一个明显优势是,配置、构建和链接对于每个平台和每组编译器都是本地的。
Ninja 程序是一个不同的构建自动化工具,适用于 GNU/Linux、macOS 和 Windows。Ninja 是一个新的构建工具,专注于速度,特别是增量重建。预打包的二进制文件可以在项目的 GitHub 仓库中找到,网址为github.com/ninja-build/ninja/releases。
使用 CMake 和 Ninja 与 Fortran 项目需要一些注意。需要 CMake 3.7.2 或更高版本,以及 Kitware 维护的 Ninja 版本,可在github.com/Kitware/ninja/releases找到。
在 GNU/Linux 上,你可以通过以下一系列命令安装 Ninja:
$ mkdir -p ninja $ ninja_url="https://github.com/Kitware/ninja/releases/download/v1.8.2.g3bbbe.kitware.dyndep-1.jobserver-1/ninja-1.8.2.g3bbbe.kitware.dyndep-1.jobserver-1_x86_64-linux-gnu.tar.gz" $ curl -Ls ${ninja_url} | tar -xz -C ninja --strip-components=1 $ export PATH=$HOME/Deps/ninja${PATH:+:$PATH}
在 Windows 上,使用 MSYS2 环境(假设是 64 位版本),执行以下命令:
$ pacman -S mingw64/mingw-w64-x86_64-ninja
我们建议阅读www.aosabook.org/en/posa/ninja.html上的文章,以获得关于 Ninja 的历史和设计选择的启发性讨论。
Python
本书是关于 CMake 的,但其中一些配方,以及整个用于测试的基础设施,需要 Python。因此,首先,你需要一个可用的 Python 安装:解释器、头文件和库。Python 2.7 的生命周期已于 2020 年结束,因此我们将使用 Python 3.5。
在 Ubuntu 14.04 LTS 上(这是 Travis CI 使用的环境,我们将在后面讨论),可以按如下方式安装 Python 3.5:
$ sudo apt-get install python3.5-dev
在 Windows 上,使用 MSYS2 环境,可以按如下方式安装 Python 环境(假设是 64 位版本):
$ pacman -S mingw64/mingw-w64-x86_64-python3 $ pacman -S mingw64/mingw-w64-x86_64-python3-pip $ python3 -m pip install pipenv
还需要特定的 Python 模块,以便运行我们设置的测试机制。这些可以通过使用你喜欢的包管理器全局安装,或者在隔离环境中安装。后者方法强烈推荐,因为它提供了以下优势:
- 你可以安装包并清理安装,而不会影响系统环境。
- 无需管理员权限即可安装包。
- 你降低了版本和依赖冲突的风险。
- 你可以更好地控制包依赖关系,以实现可重复性。
我们已经为此准备了一个Pipfile。结合其Pipfile.lock,您可以使用 Pipenv(pipenv.readthedocs.io)来生成一个隔离环境,其中安装了所有软件包。要在配方示例仓库中创建此环境,请在仓库的顶级目录中运行以下命令:
$ pip install --user pip pipenv --upgrade $ pipenv install --python python3.5
**pipenv shell**命令将使您进入一个命令行环境,其中包含特定版本的 Python 和所有可用的软件包。执行exit将带您回到一个干净的环境。您也可以使用pipenv run直接在隔离环境中执行命令。
或者,可以使用仓库中的requirements.txt文件,结合 Virtualenv(docs.python-guide.org/en/latest/dev/virtualenvs/)和pip,来达到同样的效果:
$ virtualenv --python=python3.5 venv $ source venv/bin/activate $ pip install -r requirements.txt
可以通过使用deactivate命令退出虚拟环境。
另一种选择是使用 Conda 环境。为此,我们建议安装 Miniconda。以下指令将安装最新的 Miniconda 到目录$HOME/Deps/conda,适用于 GNU/Linux(从repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh下载)或 macOS(从repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh下载):
$ curl -Ls https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh > miniconda.sh $ bash miniconda.sh -b -p "$HOME"/Deps/conda &> /dev/null $ touch "$HOME"/Deps/conda/conda-meta/pinned $ export PATH=$HOME/Deps/conda/bin${PATH:+:$PATH} $ conda config --set show_channel_urls True $ conda config --set changeps1 no $ conda update --all $ conda clean -tipy
在 Windows 上,您可以从repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe下载最新的 Miniconda。可以使用 PowerShell 按照以下方式安装软件包:
$basedir = $pwd.Path + "\" $filepath = $basedir + "Miniconda3-latest-Windows-x86_64.exe" $Anaconda_loc = "C:\Deps\conda" $args = "/InstallationType=JustMe /AddToPath=0 /RegisterPython=0 /S /D=$Anaconda_loc" Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru $conda_path = $Anaconda_loc + "\Scripts\conda.exe" $args = "config --set show_channel_urls True" Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru $args = "config --set changeps1 no" Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru $args = "update --all" Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru $args = "clean -tipy" Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
安装 Conda 后,可以按照以下方式安装 Python 模块:
$ conda create -n cmake-cookbook python=3.5 $ conda activate cmake-cookbook $ conda install --file requirements.txt
执行conda deactivate将使您退出隔离环境。
CMake 秘籍(一)(2)https://developer.aliyun.com/article/1524601