修订

本节介绍如何在不显式创建新版本的情况下修改给定的 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,以定义 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)

重要提示

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

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

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

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

警告

行尾符问题

默认情况下,Git 将在 Windows 系统上使用 CRLF 行尾符检出文件。这会导致与 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 命令将仅上传最新的修订

# 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,拥有超过 1 个软件包修订是一种不良迹象或潜在的不良实践。这意味着在不必要时重建了某些东西,浪费了计算和存储资源。有一些方法可以避免这样做,例如 conan create . --build=missing:hello* 将仅在软件包二进制文件尚不存在时构建它(或者运行 conan graph info 也可以返回有关需要构建的内容的信息。)