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> 确保主机和构建上下文使用该依赖项的相同版本。

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

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,而不会造成冲突。

警告

由于 test_requires 定义了 visible=False 特性,因此必须小心避免与常规依赖项对 test_requires 中使用的相同包存在传递性依赖。这是因为直接的 visible=False 依赖项可能会在传递性依赖项对当前配方作为测试依赖项所需的相同包存在 visible=True 依赖项时,创建冲突。在这些不同可见性规则到达同一包的情况下,可见的传递性依赖项将被使用并向下游传播。

如有必要,可以进一步修改 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 属性。