修订版本¶
本节介绍了如何在修改 recipe 或源代码时,无需显式创建新版本,Conan 仍会通过修订版本机制在内部跟踪这些更改。
创建不同的修订版本¶
我们从一个基本的“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”
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
值。
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 克隆,应通过强制检出唯一的不可变标签或提交来保证唯一性。像分支名称或 HEAD 这样可移动的目标会失效,因为修订版本被认为是不可变的。
源代码或 recipe 中的任何更改都应始终意味着一个新的修订版本。
警告
行尾符问题
默认情况下,Git 在 Windows 系统上检出文件时使用 CRLF
行尾符。这导致与使用 LF
行尾符的 Linux 系统上的文件不同。由于文件不同,在 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 来定义它需要第一个创建的修订版本。
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、settings 等),则该二进制文件应该只构建一次。
由于 C 和 C++ 构建不是确定性的,同一包的后续构建即使未修改任何内容,也可能创建新的包修订版本。
# Build again 2 times the latest
$ conan create .
$ conan create .
在某些操作系统(如 Windows)中,此类构建可能不可复现,并且生成的 artifact 将具有不同的校验和,从而导致新的包修订版本。
$ 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
也可以返回需要构建的信息)。