版本¶
本节解释如何创建给定软件包的不同版本,首先从手动修改 conanfile.py
recipe 中的 version 属性开始,然后介绍 set_version()
方法作为自动化定义软件包版本的机制。
注意
本节使用非常简单的空 recipes,不构建任何代码(因此没有 build()
、package()
等方法),以便用最简单的 recipes 来演示版本控制,并使示例易于运行、快速且简单。在实际情况中,recipes 会是完整的,如同在教程前面章节中看到的那样,构建实际的库和软件包。
让我们从一个非常简单的 recipe 开始
from conan import ConanFile
class pkgRecipe(ConanFile):
name = "pkg"
version = "1.0"
# The recipe would export files and package them, but not really
# necessary for the purpose of this part of the tutorial
# exports_sources = "include/*"
# def package(self):
# ...
我们可以用以下命令创建 pkg/1.0
软件包
$ conan create .
...
pkg/1.0 .
...
$ conan list "pkg/*"
Local Cache
pkg
pkg/1.0
如果我们现在对该库的源文件进行一些更改,这将是一个新版本,我们可以将 conanfile.py
中的 version 修改为 version = "1.1"
并创建新的 pkg/1.1
版本
# Make sure you modified conanfile.py to version=1.1
$ conan create .
...
pkg/1.1 .
...
$ conan list "pkg/*"
Local Cache
pkg
pkg/1.0
pkg/1.1
正如我们所见,现在在我们的缓存中可以看到 pkg/1.0
和 pkg/1.1
。Conan 缓存可以存储同一个 pkg
软件包的任意数量的不同版本和配置。
自动化版本控制¶
除了手动修改 conanfile.py
中的 version 外,还可以通过两种不同的方法来实现自动化。
首先,可以在命令行中直接提供 version
。在上面的示例中,我们可以从 recipe 中移除 version
属性,然后执行
# Make sure you removed the version attribute in conanfile.py
$ conan create . --version=1.2
...
pkg/1.2 .
...
$ conan list "pkg/*"
Local Cache
pkg
pkg/1.0
pkg/1.1
pkg/1.2
另一种可能性是使用 set_version()
方法动态定义版本,例如,如果版本已存在于源代码或文本文件中,或者应从 git 版本中推导出来。
假设我们在仓库中有一个 version.txt
文件,它只包含版本字符串 1.3
。那么,可以这样做
from conan import ConanFile
from conan.tools.files import load
class pkgRecipe(ConanFile):
name = "pkg"
def set_version(self):
self.version = load(self, "version.txt")
# No need to specify the version in CLI arg or in recipe attribute
$ conan create .
...
pkg/1.3 .
...
$ conan list "pkg/*"
Local Cache
pkg
pkg/1.0
pkg/1.1
pkg/1.2
pkg/1.3
还可以结合命令行版本定义,如果未提供命令行参数,则回退到从文件读取版本,使用以下语法:
def set_version(self):
# if self.version is already defined from CLI --version arg, it will
# not load version.txt
self.version = self.version or load(self, "version.txt")
# This will create the "1.4" version even if the version.txt file contains "1.3"
$ conan create . --version=1.4
...
pkg/1.4 .
...
$ conan list "pkg/*"
Local Cache
pkg
pkg/1.0
pkg/1.1
pkg/1.2
pkg/1.3
pkg/1.4
同样,也可以从 Git 标签获取版本
from conan import ConanFile
from conan.tools.scm import Git
class pkgRecipe(ConanFile):
name = "pkg"
def set_version(self):
git = Git(self)
tag = git.run("describe --tags")
self.version = tag
# assuming this is a git repo, and it was tagged to 1.5
$ git init .
$ git add .
$ git commit -m "initial commit"
$ git tag 1.5
$ conan create .
...
pkg/1.5 .
...
$ conan list "pkg/*"
Local Cache
pkg
pkg/1.0
pkg/1.1
pkg/1.2
pkg/1.3
pkg/1.4
pkg/1.5
注意
最佳实践
我们可以尝试使用诸如分支名称或提交哈希之类的内容作为版本号。但这可能有一些缺点,例如,当需要这个软件包时,每个需要它的其他软件包 recipe 都需要一个显式的
requires = "pkg/commit"
,并且可能难以一致地更新消费者,也难以知道使用的是较新还是较旧的依赖项。
引用新版本¶
创建新软件包版本后,如果其他需要此软件包的 recipes 包含显式的 requires
,并锁定精确版本,例如:
from conan import ConanFile
class AppRecipe(ConanFile):
name = "app"
version = "1.0"
requires = "pkg/1.0"
然后,安装或创建 app
recipe 将继续引用和使用 pkg/1.0
版本,而不是更新的版本。要开始使用新的 pkg
版本,需要显式更新 requires
,例如:
from conan import ConanFile
class AppRecipe(ConanFile):
name = "app"
version = "1.0"
requires = "pkg/1.5"
这个过程虽然可以实现很好的可重现性和可追溯性,但如果我们要管理一个大型依赖图,并希望更快地使用最新的依赖版本,且减少人工干预,这可能会有点繁琐。为了实现自动化,可以使用下一节中解释的版本范围(version-ranges)。