添加包的依赖项¶
在上一教程部分中,我们为“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++ 标准至少为 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
包的公共头文件包含对 fmt
头文件的 #include
,那么这些头文件必须向下传递,以便 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
,因此它不能滥用 hello
的 fmt
头文件的传递性,而是必须明确声明它们。