text: 关于 C++ 依赖管理
  有经验的程序员，不论所用何种语言，对代码依赖都不会陌生。代码无论是依赖于内部依赖关系，还是依赖于外部库或框架，通常都不会孤立运行。代码的重复使用，亦即使用现有代码的做法，是提升开发效率的重要工具。不过，对代码的重复使用会导致产生一种依赖项。遗憾的是，依赖项都有其消极一面。它们很难管理。依赖管理是一种多层面进程，负责构建和维护从代码库调用的外部实体列表。遗憾的是，这项工作并不像听起来那么容易。首当其冲的问题就是，你必须确保引入的依赖项都是正确的。然后，你还需要考虑如何处理依赖冲突。维护依赖列表是一件需要效率和可靠性的事，因而这项工作应尽可能实现自动化。同时，对于解决依赖冲突等类似任务，还需要进行人工干预。为了辅助实现这个过程，开发人员会利用依赖管理工具。依赖管理工具不仅负责构建依赖列表，还负责及时报告最新的更新。举例而言，新版本的库一经发布，代码库就可能受到影响，这时工具就需要对此进行识别和处理。依赖管理工具需要密切关注动态链接库等事项，要定期识别和解决可能出现的任何问题。出现问题是常事，例如将不同依赖项依赖于同一工件的不同版本时，就会引发问题。如果依赖项“A”依赖于特定版本的依赖项“C”，且与此同时，依赖项“B”却依赖于依赖项“C”的另一版本，那么就会出现依赖冲突，需要解决。依赖管理中的一个常见问题是如何使依赖项保持最新状态。理想情况下，依赖项会随时更新，以便从最新更改中受益。即便是稳定型产品，虽然利用最新功能并非必要，但实施最新错误修复和其他改进仍很重要。然而这种情况并不多见。事实上，更新被忽略、进程有所拖延是常有的事。使用人工操作进程，工作复杂而耗时，这就是为什么依赖管理工具要致力于提升进程的自动化水平。这也提示我们一定要记住，通常情况下，解决依赖冲突仍然需要人工干预。不论所用的编程语言是什么，依赖管理工具在任何软件开发环境中都很常见。例如，你会发现 Maven 和 Gradle 在 Java 程序员中十分流行，而 pip 却在 Python 圈子更受欢迎，还有 npm 和 Yarn，一般用于 Node.js，而 Composer 更适用于 PHP 项目。即使是 Rust 这门十分年轻的语言，也从早期阶段就开始将 Cargo 作为包管理的有效工具。对于以上和其他一些语言，包管理工具还存在无数种，但 C++ 依赖管理工具却绝对是稀有品种。如果要资深 C++  开发人员说出对使用依赖项有什么认识，那一定是 C++ 依赖管理真的很难做。事实上，C++  并不像其他语言，它没有标准或占主导地位的包管理器。这造成的结果就是，C++  依赖管理要么不值一提，例如只能凭借在文件中复制粘贴这样的方法，毫无自动管理可言，要么需要靠 APT 等类似的操作系统特定工具来维持。C/C++  生态系统庞大、成熟且相当复杂。想想大量现存的第三方库，包括那些由开源社区支持的库，再想想这些库随着时间的推移，会跨平台、跨操作系统甚至是随语言版本而发展，这些都会导致生态系统变得支离破碎，而依赖管理在其中也远远不像原本那样省时。构建系统各有不同，例如每个系统都有自己的特性和选项可供选择。处理不同的构建系统本身就很难，然而这还只是难题的一部分。C++  依赖管理很难用的另一大原因是，源码可移植，而二进制文件却不行。举例来说，C++ 并不像 Java 那样可以使用 JVM  在不同平台执行编译代码。使用 C++  时，库在不同平台均可用，这意味着在管理依赖项时，如果需要维护可移植性，则不同平台的相同库就全部都需要维护。为不同平台保留不同的工件或许是一个办法，但如果需要为每个架构和操作系统都存储预编译二进制文件，则  ABI 兼容性所需的组合数量又会非常庞大（在某些情况下还不止如此，根据编译标志，同一操作系统的 ABI 各有不同）。依赖管理工具会使用不同的技术处理可移植性问题。例如下方提到的 Conan C/C++ 包管理器，它使用分散系统为多二进制包维护同一存储库系统。而 Cargo 作为 Rust 包管理器，其方法是在 crates.io 中检索依赖项，crates.io 是一个由社区维护的中央包注册表。开发车间会使用各种不同的方法来进行 C++ 依赖管理，包括手动下载、传输或复制文件、编写或采用更复杂的工具等，这些对任务执行均有帮助。一些程序员会选择使用操作系统特定的工具，例如 APT，而其他程序员则会采用更偏人工的方法。对于那些使用偏人工方法的人，几乎无法绕开的问题就是效率偏低以及容易出错，这时就需要人工维护依赖图以避免版本冲突。既然我们致力于在开发和处理时间上提高效率，那么建议参考一些现存的热门 C++ 依赖管理系统。现有几种 C++ 依赖管理工具人们正在积极开发，其中每一种都有其功能、优势及不足。比较流行的特征包括与 Visual Studio 等 CMake 和 IDE 进行集成、跨平台支持以及使用简便。虽然也有下面提到的 Buckaroo、Hunter 和 Build2 等其他选择，但目前两大最常用的成熟工具仍是 Conan 和 vcpkg。Conan C/C++  包管理器是一个开源项目，它使用了灵活的分散式客户服务器模型来存储供客户检索的包。该工具支持多平台和工具集，允许存储源码和二进制文件，并且还拥有一套丰富的选项，可用于为不同开发环境微调进程。它的文档编写质量很高，背后还有一个活跃的社区，其中包括数千公司和上万名开发人员。目前，ConanCenter  存储库中共有 2,611 个包。vcpkg 包管理器是一个来自 Microsoft  的开源、跨平台依赖管理器。它使用简便，用于在开源和私人存储库中检索和管理库，且与 Visual Studio 和 Visual Studio  Code 进行了集成。它的文档质量高，适合新手，文档中还包含了入门指南一节供参考。Vcpkg 在检索特定版本的库时，所使用的方法是基于在  Vcpkg 存储库中检索相关分支，这种方法在某些人看来笨拙而过于技术化（没有任何声明性的简单语法可实现这一点）。目前，vcpkg 存储库中共有  1,822 个包。Buckaroo 是一种开源解决方案，自称是 C++ 和  Friends 包管理器。它的文档中包含入门指南、如何与常见 IDE 集成的相关提示，以及一些其他提示和技巧。Buckaroo  不像其他依赖管理器可完全支持 CMake 构建系统，它默认采用 Buck 构建系统，对 CMake 仅提供有限支持。Buck 是一种由  Facebook 开发的构建系统，它鼓励使用小模块，但并不支持对二进制依赖项进行打包。依赖项可以直接从 GitHub、BitBucket 和  GitLab 中拉取。Hunter 是一种支持多平台的依赖管理器，可用于 C/C++  和其他语言。它旨在使用 CMake 构建系统进行包管理，同时也能够使用自定义模板支持非 CMake  包。它的文档中包含快速入门指南，以及如何与常见 IDE 集成的说明。Hunter 的主要优势在于它仅需要  CMake，因此对开发人员来说比较透明。用户对于 Hunter 的主要意见是，它的一切都构建自源码，使用时过于耗时。Build2  自身即是一种构建系统。它能够提供构建部分，也能提供包管理部分。它的文档中包含安装和入门指南说明，以及用例。其优势在于，作为一个包含构建和包管理部分的完整工具链，它需要的仅仅是  C++ 编译器而已。另一方面，许多 C++ 开发人员并不会为了获取包管理功能而考虑迁移到其他构建系统，而且，build2 首先是一个无法与  make、ninja 或 CMake 集成或兼容的构建系统。因此你不得不迁移到 build2，将其作为自己的构建系统。build2  所维护的存储库位于 https://cppget.org，目前其中仅有 95 个托管包，这个数量并不大。点击了解 Incredibuild 加速 C++ 构建的解决方案！高效且运行合理的依赖管理应做到对开发人员基本透明。当然，不可避免地会出现错误并且需要一些调整，但只要遵循一组最佳做法，这些问题都可以最小化。其中，有几条经验非常重要，首要的就是构建应保持稳定，并在各平台和操作系统间保持一致。如果构建在某一个机器上运行，那么它在不同环境中的其他机器上也应以同样方式构建和运行。也就是说，对依赖项进行合理维护的依据，不仅有版本，还有平台。尽管与依赖管理并不直接相关，开发人员仍须记住，如果在多环境中进行构建，即便代码一模一样，也要在每个环境中逐一对构建成果进行测试。另外，依赖项应尽可能地重复使用，以使依赖图更加精简。在所做工作或赖以运行的功能方面，项目中的组件很少是独一无二的。因此，要确定已具备了什么，避免将事情不必要地扩大或复杂化。关于此有一个非常简单的例子，即通常来说，不需要在单个项目中依赖于两个不同的日志库。也就是说，对依赖管理的需要处于首位，因为环境总是复杂的。最后，尽可能不要手动操作。依赖管理是一组工具和技术，用于识别和解决与代码库中的依赖项相关的问题。依赖项的形式多种多样，包括库和框架，且存在于各种从大到小、从概念验证到较成熟、跨行业、跨编码技术水平以及跨编程语言的不同项目当中。相比其他语言的依赖管理，C++  依赖管理难度更大。这既是因为目前缺乏标准，存在可移植性问题，全球代码库愈发成熟，也是因为 C++  这种语言本身就比较复杂。解决问题的方法有好有坏，不过幸运的是，有几种工具可以帮助你更好地执行任务。在这些工具中，最普遍的莫过于 Conan 和  vcpkg，如果愿意，也可以尝试一些其他工具。毫无疑问，不论选择了何种包或工具集，都能够减少过去手动复制粘贴的麻烦（对其他人来说可能没过去多久）点击了解 Incredibuild 加速 C++ 构建的解决方案，并获取试用 License！