依赖于同一工具依赖的不同版本¶
注意
这是一个高级用例。在绝大多数情况下,它不是必需的。
在一般情况下,尝试这样做
def build_requirements(self):
self.tool_requires("gcc/1.0")
self.tool_requires("gcc/2.0")
将产生“冲突”,并显示类似 Duplicated requirement
的错误。这在大多数情况下是正确的,因为很明显无法使用同一编译器的两个版本来构建当前包。
然而,在某些特殊情况下,可能需要这样做。让我们重现这种潜在场景。请首先克隆源代码以重现此项目。您可以在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.0
和 mygcc2.0
,也就是说,它在可执行文件名本身中包含版本。
我们可以使用以下命令为 gcc/1.0
和 gcc/2.0
创建两个不同版本
$ 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 将这两个 tool_requires
识别为冲突。因此,依赖图可以无错误地构建,并且 wine/1.0
包将包含对 gcc/1.0
和 gcc/2.0
的两个不同工具依赖。
当然,我们不会运行这些 tool_requires
中的任何东西的说法并非完全属实,但现在 Conan 并不知情,管理它完全是用户的责任。
警告
使用 run=False
会使 tool_requires()
完全不可见,这意味着配置文件 [tool_requires]
将无法覆盖其版本,但它会创建一个额外的工具依赖,其版本来自配置文件注入。您可能希望使用类似 !wine/*: gcc/3.0
的方式排除特定包。
该配方在 generate()
方法中仍然可以访问每个不同版本的 tool_require
,只需提供完整引用,例如 self.dependencies.build["gcc/1.0"]
。
最后,最重要的一点是,这些工具的使用完全由用户负责。包含可执行文件的两个 tool_requires
的 bin
文件夹将位于 PATH 中,这要归功于默认更新 PATH 环境变量的 VirtualBuildEnv
生成器。在这种情况下,可执行文件是不同的,例如 mygcc1.0.sh
和 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!!