版本

本节介绍如何创建给定软件包的不同版本,首先从手动更改 conanfile.py 配方中的 version 属性开始,然后介绍 set_version() 方法作为自动定义软件包版本的机制。

注意

本节使用非常简单的空配方,不构建任何代码,因此没有 build()package() 等,以最简单的配方来说明版本控制,并使示例易于运行且非常快速和简单。在实际应用中,配方将是全面的配方,如教程前几节所示,构建实际的库和软件包。

让我们从一个非常简单的配方开始

conanfile.py
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 = "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.0pkg/1.1。Conan 缓存可以为同一个 pkg 软件包存储任意数量的不同版本和配置。

自动化版本

除了手动更改 conanfile.py 中的版本外,还可以通过 2 种不同的方法来自动化它。

首先,可以直接在命令行中提供 version。在上面的示例中,我们可以从配方中删除 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 版本中推断出来。

假设我们在 repo 中有一个 version.txt 文件,其中仅包含版本字符串 1.3。那么,可以这样做

conanfile.py
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

也可以组合命令行版本定义,如果未提供命令行参数,则回退到从文件读取,语法如下

conanfile.py
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 标签获取版本

conanfile.py
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

注意

最佳实践

  • 我们可以尝试使用分支名称或提交作为版本号。但是,这可能有一些缺点,例如,当需要此软件包时,它需要在每个需要此软件包的其他配方中都有显式的 requires = "pkg/commit",并且可能难以一致地更新使用者,以及知道正在使用较新还是较旧的依赖项。

需要新版本

当创建新的软件包版本时,如果其他需要此软件包的配方包含显式的 requires,则会固定确切的版本,例如

app/conanfile.py
from conan import ConanFile

class AppRecipe(ConanFile):
    name = "app"
    version = "1.0"
    requires = "pkg/1.0"

然后,安装或创建 app 配方将继续需要和使用 pkg/1.0 版本,而不是更新的版本。要开始使用新的 pkg 版本,必须显式更新 requires,例如

app/conanfile.py
from conan import ConanFile

class AppRecipe(ConanFile):
    name = "app"
    version = "1.0"
    requires = "pkg/1.5"

这个过程虽然实现了非常好的可重现性和可追溯性,但如果我们管理一个庞大的依赖关系图,并且希望更快地使用最新的依赖项版本并减少人工干预,则可能会有点乏味。为了自动化这个过程,可以使用下一节中解释的版本范围