修订版本

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

创建不同的修订版本

让我们从一个基本的“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!
...

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

$ 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)

此配方**修订版本**是配方内容的哈希值,包括 conanfile.py 和导出的源代码(src/main.cppCMakeLists.txt 等,即配方中导出的所有文件)。

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

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)

重要

配方**修订版本**是内容的哈希值。它可以通过 revision_mode = "scm" 更改为 Git 提交哈希值。但在任何情况下,至关重要的是每个修订版本都代表一个不可变的源,包括配方和源代码

  • 如果源代码使用 exports_sources 进行管理,则它们将自动成为哈希的一部分

  • 如果源代码从外部位置获取,例如下载的 tarball 或 git 克隆,则应通过强制检出唯一的不可变标签或提交来保证唯一性。移动目标(如分支名称或 HEAD)将出现问题,因为修订版本被视为不可变的。

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

警告

行结束符问题

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

使用修订版本

配方修订版本默认情况下解析为每个给定版本的最新修订版本。在上面的例子中,我们可以有一个 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

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

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

在服务器端,最新上传的修订版本成为最新的版本,并且是默认解析的版本。出于这个原因,上述命令按顺序上传不同的修订版本(从旧的修订版本到最新的修订版本),因此修订版本的相对顺序在服务器端得到尊重。

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

包修订版本

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

由于 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
                ...

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

警告

最佳实践

对于任何给定的配方修订版本 + package_id 拥有多个包修订版本是一种气味或潜在的不良实践。这意味着在没有必要的情况下重建了一些东西,浪费了计算和存储资源。有一些方法可以避免这样做,例如 conan create . --build=missing:hello* 仅在包二进制文件不存在时才构建它(或运行 conan graph info 也可以返回需要构建的内容的信息)。