依赖于同一工具需求的多个不同版本

注意

这是一个 高级 用例。在绝大多数情况下,它都不是必需的。

在一般情况下,尝试做这样的事情

def build_requirements(self):
    self.tool_requires("gcc/1.0")
    self.tool_requires("gcc/2.0")

将生成一个“冲突”,显示类似 Duplicated requirement 的错误。在大多数情况下,这是正确的,因为很明显不可能使用同一编译器的 2 个版本来构建当前软件包。

但是,在某些特殊情况下,需要这样做。 让我们重现潜在的场景。 请首先克隆源代码以重现此项目。 您可以在 GitHub 上的 examples2 存储库中找到它们

git clone https://github.com/conan-io/examples2.git
cd examples2/examples/graph/tool_requires/different_versions

我们在那里有一个 gcc 伪造配方

class Pkg(ConanFile):
    name = "gcc"

    def package(self):
        echo = f"@echo off\necho MYGCC={self.version}!!"
        save(self, os.path.join(self.package_folder, "bin", f"mygcc{self.version}.bat"), echo)
        save(self, os.path.join(self.package_folder, "bin", f"mygcc{self.version}.sh"), echo)
        os.chmod(os.path.join(self.package_folder, "bin", f"mygcc{self.version}.sh"), 0o777)

这不是一个真正的编译器,它使用 shell 或 bat 脚本来伪造它,当执行时,它会打印 MYGCC=current-version。 请注意,二进制文件本身称为 mygcc1.0mygcc2.0,也就是说,它在可执行文件名称本身中包含版本。

我们可以为 gcc/1.0gcc/2.0 创建 2 个不同的版本

$ conan create gcc --version=1.0
$ conan create gcc --version=2.0

现在,在 wine 文件夹中,有一个类似这样的 conanfile.py

class Pkg(ConanFile):
    name = "wine"
    version = "1.0"

    def build_requirements(self):
        # If we specify "run=False" they no longer conflict
        self.tool_requires("gcc/1.0", run=False)
        self.tool_requires("gcc/2.0", run=False)

    def generate(self):
        # It is possible to individually reference each one
        gcc1 = self.dependencies.build["gcc/1.0"]
        assert gcc1.ref.version == "1.0"
        gcc2 = self.dependencies.build["gcc/2.0"]
        assert gcc2.ref.version == "2.0"

    def build(self):
        ext = "bat" if platform.system() == "Windows" else "sh"
        self.run(f"mygcc1.0.{ext}")
        self.run(f"mygcc2.0.{ext}")

第一个重点是 build_requirements() 方法,它对两个版本都执行 tool_requires(),但定义了 run=False这非常重要:我们告诉 Conan 我们实际上不需要运行这些软件包中的任何内容。 由于 tool_requires 是不可见的,它们不定义头文件或库,因此 Conan 无法将这 2 个 tool_requires 识别为冲突。 因此,可以构建依赖关系图而不会出现错误,并且 wine/1.0 软件包将包含对 gcc/1.0gcc/2.0 的 2 个不同的工具需求。

当然,我们不会运行这些 tool_requires 中的任何内容是不正确的,但是现在 Conan 并不知道这一点,并且完全由用户负责管理它。

警告

使用 run=False 会使 tool_requires() 完全不可见,这意味着配置文件 [tool_requires] 将无法覆盖其版本,但它将创建一个额外的工具需求依赖项,其中包含从配置文件注入的版本。 您可能想要使用类似 !wine/*: gcc/3.0 的内容排除特定软件包。

配方仍然可以在 generate() 方法中访问每个不同的 tool_require 版本,只需提供完整的引用,例如 self.dependencies.build["gcc/1.0"]

最后,最重要的是这些工具的使用完全由用户负责。 包含可执行文件的两个 tool_requiresbin 文件夹将位于路径中,这要归功于默认更新 PATH 环境变量的 VirtualBuildEnv 生成器。 在这种情况下,可执行文件是不同的,例如 mygcc1.0.sh```and ``mygcc2.0.sh,所以这不是问题,并且每个都将在其软件包中找到。

但是,如果可执行文件完全相同,例如 gcc.exe,则需要获取完整文件夹(通常在 generate() 方法中),例如 self.dependencies.build["gcc/1.0"].cpp_info.bindir,并使用完整路径来消除歧义。

让我们看看它的工作原理。 如果我们执行

$ conan create wine
...
wine/1.0: RUN: mygcc1.0.bat
MYGCC=1.0!!

wine/1.0: RUN: mygcc2.0.bat
MYGCC=2.0!!