版本控制入门

将版本与清单配合使用

让我们先来创建一个依赖于 fmtzlib 的简单的 CMake 项目。

创建一个包含以下文件的文件夹:

vcpkg.json

{
    "name": "versions-test",
    "version": "1.0.0",
    "dependencies": [
        {
            "name": "fmt",
            "version>=": "7.1.3#1"
        }, 
        "zlib"
    ],
    "builtin-baseline": "3426db05b996481ca31e95fff3734cf23e0f51bc"
}

main.cpp

#include <fmt/core.h>
#include <zlib.h>

int main()
{
    fmt::print("fmt version is {}\n"
               "zlib version is {}\n", 
               FMT_VERSION, ZLIB_VERSION);
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.18)

project(versionstest CXX)

add_executable(main main.cpp)

find_package(ZLIB REQUIRED)
find_package(fmt CONFIG REQUIRED)
target_link_libraries(main PRIVATE ZLIB::ZLIB fmt::fmt)

接下来,我们要使用 CMake 来构建和运行我们的项目:

  1. 为项目创建生成目录。

    PS D:\versions-test> mkdir build
    PS D:\versions-test> cd build
    
  2. 配置 CMake。

    PS D:\versions-test\build> cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake ..
    -- Running vcpkg install
    Detecting compiler hash for triplet x86-windows...
    The following packages will be built and installed:
        fmt[core]:x64-windows -> 7.1.3#1 -- D:\Work\viromer\vcpkg\buildtrees\versioning\versions\fmt\4f8427eb0bd40da1856d4e67bde39a4fda689d72
        vcpkg-cmake[core]:x64-windows -> 2021-02-26 -- D:\Work\viromer\vcpkg\buildtrees\versioning\versions\vcpkg-cmake\51896aa8073adb5c8450daa423d03eedf0dfc61f
        vcpkg-cmake-config[core]:x64-windows -> 2021-02-26 -- D:\Work\viromer\vcpkg\buildtrees\versioning\versions\vcpkg-cmake-config\d255b3d566a8861dcc99a958240463e678528066
        zlib[core]:x64-windows -> 1.2.11#9 -- D:\Work\viromer\vcpkg\buildtrees\versioning\versions\zlib\827111046e37c98153d9d82bb6fa4183b6d728e4
    ...
    
  3. 生成项目。

    PS D:\versions-test\build> cmake --build .
    [2/2] Linking CXX executable main.exe
    
  4. 运行项目!

    PS D:\versions-test\build> ./main.exe
    fmt version is 70103
    zlib version is 1.2.11
    

查看输出:

fmt[core]:x86-windows -> 7.1.3#1 -- D:\vcpkg\buildtrees\versioning\versions\fmt\4f8427eb0bd40da1856d4e67bde39a4fda689d72
...
zlib[core]:x86-windows -> 1.2.11#9 -- D:\vcpkg\buildtrees\versioning\versions\zlib\827111046e37c98153d9d82bb6fa4183b6d728e4

vcpkg 没有使用 ports/ 中的端口文件,而是签出 buildtrees/versioning/versions/ 中每个版本的文件。 在经典模式下运行 vcpkg 时,仍会使用 ports/ 中的这些文件。

注意

配置 CMake 时,vcpkg 的输出仅在使用 CMake 版本 3.18 或更高版本时可用。 如果使用较旧的 CMake,可以改为检查生成目录中的 vcpkg-manifest-install.log 文件。

请参阅我们的清单公告博客文章,了解如何将清单与 MSBuild 配合使用。

清单更改

如果之前使用过清单,则会注意到有一些新的 JSON 属性。 让我们来看看有哪些变化:

version

{
    "name": "versions-test",
    "version": "1.0.0"
}

这是项目的版本声明。 以前,只能使用 version-string 属性来声明项目的版本。 现在已经推出版本控制功能,vcpkg 了解一些新的版本控制方案。

版本方案 说明
version 句点分隔的数值:1.0.0.5
version-semver 兼容的语义版本1.2.01.2.0-rc
version-date 日期采用 YYYY-MM-DD 格式:2021-01-01
version-string 任意字符串:vistacandy

version>=

{
    "dependencies": [
        { "name": "fmt", "version>=": "7.1.3" },
        "zlib"
    ]
}

此属性用于表示最低版本约束,仅允许作为声明的 "dependencies" 一部分。 在示例中,我们对 fmt 的 版本 7.1.3#1 设置了明确约束。

如果传递依赖项需要较新版本,则允许 vcpkg 升级此约束。 例如,如果 zlib 之前用于对 fmt 版本 7.1.4 声明依赖项,则 vcpkg 会改为安装 7.1.4

vcpkg 使用最低版本方法,在示例中,即使 fmt 版本 8.0.0 是要发布的版本,vcpkg 还是会安装版本 7.1.3#1,因为这是满足该约束的最低版本。 这种方法的优势在于当你更新 vcpkg 时不会遇到意外的依赖项升级,而是获得可重现的版本(对于使用过的版本),只要你使用的是同一个清单。

如果要升级依赖项,可以升级最低版本约束或使用较新的基线。

builtin-baseline

{ "builtin-baseline": "3426db05b996481ca31e95fff3734cf23e0f51bc" }

此字段声明所有端口的版本控制基线。 若要启用版本控制就需要设置基线,否则将会获得 ports/ 目录上的当前版本。 可以运行“git rev-parse HEAD”来获取 vcpkg 的当前提交,并将其设置为内置基线。 有关详细信息,请参阅"builtin-baseline"文档

我们在示例中没有为 zlib 声明版本约束,而是从基线获取版本。 vcpkg 在内部会查看提交 3426db05b996481ca31e95fff3734cf23e0f51bc 来弄清楚 zlib 的哪个版本是该时间点上的最新版本(在我们的示例中为 1.2.11#9)。

在版本解决期间,将基线版本视为最小版本约束。 如果声明低于基线版本的明确约束,则明确约束将升级到基线版本。

例如,如果我们修改了如下所示的依赖项:

{ "dependencies": [
    {
        "name": "fmt",
        "version>=": "7.1.3#1"
    },
    {
        "name": "zlib",
        "version>=": "1.2.11#7"
    }
] }

注意

1.2.11#7 表示版本 1.2.11、端口版本 7

由于基线在 1.2.11#9 引入 zlib 的最小版本约束,且更高版本满足 1.2.11#7 的最小版本约束,因此允许 vcpkg 对其进行升级。

此外,基线也是一性次升级多个版本的便捷机制。例如,如果想要依赖多个 boost 库,则一次性设置 baseline 要比在每个包上声明一个版本约束要便捷得多。

但是,如果要固定早于基线的版本,该怎么办?

overrides

由于基线为所有包建立版本基础,并且明确约束在低于基线时升级,因此我们需要另一种机制来降级基线版本。

vcpkg 为这种情况提供的机制是 overrides。 在包上声明替代时,vcpkg 将忽略在清单中直接声明或从可传递依赖项声明的所有其他版本约束。 简言之,overrides 会强制 vcpkg 使用声明的确切版本,就是这样。

让我们再次修改示例。这一次要强制 vcpkg 使用 fmt 的 版本 6.0.0

{
    "name": "versions-test",
    "version": "1.0.0",
    "dependencies": [
        {
            "name": "fmt",
            "version>=": "7.1.3#1"
        },
        {
            "name": "zlib",
            "version>=": "1.2.11#7"
        }
    ],
    "builtin-baseline": "3426db05b996481ca31e95fff3734cf23e0f51bc",
    "overrides": [
        {
            "name": "fmt",
            "version": "6.0.0"
        }
    ]
}

重新生成项目:

PS D:\versions-test\build> rm ./CMakeCache.txt
PS D:\versions-test\build> rm -r ./vcpkg_installed
PS D:\versions-test\build> cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake ..
-- Running vcpkg install
Detecting compiler hash for triplet x86-windows...
The following packages will be built and installed:
    fmt[core]:x86-windows -> 6.0.0 -- D:\vcpkg\buildtrees\versioning\versions\fmt\d99b6a35e1406ba6b6e09d719bebd086f83ed5f3
    zlib[core]:x86-windows -> 1.2.11#9 -- D:\vcpkg\buildtrees\versioning\versions\zlib\827111046e37c98153d9d82bb6fa4183b6d728e4
...
PS D:\versions-test\build> cmake --build .
[2/2] Linking CXX executable main.exe

然后运行项目!

PS D:\versions-test\build> .\main.exe
fmt version is 60000
zlib version is 1.2.11

请注意,fmt 现在在版本 6.0.0 时和我们想的一样。

版本和自定义端口

最后要介绍的是覆盖端口与版本控制解决的交互方式。 答案是:它们不进行交互。

具体而言,当你为端口提供覆盖时,vcpkg 会始终使用覆盖端口,而不会关心它其中约束的是哪个版本。 有以下两个原因:(1) 这与完成过滤现有端口的覆盖端口现有行为保持一致,(2) 覆盖端口不提供(也不要期待)它提供足够的信息来支持 vcpkg 的版本控制功能。

如果想要具有灵活的端口自定义以及版本控制,应考虑创建自己的自定义注册表

其他阅读材料

如果想要更深入地了解版本控制的工作原理,建议参阅我们的版本控制参考版本控制概念