版本控制概念

最低版本控制

vcpkg 受到 Go 使用的版本控制方法启发,使用最小选择,但在以下一些方面进行了修改:

  • 始终以全新安全开始,不需要进行升级/降级操作。
  • 通过引入基线来允许不受约束的依赖项。

但是,最小选择原则保持不变。 给定一组约束后,vcpkg 会使用能够满足所有约束的尽可能的“最早”版本的包。

使用最低版本方法具有以下几点优势:

  • 可预测并且易于理解。
  • 用户可以控制何时进行升级,不会像新版本发布时那样自动执行升级。
  • 避免使用 SAT 求解器。

若要提供示例,请考虑以下包关系图:

    (A 1.0) -> (B 1.0)

    (A 1.1) -> (B 1.0) 
            -> (C 3.0) 

    (A 1.2) -> (B 2.0)
            -> (C 3.0)

    (C 2.0)

以及以下清单:

{
    "name": "example",
    "version": "1.0.0",
    "dependencies": [ 
        { "name": "A", "version>=": "1.1" },
        { "name": "C", "version>=": "2.0" }
    ], 
    "builtin-baseline": "<some git commit with A's baseline at 1.0>"
}

在考虑到转换依赖项之后,即可得到这样一组约束:

  • A >= 1.1
    • B >= 1.0
    • C >= 3.0
  • C >= 2.0

由于 vcpkg 必须满足所有约束,安装包集将变为:

  • A 1.1,即使当 A 1.2 存在时,也没有比 1.1 更高的约束,因此 vcpkg 选择可能的最小版本。
  • B 1.0A 1.1 转换需要。
  • C 3.0,由 B 1.0 为满足版本约束而添加的转换约束进行升级。

约束解决

给定具有一组版本控制依赖项的清单,vcpkg 将尝试计算满足所有约束的包安装计划。

版本约束具有以下风格:

  • 声明的约束:使用 version>= 在顶级清单中明确声明的约束。
  • 基线约束:由 builtin-baseline 隐性添加的约束。
  • 传递约束:由依赖项的依赖项间接添加的约束。
  • 重写约束:使用 overrides 声明在顶级清单中重写的约束。

若要计算安装计划,vcpkg 大致遵循以下步骤:

  • 将所有顶级约束添加到计划。
  • 以递归方式向计划添加传递约束。
    • 每次向计划添加新包时,也会将其基线约束添加到计划。
    • 每次添加约束时:
    • 如果包存在替代
      • 选择替代中的版本。
    • 否则:
      • 如果没有选择以前的版本。
        • 选择满足约束的最低版本。
      • 如果选择了以前的版本:
        • 如果新约束的版本控制方案与以前选择的版本不匹配:
          • 添加版本冲突。
        • 如果约束的版本与以前选择的版本不能相比。 例如,将“version-string: apple”与“version-string: orange”进行比较:
          • 添加版本冲突。
        • 如果约束版本高于以前选择的版本:
          • 选择“最高版本”。
        • 否则:
          • 保留上一个选择。
  • 检查计划:
    • 如果没有冲突
      • 安装选择的包
    • 否则:
      • 向用户报告冲突

获取端口版本

虽然包版本的概念一直存在于 vcpkg 中,但版本约束的概念尚未出现。

随着版本控制约束的引入,包现在可能依赖于与本地可用的端口版本不匹配的端口版本。 由此引发了问题,因为 vcpkg 需要了解如何获取请求的版本的端口文件。

为了解决此问题,引入了一组新的元数据文件。 这些文件位于 vcpkg 存储库根级别的 versions/ 目录中。

versions/ 目录将包含注册表中可用的每个端口的 JSON 文件。 每个文件都会列出可用于包的所有版本,并包含 vcpkg 可以签出的 Git 树 ish 对象来获取该版本的端口文件。

示例:zlib.json

{
  "versions": [
    {
      "git-tree": "2dfc991c739ab9f2605c2ad91a58a7982eb15687",
      "version-string": "1.2.11",
      "port-version": 9
    },
    ...
    {
      "git-tree": "a516e5ee220c8250f21821077d0e3dd517f02631",
      "version-string": "1.2.10",
      "port-version": 0
    },
    {
      "git-tree": "3309ec82cd96d752ff890c441cb20ef49b52bf94",
      "version-string": "1.2.8",
      "port-version": 0
    }
  ]
}

每个端口对应的版本文件应位于 versions/{first letter of port name}-/{port name}.json 中。 例如,zlib 的版本文件将位于 versions/z-/zlib.json 中。 除了端口版本文件外,当前基线文件位于 versions/baseline.json