修订版本

本节介绍如何在不显式创建新版本的情况下修改给定的 recipe 或源代码,仍然会使用一种称为修订版本的机制在内部跟踪这些更改。

创建不同的修订版本

让我们从一个基本的“hello”包开始

$ mkdir hello && cd hello
$ conan remove hello* -c # clean possible existing ones
$ conan new cmake_lib -d name=hello -d version=1.0
$ conan create .
hello/1.0: Hello World Release!
...

我们现在可以列出缓存中现有的 recipe 修订版本

$ conan list "hello/1.0#*"
Local Cache
  hello
    hello/1.0
      revisions
        2475ece651f666f42c155623228c75d2 (2023-01-31 23:08:08 UTC)

如果我们现在编辑 src/hello.cpp 文件,将输出消息从 “Hello” 更改为 “Bye”

hello/src/hello.cpp
void hello(){

    #ifdef NDEBUG
    std::cout << "hello/1.0: Bye World Release!\n";
    ...

因此,如果我们再次创建该包,而不更改版本 hello/1.0,我们将得到一个新的输出

$ conan create .
hello/1.0: Bye World Release!
...

但即使版本相同,内部也会创建一个新的修订版本 2b547b7f20f5541c16d0b5cbcf207502

$ conan list "hello/1.0#*"
Local Cache
  hello
    hello/1.0
      revisions
        2475ece651f666f42c155623228c75d2 (2023-01-31 23:08:08 UTC)
        2b547b7f20f5541c16d0b5cbcf207502 (2023-01-31 23:08:25 UTC)

这个 recipe **修订版本** 是 recipe 内容的哈希值,包括 conanfile.py 和导出的源文件(src/main.cpp, CMakeLists.txt 等,即 recipe 中导出的所有文件)。

我们现在可以编辑 conanfile.py,以定义 license

hello/conanfile.py
class helloRecipe(ConanFile):
    name = "hello"
    version = "1.0"

    # Optional metadata
    license = "MIT"
    ...

因此,如果我们再次创建该包,输出将相同,但我们也会获得一个新的修订版本,因为 conanfile.py 已更改

$ conan create .
hello/1.0: Bye World Release!
...
$ conan list "hello/1.0#*"
Local Cache
  hello
    hello/1.0
      revisions
        2475ece651f666f42c155623228c75d2 (2023-01-31 23:08:08 UTC)
        2b547b7f20f5541c16d0b5cbcf207502 (2023-01-31 23:08:25 UTC)
        1d674b4349d2b1ea06aa6419f5f99dd9 (2023-01-31 23:08:34 UTC)

重要提示

recipe **修订版本** 是内容的哈希值。可以使用 revision_mode = "scm" 将其更改为 Git 提交哈希值。但在任何情况下,每个修订版本都表示不可变来源(包括 recipe 和源代码)都至关重要

  • 如果源文件使用 exports_sources 进行管理,那么它们将自动成为哈希值的一部分

  • 如果从外部位置检索源文件(例如下载的 tarball 或 git clone),则应通过强制检出唯一的不可变标签或提交来保证唯一性。移动目标(如分支名称或 HEAD)将被破坏,因为修订版本被认为是不可变的。

源代码或 recipe 中的任何更改都应始终意味着一个新的修订版本。

警告

行尾问题

默认情况下,Git 将在使用 CRLF 行尾的 Windows 系统上检出文件。这会导致与 Linux 系统上的文件(使用 LF 行尾)相比不同的文件。由于文件不同,因此在 Windows 上计算的 Conan recipe 修订版本将与 Linux 等其他平台上的修订版本不同。请查看 FAQ 专用部分 中有关此问题的更多信息以及如何解决它。

使用修订版本

默认情况下,recipe 修订版本会解析为每个给定版本的最新修订版本。在上面的例子中,我们可以有一个 chat/1.0 包,它使用上面的 hello/1.0

$ cd ..
$ mkdir chat && cd chat
$ conan new cmake_lib -d name=chat -d version=1.0 -d requires=hello/1.0
$ conan create .
...
Requirements
chat/1.0#17b45a168519b8e0ed178d822b7ad8c8 - Cache
hello/1.0#1d674b4349d2b1ea06aa6419f5f99dd9 - Cache
...
hello/1.0: Bye World Release!
chat/1.0: Hello World Release!

我们可以看到,默认情况下,它正在解析为最新修订版本 1d674b4349d2b1ea06aa6419f5f99dd9,因此我们还看到 hello/1.0: Bye World 已修改的消息。

可以在 recipe 中显式依赖于给定的修订版本,因此可以修改 chat/1.0 recipe 以定义它需要第一个创建的修订版本

chat/conanfile.py
def requirements(self):
    self.requires("hello/1.0#2475ece651f666f42c155623228c75d2")

因此,创建 chat 现在将强制使用第一个修订版本

$ conan create .
...
Requirements
chat/1.0#12f87e1b8a881da6b19cc7f229e16c76 - Cache
hello/1.0#2475ece651f666f42c155623228c75d2 - Cache
...
hello/1.0: Hello World Release!
chat/1.0: Hello World Release!

上传修订版本

默认情况下,上传命令将仅上传最新的修订版本

# upload latest revision only, all package binaries
$ conan upload hello/1.0 -c -r=myremote

如果由于某种原因我们要上传所有现有的修订版本,可以使用

# upload all revisions, all binaries for each revision
$ conan upload hello/1.0#* -c -r=myremote

在服务器端,最新上传的修订版本将成为最新的,也是默认解析的修订版本。因此,上述命令按顺序上传不同的修订版本(从旧修订版本到最新修订版本),以便在服务器端保留修订版本的相对顺序。

请注意,如果另一台机器决定上传一段时间前创建的修订版本,它仍然会成为服务器端最新的修订版本,因为它是在服务器端使用该时间创建的。

包修订版本

创建包二进制文件时,也会计算其内容的哈希值,形成**包修订版本**。但它们在本质上与 **recipe 修订版本** 非常不同。自然而然地期望 recipe 修订版本,源代码或 recipe 中的每次更改都会导致一个新的 recipe 修订版本。但是,包二进制文件不应有多个**包修订版本**,因为二进制文件的可变性应该已经编码在唯一的 package_id 中。换句话说,如果 recipe 修订版本相同(完全相同的输入 recipe 和源代码),并且 package_id 相同(完全相同的配置 profile,设置等),那么该二进制文件应该只构建一次。

由于 C 和 C++ 构建不是确定性的,因此在不修改任何内容的情况下,同一包的后续构建可能会创建新的包修订版本

# Build again 2 times the latest
$ conan create .
$ conan create .

在某些操作系统(如 Windows)中,此构建将不可重现,并且生成的工件将具有不同的校验和,从而导致新的包修订版本

$ conan list "hello/1.0:*#*"
Local Cache
  hello
    hello/1.0
      revisions
        1d674b4349d2b1ea06aa6419f5f99dd9 (2023-02-01 00:03:29 UTC)
          packages
            2401fa1d188d289bb25c37cfa3317e13e377a351
              revisions
                8b8c3deef5ef47a8009d4afaebfe952e (2023-01-31 23:08:40 UTC)
                8e8d380347e6d067240c4c00132d42b1 (2023-02-01 00:03:12 UTC)
                c347faaedc1e7e3282d3bfed31700019 (2023-02-01 00:03:35 UTC)
              info
                settings
                arch: x86_64
                build_type: Release
                ...

默认情况下,包修订版本也将解析为最新的修订版本。但是,无法在 recipe 中显式地固定包修订版本,recipe 只能像我们上面定义的那样要求到 recipe 修订版本。

警告

最佳实践

对于任何给定的 recipe 修订版本 + package_id 具有多个包修订版本是一种异味或潜在的错误做法。这意味着在不必要时重建了某些内容,从而浪费了计算和存储资源。有一些方法可以避免这样做,例如 conan create . --build=missing:hello* 仅当该包二进制文件尚不存在时才构建它(或者运行 conan graph info 也可以返回有关需要构建的内容的信息。)