build_requirements()

build_requirements() 方法在功能上与 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 的一个专门实例,具有一些预定义的 trait 值。有关 trait 的更多信息,请参阅 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),从而避免下载大型二进制文件,只下载通常非常快的 recipe。这意味着在大多数情况下,这些 confs 是不必要的,Conan 的默认设置就很好,请在使用它们时了解其权衡。

tool_requires()

tool_requires 等同于 requires(),并带有以下 traits:

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

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

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

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

  • libs=False:工具依赖项没有供消费者链接的库(如果它有库,它们将位于“build”上下文中,并且可能与消费者的“host”上下文不兼容)。

请记住,tool_requires 专门用于依赖 cmakeninja 等工具,这些工具在“build”上下文中运行,而不是用于将链接到二进制文件中的类似库的依赖项。对于库或类似库的依赖项,请使用 requirestest_requires

<host_version>

警告

此功能是实验性的,可能会发生重大更改。有关更多信息,请参阅 Conan 稳定性 部分。

当您将相同的包 recipe 同时用作 *requires* 和 *tool_requires*,并且希望避免下游冲突(例如,如果用户决定在 *host* 上下文中覆盖原始 *requires* 版本,用户最终可能会在同一依赖项的 host 和 build 上下文中得到两个不同版本)时,此语法非常有用。

简而言之,<host_version> 说明符使我们能够确保为 *tool_requires* 解析的版本始终与 host 需求的版本匹配。

例如,我们用一个简单的 recipe 展示如何使用 *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 和 build 上下文使用的是该依赖项的相同版本。

此外,可以使用 <host_version:mylib> 语法来指定要跟踪的包的名称,以防 *requires* 和 *tool_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(),并带有以下 traits:

  • test=True。此依赖项是“test”依赖项,存在于“host”上下文中,但不是最终产品的一部分。

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

警告

由于 test_requires 定义了 visible=False trait,因此必须小心,避免与正常需求出现同名包的传递依赖,这些同名包也被用在 test_requires 中。这是因为具有直接 visible=False 需求可能会产生冲突,如果传递依赖项对当前 recipe 作为测试依赖项要求的同一个包具有 visible=True 需求。在这些规则不同可见性到达同一个包的情况下,将使用可见的传递依赖项并将其向下传播。

如果需要,可以进一步修改 tool_requires()test_requires() 的各个 traits,例如:

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

test_requires()force=True trait 提供了支持,以防出现版本冲突的传递测试需求,同样 tool_requires() 也支持 override=True trait,用于覆盖直接工具需求可能存在的传递依赖项。

注意

最佳实践

  • tool_requires 仅用于构建时的 **工具**,而不是用于包含和链接到消费者包中的库。对于具有特殊特征的库,请使用带有自定义 trait 值的 requires()

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