添加依赖到包

之前的教程章节中,我们为“Hello World”C++ 库创建了一个 Conan 包。我们使用 conan.tools.scm.Git() 工具从 git 仓库检索源代码。到目前为止,该包没有任何对其他 Conan 包的依赖。 让我们解释如何以与我们在使用包章节中非常相似的方式将依赖项添加到我们的包中。 我们将使用fmt库为我们的“Hello World”库添加一些花哨的彩色输出。

请首先克隆源代码以重新创建此项目。 您可以在 GitHub 上的examples2 仓库中找到它们

$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/creating_packages/add_requires

您会注意到与之前的配方相比,conanfile.py 文件中的一些变化。 让我们检查相关部分

...
from conan.tools.build import check_max_cppstd, check_min_cppstd
...

class helloRecipe(ConanFile):
    name = "hello"
    version = "1.0"

    ...
    generators = "CMakeDeps"
    ...

    def validate(self):
        check_min_cppstd(self, "11")
        check_max_cppstd(self, "20")

    def requirements(self):
        self.requires("fmt/8.1.1")

    def source(self):
        git = Git(self)
        git.clone(url="https://github.com/conan-io/libhello.git", target=".")
        # Please, be aware that using the head of the branch instead of an immutable tag
        # or commit is not a good practice in general
        git.checkout("require_fmt")
  • 首先,我们设置 generators 类属性,以使 Conan 调用 CMakeDeps 生成器。 在之前的配方中,这是不需要的,因为我们没有任何依赖项。 CMakeDeps 将生成 CMake 找到 fmt 库所需的所有配置文件。

  • 接下来,我们使用 requires() 方法将 fmt 依赖项添加到我们的包中。

  • 请注意,我们在 source() 方法中添加了一行额外的代码。 我们使用 Git().checkout() 方法检出 require_fmt 分支中的源代码。 此分支包含源代码中的更改,以向库消息添加颜色,并且在 CMakeLists.txt 中声明我们正在使用 fmt 库。

  • 最后,请注意我们已将 validate() 方法添加到配方中。 我们已经在使用包章节中使用此方法,以针对不受支持的配置引发错误。 在这里,我们调用函数 check_min_cppstd()check_max_cppstd(),以验证我们在设置中至少使用 C++11 标准,至多使用 C++20 标准。

您可以使用 require_fmt 分支检查使用 fmt 库的新源代码。 您将看到 hello.cpp 文件将颜色添加到输出消息

#include <fmt/color.h>

#include "hello.h"

void hello(){
    #ifdef NDEBUG
    fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "hello/1.0: Hello World Release!\n");
    #else
    fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "hello/1.0: Hello World Debug!\n");
    #endif
    ...

让我们使用当前的默认配置从源代码构建包,然后让 test_package 文件夹测试该包。 您现在应该看到带有颜色的输出消息

$ conan create . --build=missing
-------- Exporting the recipe ----------
...
-------- Testing the package: Running test() ----------
hello/1.0 (test package): Running test()
hello/1.0 (test package): RUN: ./example
hello/1.0: Hello World Release!
  hello/1.0: __x86_64__ defined
  hello/1.0: __cplusplus 201103
  hello/1.0: __GNUC__ 4
  hello/1.0: __GNUC_MINOR__ 2
  hello/1.0: __clang_major__ 13
  hello/1.0: __clang_minor__ 1
  hello/1.0: __apple_build_version__ 13160021

标头传递性

默认情况下,Conan 假定所需的依赖项标头是当前包的实现细节,以促进良好的软件工程实践,如低耦合和封装。 在上面的示例中,fmt 纯粹是 hello/1.0 包中的一个实现细节。 hello/1.0 的使用者不会知道任何关于 fmt 的信息,也不会有权访问它的标头。如果 hello/1.0 的使用者尝试添加 #include <fmt/color.h>,它将会失败,因为它无法找到该标头。

但是,如果 hello/1.0 包的公共标头具有 #includefmt 标头,这意味着必须将这些标头向下传播,以使 hello/1.0 的使用者能够成功编译。 由于这不是默认的预期行为,因此配方必须将其声明为

class helloRecipe(ConanFile):
    name = "hello"
    version = "1.0"

    def requirements(self):
        self.requires("fmt/8.1.1", transitive_headers=True)

这将把必要的编译标志和标头 includedirs 传播到 hello/1.0 的使用者。

注意

最佳实践

如果 hello/1.0 的使用者直接包含 fmt 标头,例如 #include <fmt/color.h>,那么,这样的使用者应该声明它自己的 self.requires("fmt/8.1.1") 需求,因为这是一个直接需求。 换句话说,即使从该使用者中删除了对 hello/1.0 的依赖,它仍然会依赖于 fmt,因此它不能滥用来自 hellofmt 标头的传递性,而应显式地声明它们。