美文网首页
第 1 章 初识 CMake

第 1 章 初识 CMake

作者: 奇点创客 | 来源:发表于2022-04-02 17:47 被阅读0次

    基础知识

    编译 C++ 源代码似乎是一个相当简单的过程。让我们创建一个简单的小程序,例如经典的 hello.cpp,如下:


    chapter-01/01-hello/hello.cpp

    #include <iostream>
    
    int main() {
        std::cout << "Hello World!" << std::endl;
        return 0;
    }
    

    现在,要获得可执行文件,我们需要运行一行命令。用文件名作为参数调用编译器:

    $ g++ hello.cpp -o a.out
    

    代码正确,编译器会默默地帮我们生成机器能够读懂的二进制可执行文件。我们可以通过文件名来调用它:

    $ ./a.out
    Hello World!
    $
    

    然而,随着我们项目的发展,你很快就会明白,将所有代码保存在一个文件中是不可能的。良好的代码实践建议文件应该保持短小并且具有良好的组织结构。手工编译每个源代码文件可能是一个令人厌倦并且极易出错的过程。一定有更好的办法。

    CMake 是什么?

    假设我们通过编写一个脚本来自动化构建,该脚本遍历我们的项目树并编译所有内容。为了避免任何不必要的编译,我们的脚本将检测源代码在上次运行之后是否被修改过。现在,我们想要一种方便的方式来管理每个文件传递给编译器的参数——最好是我们希望根据可配置的标准来做。此外,我们的脚本应该知道如何以二进制形式链接所有已编译的文件,或者更好的是,如何构建可以在更大的项目中重用并作为模块合并的整体解决方案。

    我们添加的特性越多,就越有可能得到一个成熟的解决方案。构建软件是一个通用的过程,可以包括许多不同的方面:

    • 编译成可执行文件和库
    • 依赖管理
    • 测试
    • 安装
    • 打包
    • 生成文档
    • 更多的测试

    要想出一个真正模块化的、功能强大的、适合各种用途的 C++ 构建应用程序,需要很长时间。但确实有人确实做到了。Kitware 的 Bill Hoffman 在 20 多年前实现了 CMake 的第一个版本。如你所料,它非常地成功。它现在已经有很多功能并且得到了来自社区的支持。今天,CMake 正在被积极地开发,并且已经成为 C 和 C++ 程序员的行业标准。

    采用自动化方式构建代码的想法比 CMake 出现早得多,所以很自然地,有很多解决方案:Make、Autotools、SCons、Ninja、Premake 等等。但是为什么 CMake 如今独占鳌头呢?

    就个人主观而言,我认为 CMake 做到了以下几点:

    • 它专注于支持现代编译器和工具链
    • CMake 真正做到了跨平台——它支持在 Windows、Linux、macOS 和 Cygwin 上构建
    • 它可以为主流的 IDE 软件生成项目文件,如:Microsoft Visual Studio、Xcode 和
      Eclipse CDT。此外,它也是其他软件(如 CLion)的项目模型。
    • CMake 的操作建立在正确的抽象级别上——它允许你在项目中对文件进行分组,并支持目标的可复用性。
    • 有大量的项目是用 CMake 构建的,并提供了一个简单的方法来将它们包含到你的项目中。
    • CMake 将测试、打包和安装作为构建过程的固有部分。
    • 过时的、没用的特性被弃用,以保持 CMake 的精简。

    CMake 为构建提供了一个统一而流畅的体验。不管你是在 IDE 中构建软件,还是直接从命令行构建软件;更重要的是,它也照顾到后期的构建阶段。即使前面的所有环境都不同,你的持续集成/连续部署(CI/CD) 也可以轻松地使用相同的CMake配置,并使用单一标准构建项目。

    CMake 是如何起作用的?

    你可能会有这样的印象,CMake 是一个一端读取源代码,另一端生成二进制文件的工具——虽然这在原则上是正确的,但并不是全部。

    事实上,CMake 并不能自己构建任何东西——它依赖于系统中的其他工具来执行实际的编译、链接和其他任务。你可以将其视为构建过程的协调者:它知道需要完成哪些步骤,最终目标是什么,以及如何为工作找到合适的工人和材料。

    这个过程分为三个阶段:

    • 配置
    • 生成
    • 构建

    配置阶段
    这个阶段 CMake 会从源代码目录(源码树)下读取一些项目相关的信息,并为生成阶段准备一个输出目录(构建树)。

    CMake 首先创建一个空的构建树,并收集有关它工作环境的所有信息,例如:计算机体系结构、可用的编译器、链接器和归档器。此外,它检查一个简单的测试程序是否可以被正确编译。

    接下来,CMakeLists.txt 项目配置文件被解析并执行(是的,CMake 项目是用 CMake 的编码语言配置的)。这个文件是一个 CMake 项目的最低限度(源代码文件可以稍后添加)。它告诉 CMake 关于项目结构、目标和依赖项(库和其他 CMake 包)的信息。在此过程中,CMake 将收集到的信息存储在构建树中,例如:系统详细信息、项目配置、日志和临时文件,这些将用于下一步。具体来说,它将创建一个 CMakeCache.txt 文件来存储已经确定的变量(例如编译器和其他工具的路径),并在下次配置时节省时间。

    生成阶段
    读完项目配置之后,CMake 将为当前具体的工作环境生成一个构建系统。构建系统是一个为其他构建工具提供的简要工程模板(例如,GNU 的 Makefile 、Ninja 或者 Visual Studio IDE 的工程文件)。在这个阶段,CMake 仍然可以通过计算生成器表达式对构建配置进行一些最后的修改。

    注意:
    生成阶段会在配置阶段之后自动执行。由于这个原因,当提到构建系统的“配置”或“生成”时,本书和一些其他资料经常指的是这两个阶段。要想显式地只运行配置阶段,可以使用 cmake-gui 程序。

    构建阶段
    为了生成项目中指定的最终工件,我们必须运行适当的构建工具。可以通过 IDE 直接调用,或者使用 CMake 命令。接下来,这些构建工具将执行构建步骤,使用编译器、链接器、静态和动态分析工具、测试框架、报告工具和其他任何您能想到的工具来生成目标。

    这个解决方案的美妙之处在于,它能够用一个配置(即相同的项目文件)为每个平台按需生成构建系统: 图1.1 - CMake 的各个阶段

    还记得在理解基础部分中我们的 hello.cpp 程序吗?使用 CMake 也很容易构建它。只需要加入以下的 CMakeLists.txt 并敲两条简单的命令:cmake -B buildtree 和 cmake --build buildtree,如下所示:


    chapter01/01-hello/CMakeLists.txt:CMake 语言中的 Hello world

    cmake_minimum_required(VERSION 3.20)
    project(Hello)
    add_executable(Hello hello.cpp)
    

    下面是 Dockerized Linux 系统的输出(注意,我们将在为不同平台上安装 CMake 部分讨论 Docker):

    root@5f81fe44c9bd:/root/examples/chapter01/01-hello# cmake
    -B buildtree.
    -- The C compiler identification is GNU 9.3.0
    -- The CXX compiler identification is GNU 9.3.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /root/examples/
    chapter01/01-hello/buildtree
    root@5f81fe44c9bd:/root/examples/chapter01/01-hello# cmake
    --build buildtree/
    Scanning dependencies of target Hello
    [ 50%] Building CXX object CMakeFiles/Hello.dir/hello.cpp.o
    [100%] Linking CXX executable Hello
    [100%] Built target Hello
    

    接下来就是运行:

    root@68c249f65ce2:~# ./buildtree/Hello
    Hello World!
    

    在这里,我们生成了一个 buildtree 目录下的构建系统。在此之后,我们执行构建阶段,并生成我们最终能够运行的二进制文件。

    现在你看到了最终的结果,我敢肯定你一定会有很多问题:这个过程的先决条件是什么?这些命令是什么意思?为什么我们需要两个文件?我如何编写我自己的项目文件?不要担心——这些问题将在下面的部分中得到解答。

    获取帮助
    这本书将为您提供与当前版本的 CMake 相关的最重要的信息(在撰写本书时是3.20)。为了向你提供最好的建议,我故意避免了任何已被弃用和不再被推荐的特性。我强烈建议至少使用 3.15 版本,它被认为是“现代 CMake”。如需要更多信息,你可以在网上找到最新的、完整的官方文档 https://cmake.org/cmake/help/

    为不同平台安装 CMake

    CMake 是一个用 C++ 编写的跨平台开源软件。
    这意味着你完全可以自己编译它,然而,通常没必要。因为你完全可以从官网(https://cmake.org/download/)上下载提前编译好的二进制文件。

    类 Unix 系统可以直接通过命令行来获取安装包。

    注意
    牢记 CMake 不会同编译器一起被提供。如果你的系统还未安装编译器,你需要在使用 CMake 之前提供它,并确保将它的可执行文件路径添加到 PATH 环境变量了以使 CMake 能找到它。
    学习本书的过程中,为了避免解决工具和依赖问题,我建议选择第一种安装方法:Docker。

    让我们来看看可以使用 CMake 的不同环境。

    Docker
    Docker(https://www.docker.com/)是一个提供操作系统级虚拟化的跨平台工具,允许应用程序以完整的包(称为容器)的形式发布。

    相关文章

      网友评论

          本文标题:第 1 章 初识 CMake

          本文链接:https://www.haomeiwen.com/subject/upunjrtx.html