build_requirements()

build_requirements() 方法在功能上等同于 requirements() 方法,它紧随后者执行。理论上,此方法中的所有内容都可以在 requirements() 方法的末尾完成,因此它并非严格必要。不过,build_requirements() 提供了一个专用位置来定义 tool_requirestest_requires,这很有益处。

def build_requirements(self):
   self.tool_requires("cmake/3.23.5")
   self.test_requires("gtest/1.13.0")

对于简单的情况,属性语法就足够了,例如 tool_requires = "cmake/3.23.5"test_requires = "gtest/1.13.0"。对于条件性或参数化的依赖,则可能需要方法形式。

tool_requirestest_requires 方法只是 requires 的一个特殊实例,预定义了一些特性值。关于特性的更多信息,请参阅 requires() 参考

有 2 个实验性 confs 可用于避免这些类型依赖的扩展

  • tools.graph:skip_build 允许完全跳过 tool_requires 依赖。这需要在满足两个条件时才能实现:要求这些工具的包不需要从源代码构建,并且工具依赖不影响消费者的 package_id。如果出现这种情况,Conan 将会抛出错误。

  • tools.graph:skip_test 允许完全跳过 test_requires 依赖。如果这些依赖被跳过,但随后某些包需要从源代码构建且 tools.build:skip_test 未激活,则会找不到 test_requires。因此在大多数情况下,tools.build:skip_test 也应该被定义。

请注意,如果工具和/或测试依赖被跳过,它们将不会成为依赖图的一部分,也不会成为可能生成的锁定文件或包列表的一部分,这可能会影响未来的可重现性。此外,在大多数情况下,Conan 能够将工具和测试依赖标记为非必要 (Skip),从而避免下载大型二进制文件,只下载通常非常快速的配方。这意味着在大多数情况下,这些 confs 是不必要的,并且 Conan 的默认设置很好,请在使用它们时了解其中的权衡。

tool_requires()

tool_requires 等同于带有以下特性的 requires()

  • build=True。此依赖位于“构建”上下文,在构建时是必需的,但在应用运行时则不是,并且将接收“构建”配置文件和配置。

  • visible=False。工具依赖不会向下游传播。例如,一个包可以调用 tool_requires("cmake/3.23.5"),但这不意味着消费者包也使用 cmake,它们甚至可以使用不同的构建系统或不同的版本,而不会引起冲突。

  • run=True。此依赖包含一些在构建时需要运行的可执行文件或运行时。

  • headers=False:工具依赖没有头文件。

  • libs=False:工具依赖不包含可供消费者链接的库(如果它包含库,它们将位于“构建”上下文,并可能与消费者包的“主机”上下文不兼容)。

请注意,tool_requires 仅用于依赖在“构建”上下文运行的工具,如 cmakeninja,而不用于将链接到二进制文件的库类依赖。对于库或库类依赖,请使用 requirestest_requires

<host_version>

警告

此特性是实验性的,可能会发生破坏性更改。更多信息请参阅 Conan 稳定性 部分。

当您将同一个包配方同时用作 requirestool_requires 时,此语法非常有用,它可以帮助避免下游冲突,特别是在任何用户决定覆盖 host 上下文中的原始 requires 版本时。换句话说,用户可能会在同一个依赖的主机和构建上下文中得到两个不同的版本。

简而言之,<host_version> 指定符允许我们确保为 tool_requires 解析的版本始终与主机依赖的版本匹配。

例如,让我们展示一个使用 protobuf 的简单配方

from conan import ConanFile

class mylibRecipe(ConanFile):
    name = "mylib"
    version = "0.1"
    def requirements(self):
        self.requires("protobuf/3.18.1")
    def build_requirements(self):
        self.tool_requires("protobuf/<host_version>")

然后,如果任何用户想要使用 mylib/0.1,但同时使用 protobuf 的另一个版本,覆盖它应该不会有任何问题

from conan import ConanFile

class myappRecipe(ConanFile):
    name = "myapp"
    version = "0.1"
    def requirements(self):
        self.requires("mylib/0.1")
        self.requires("protobuf/3.21.9", override=True)

上游定义的 <host_version> 确保主机和构建上下文使用该依赖的相同版本。

此外,如果 requirestool_requires 具有不同的名称,可以使用 <host_version:mylib> 语法来指定要跟踪的包名称。例如

from conan import ConanFile

class mylibRecipe(ConanFile):
    name = "mylib"
    version = "0.1"
    def requirements(self):
        self.requires("gettext/2.31")
    def build_requirements(self):
        self.tool_requires("libgettext/<host_version:gettext>")

test_requires

test_requires 等同于带有以下特性的 requires()

  • test=True。此依赖是“测试”依赖,存在于“主机”上下文,但不旨在成为最终产品的一部分。

  • visible=False。测试依赖不会向下游传播。例如,一个包可以调用 self.test_requires("gtest/1.13.0"),但这不意味着消费者包也使用 gtest,它们甚至可以使用不同的测试框架,或者使用不同版本的 gtest,而不会引起冲突。

如果需要,可以进一步修改 tool_requires()test_requires() 的单独特性,例如

def build_requirements(self):
   self.tool_requires("cmake/3.23.5", options={"shared": False})

test_requires() 允许使用 force=True 特性,以应对存在传递性测试依赖版本冲突的情况;类似地,tool_requires() 支持 override=True 特性,用于覆盖直接工具依赖可能存在的传递性依赖。

注意

最佳实践

  • tool_requires 专门用于构建时工具,而不是用于会被包含并链接到消费者包中的库。对于具有某些特殊特性的库,请使用带有自定义特性值的 requires()

  • self.test_requires()self.tool_requires() 方法应仅在 build_requirements() 方法中使用,唯一可能的例外是 requirements() 方法。禁止在任何其他方法中使用它们。要在某些方法中必要时访问依赖信息,应使用 self.dependencies 属性。