版本控制简介¶
到目前为止,我们一直在使用固定版本的 `requires`,例如 `requires = "zlib/1.2.12"`。但有时依赖项会演进,发布新版本,消费者希望尽可能方便地更新到这些版本。
虽然始终可以编辑 `conanfiles` 并将版本明确更新为新版本,但 Conan 中有一些机制可以实现在不修改配方的情况下进行此类更新。
版本范围¶
一个 `requires` 可以使用 `pkgname/[version-range-expression]` 语法表达对给定包的某个版本范围的依赖。让我们看一个例子。请首先克隆源代码以重新创建此项目。您可以在 GitHub 的 examples2 存储库中找到它们。
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/consuming_packages/versioning
我们可以看到我们有
from conan import ConanFile
class CompressorRecipe(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeToolchain", "CMakeDeps"
def requirements(self):
self.requires("zlib/[~1.2]")
该 `requires` 包含表达式 `zlib/[~1.2]`,这意味着“大约” `1.2` 版本。这意味着,它可以解析为 `zlib/1.2.8`、`zlib/1.2.11` 或 `zlib/1.2.12`,但它不会解析为 `zlib/1.3.0` 等。在可用的匹配版本中,版本范围将始终选择最新的一个。
如果我们执行 conan install,我们将看到类似以下内容
$ conan install .
Graph root
conanfile.py: .../conanfile.py
Requirements
zlib/1.2.12#87a7211557b6690ef5bf7fc599dd8349 - Downloaded
Resolved version ranges
zlib/[~1.2]: zlib/1.2.12
如果我们尝试使用 `zlib/[<1.2.12]`,这意味着我们希望使用低于 `1.2.12` 的版本,但该版本被排除,因此满足该范围的最新版本将是 `zlib/1.2.11`。
$ conan install .
Resolved version ranges
zlib/[<1.2.12]: zlib/1.2.11
这同样适用于其他类型的要求,例如 `tool_requires`。让我们在配方中添加一个
from conan import ConanFile
class CompressorRecipe(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeToolchain", "CMakeDeps"
def requirements(self):
self.requires("zlib/[~1.2]")
def build_requirements(self):
self.tool_requires("cmake/[>3.10]")
我们看到它解析为可用的最新 CMake 包,版本至少为 `3.11`。
$ conan install .
...
Graph root
conanfile.py: .../conanfile.py
Requirements
zlib/1.2.12#87a7211557b6690ef5bf7fc599dd8349 - Cache
Build requirements
cmake/3.27.9#f305019023c2db74d1001c5afa5cf362 - Downloaded
Resolved version ranges
cmake/[>3.10]: cmake/3.27.9
zlib/[~1.2]: zlib/1.2.12
修订版¶
当包创建者对包配方或源代码进行了一些更改,但他们没有提高 `version` 以反映这些更改时会发生什么?Conan 有一个内部机制来跟踪这些修改,它被称为修订版。
配方修订版是与包名称和版本一起显示的哈希值,形式为 `pkgname/version#recipe_revision` 或 `pkgname/version@user/channel#recipe_revision`。配方修订版是配方内容和源代码的哈希值。因此,如果配方、其关联文件或此配方正在打包的源代码中发生任何变化,它将创建一个新的配方修订版。
您可以使用 conan list 命令列出现有的修订版
$ conan list "zlib/1.2.12#*" -r=conancenter
conancenter
zlib
zlib/1.2.12
revisions
82202701ea360c0863f1db5008067122 (2022-03-29 15:47:45 UTC)
bd533fb124387a214816ab72c8d1df28 (2022-05-09 06:59:58 UTC)
3b9e037ae1c615d045a06c67d88491ae (2022-05-13 13:55:39 UTC)
...
修订版总是解析为最新的(按创建或上传到服务器的时间顺序)修订版。虽然这不是一种常见的做法,但可以直接在 `conanfile` 中明确指定给定的配方修订版,例如:
def requirements(self):
self.requires("zlib/1.2.12#87a7211557b6690ef5bf7fc599dd8349")
然而,当创建新修订版时,这种机制可能难以维护和更新,因此通常情况下不应这样做。
锁定文件¶
版本范围的使用以及在不更改版本的情况下为给定包创建新修订版的可能性,可以实现快速、自动和方便的更新,而无需编辑配方。
但在某些情况下,还需要提供一组不可变和可重现的依赖项。此过程称为“锁定”,实现此目的的机制是“lockfile”文件。锁定文件是一个包含固定依赖项列表的文件,指定确切的版本和确切的修订版。因此,例如,锁定文件永远不会包含带有表达式的版本范围,而只包含固定依赖项。
锁定文件可以看作是某个时间点给定依赖关系图的快照。此快照必须是“可实现的”,也就是说,它需要是实际上可以从 conanfile 配方重现的状态。此锁定文件可以在以后使用,以强制保持相同的状态,即使有新的包版本创建。
让我们看看锁定文件的实际应用。首先,在我们的示例中将依赖项固定到 `zlib/1.2.11`
def requirements(self):
self.requires("zlib/1.2.11")
然后,捕获一个锁定文件
conan lock create .
-------- Computing dependency graph ----------
Graph root
conanfile.py: .../conanfile.py
Requirements
zlib/1.2.11#4524fcdd41f33e8df88ece6e755a5dcc - Cache
Generated lockfile: .../conan.lock
让我们看看锁定文件 `conan.lock` 包含什么
{
"version": "0.5",
"requires": [
"zlib/1.2.11#4524fcdd41f33e8df88ece6e755a5dcc%1650538915.154"
],
"build_requires": [],
"python_requires": []
}
现在,让我们恢复原始的 `requires` 版本范围
def requirements(self):
self.requires("zlib/[~1.2]")
并运行 conan install .,默认情况下它会找到 `conan.lock`,并运行等效的 conan install . --lockfile=conan.lock
conan install .
Graph root
conanfile.py: .../conanfile.py
Requirements
zlib/1.2.11#4524fcdd41f33e8df88ece6e755a5dcc - Cache
请注意,版本范围不再解析,并且它没有获取 `zlib/1.2.12` 依赖项,即使它是允许的范围 `zlib/[~1.2]`,因为 `conan.lock` 锁定文件强制它保持在 `zlib/1.2.11` 以及确切的修订版。