锁定文件¶
锁定文件是一种实现可复现依赖关系的机制,即使创建了这些依赖关系的新版本或修订版本也是如此。 让我们通过一个实际的例子来看看,首先克隆 examples2 仓库
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/versioning/lockfiles/intro
在这个文件夹中,我们有一个小项目,由 3 个包组成:一个 matrix
包,模拟一些数学库,一个 engine
包,模拟一些游戏引擎,以及一个 sound32
包,模拟一些 32 位系统的声音库。 这些包实际上大部分都是空的,它们不构建任何代码,但它们非常适合学习锁定文件的概念。
我们将首先创建第一个 matrix/1.0
版本
$ conan create matrix --version=1.0
现在我们可以检查 engine
文件夹中的配方
class Engine(ConanFile):
name = "engine"
settings = "arch"
def requirements(self):
self.requires("matrix/[>=1.0 <2.0]")
if self.settings.arch == "x86":
self.requires("sound32/[>=1.0 <2.0]")
让我们移动到 engine
文件夹并安装其依赖项
$ cd engine
$ conan install .
...
Requirements
matrix/1.0#905c3f0babc520684c84127378fefdd0 - Cache
Resolved version ranges
matrix/[>=1.0 <2.0]: matrix/1.0
由于 matrix/1.0
版本在有效范围内,因此它被解析和使用。 但是如果有人创建了新的 matrix/1.1
或 1.X
版本,它也会被自动使用,因为它也在有效范围内。 为了避免这种情况,我们将通过创建 conan.lock
锁定文件来捕获当前依赖项的“快照”
$ conan lock create .
$ cat conan.lock
{
"version": "0.5",
"requires": [
"matrix/1.0#905c3f0babc520684c84127378fefdd0%1675278126.0552447"
],
"build_requires": [],
"python_requires": []
}
我们可以看到创建的 conan.lock
锁定文件包含 matrix/1.0
版本及其修订版本。 但是 sound32/1.0
不在锁定文件中,因为对于默认配置配置文件(不是 x86
),此 sound32
不是依赖项。
现在,创建了一个新的 matrix/1.1
版本
$ cd ..
$ conan create matrix --version=1.1
$ cd engine
看看当我们为 engine 发出新的 conan install
命令时会发生什么
$ conan install .
# equivalent to conan install . --lockfile=conan.lock
...
Requirements
matrix/1.0#905c3f0babc520684c84127378fefdd0 - Cache
正如我们所看到的,即使新的 matrix/1.1
在有效范围内,也没有被使用! 发生这种情况是因为默认情况下,如果找到 conan.lock
文件,将使用 --lockfile=conan.lock
。 锁定的 matrix/1.0
版本和修订版本将用于解析范围,并且 matrix/1.1
将被忽略。
同样,可以发出其他 Conan 命令,如果 conan.lock
在那里,它将被使用
$ conan graph info . --filter=requires # --lockfile=conan.lock is implicit
# display info for matrix/1.0
$ conan create . --version=1.0 # --lockfile=conan.lock is implicit
# creates the engine/1.0 package, using matrix/1.0 as dependency
如果打算使用锁定文件,例如在 CI 中,最好显式地使用参数 --lockfile=conan.lock
。
多配置锁定文件¶
我们在上面看到,如果架构是 x86
,则 engine
具有对 sound32
包的条件依赖。 这也意味着上述锁定文件中没有捕获此类 sound32
包版本。
让我们首先创建 sound32/1.0
包,然后尝试安装 engine
$ cd ..
$ conan create sound32 --version=1.0
$ cd engine
$ conan install . -s arch=x86 # FAILS!
ERROR: Requirement 'sound32/[>=1.0 <2.0]' not in lockfile
发生这种情况是因为 conan.lock
锁定文件不包含 sound32
的锁定版本。 默认情况下,锁定文件是严格的,如果我们要锁定依赖项,则必须找到锁定文件内的匹配版本。 我们可以使用 --lockfile-partial
参数来放宽此假设
$ conan install . -s arch=x86 --lockfile-partial
...
Requirements
matrix/1.0#905c3f0babc520684c84127378fefdd0 - Cache
sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7 - Cache
Resolved version ranges
sound32/[>=1.0 <2.0]: sound32/1.0
这将设法部分锁定到 matrix/1.0
,并像往常一样解析 sound32
版本范围。 但是我们可以做得更好,我们可以扩展我们的锁定文件以锁定 sound32/1.0
版本,以避免新的 sound32
意外版本可能造成的破坏
$ conan lock create . -s arch=x86
$ cat conan.lock
{
"version": "0.5",
"requires": [
"sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675278904.0791488",
"matrix/1.0#905c3f0babc520684c84127378fefdd0%1675278900.0103245"
],
"build_requires": [],
"python_requires": []
}
现在,matrix/1.0
和 sound32/1.0
都锁定在我们的 conan.lock
锁定文件中。 可以在两种配置(64 位和 x86 架构)中使用此锁定文件,在一个给定配置未使用的锁定文件中包含版本没有问题,只要该配置所需的依赖项在其找到匹配的版本即可。
重要提示
锁定文件包含需求排序的列表,按版本和修订版本排序,因此最新版本和修订版本是在针对锁定文件解析时优先考虑的版本。 锁定文件可以包含同一包的两个或多个不同版本,仅仅是因为不同的版本范围需要它们。 排序将提供正确的逻辑,以便每个范围解析为每个有效版本。
如果锁定文件中的某个版本不适合有效范围,则不会使用它。 锁定文件无法强制依赖项违反 conanfile
要求的定义,因为它们是现有/可实现依赖图的“快照”,但无法定义“不可能的”依赖图。
演化锁定文件¶
即使锁定文件强制执行并约束可以为图解析的版本,但这并不意味着锁定文件不能演化。 实际上,锁定文件的受控演化对于重要的流程至关重要,例如持续集成,当希望隔离于其他可能的并发更改来测试图中一个更改的效果时。
在本节中,我们将介绍锁定文件的一些基本功能,这些功能允许这种演化。
首先,如果我们现在想在我们的 engine
中引入和测试新的 matrix/1.1
版本,而无需拉取可能也获得了新版本的许多其他依赖项,我们可以手动将 matrix/1.1
添加到锁定文件
$ Running: conan lock add --requires=matrix/1.1
$ cat conan.lock
{
"version": "0.5",
"requires": [
"sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675278904.0791488",
"matrix/1.1",
"matrix/1.0#905c3f0babc520684c84127378fefdd0%1675278900.0103245"
],
"build_requires": [],
"python_requires": []
}
需要明确的是:不一定建议使用 conan lock add
手动添加,可以使用其他方法自动执行该任务,这将在稍后解释。 这只是对原理和概念的介绍。
重要的想法是,现在我们在锁定文件中获得了 matrix
的 2 个版本,并且 matrix/1.1
在 matrix/1.0
之前,因此对于范围 matrix/[>=1.0 <2.0]
,第一个版本 (matrix/1.1
) 将被优先考虑。 这意味着当现在使用新的锁定文件时,它将解析为 matrix/1.1
版本(即使系统中存在 matrix/1.2
或更高版本)
$ conan install . -s arch=x86 --lockfile-out=conan.lock
Requirements
matrix/1.1#905c3f0babc520684c84127378fefdd0 - Cache
sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7 - Cache
$ cat conan.lock
{
"version": "0.5",
"requires": [
"sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675278904.0791488",
"matrix/1.1#905c3f0babc520684c84127378fefdd0%1675278901.7527816",
"matrix/1.0#905c3f0babc520684c84127378fefdd0%1675278900.0103245"
],
"build_requires": [],
"python_requires": []
}
请注意,现在 matrix/1.1
已被解析,并且其 revision
也存储在锁定文件中(因为 --lockfile-out=conan.lock
作为参数传递)。
诚然,以前的 matrix/1.0
版本未使用。 如上所述,在锁定文件中包含未使用的旧版本没有危害。 但是,如果我们想修剪未使用的版本和修订版本,我们可以为此目的使用 --lockfile-clean
$ conan install . -s arch=x86 --lockfile-out=conan.lock --lockfile-clean
...
Requirements
matrix/1.1#905c3f0babc520684c84127378fefdd0 - Cache
sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7 - Cache
...
$ cat conan.lock
{
"version": "0.5",
"requires": [
"sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675278904.0791488",
"matrix/1.1#905c3f0babc520684c84127378fefdd0%1675278901.7527816"
],
"build_requires": [],
"python_requires": []
}
值得注意的是,-lockfile-clean
可能会删除给定配置中的锁定版本。 例如,如果不是上述情况,而是使用 x86_64
架构,则 --lockfile-clean
将修剪“未使用”的 sound32
,因为在该配置中未使用它。 可以为每个不同的配置评估新的锁定文件,然后合并它们
$ conan lock create . --lockfile-out=64.lock --lockfile-clean
$ conan lock create . -s arch=x86 --lockfile-out=32.lock --lockfile-clean
$ cat 64.lock
{
"version": "0.5",
"requires": [
"matrix/1.1#905c3f0babc520684c84127378fefdd0%1675294635.6049662"
],
"build_requires": [],
"python_requires": []
}
$ cat 32.lock
{
"version": "0.5",
"requires": [
"sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675294637.9775107",
"matrix/1.1#905c3f0babc520684c84127378fefdd0%1675294635.6049662"
],
"build_requires": [],
"python_requires": []
}
$ conan lock merge --lockfile=32.lock --lockfile=64.lock --lockfile-out=conan.lock
$ cat conan.lock
{
"version": "0.5",
"requires": [
"sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675294637.9775107",
"matrix/1.1#905c3f0babc520684c84127378fefdd0%1675294635.6049662"
],
"build_requires": [],
"python_requires": []
}
这种多重清除 + 合并操作不是开发人员应该做的,只有 CI 脚本,以及一些将在稍后解释的高级 CI 流程。
另请参阅