为提供多个库的 Conan 包定义组件¶
在 关于 package_info() 方法的教程章节 中,我们学习了如何为消费者定义包的信息,例如库名或包含和库文件夹。在教程中,我们创建了一个只有一个库的包,消费者链接到该库。然而,在某些情况下,库将其功能分离成不同的组件。这些组件可以独立使用,在某些情况下,它们可能需要同一库中的其他组件或来自其他库的组件。例如,考虑一个像 OpenSSL 这样的库,它提供libcrypto和libssl,其中libssl依赖于libcrypto。
Conan 提供了一种抽象这些信息的方法,通过使用 Conan 包的每个独立组件的 components 属性 CppInfo 对象来定义每个组件的信息。消费者也可以选择特定的组件进行链接,而不链接包的其余部分。
让我们以一个游戏引擎库为例,它提供了几个组件,如algorithms、ai、rendering和network。ai和rendering都依赖于algorithms组件。
游戏引擎包的组件¶
请首先克隆源代码以重新创建此项目。您可以在 GitHub 的 examples2 仓库中找到它们。
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/examples/conanfile/package_info/components
您可以检查项目的内容
.
├── CMakeLists.txt
├── conanfile.py
├── include
│ ├── ai.h
│ ├── algorithms.h
│ ├── network.h
│ └── rendering.h
├── src
│ ├── ai.cpp
│ ├── algorithms.cpp
│ ├── network.cpp
│ └── rendering.cpp
└── test_package
├── CMakeLists.txt
├── CMakeUserPresets.json
├── conanfile.py
└── src
└── example.cpp
正如您所见,每个组件都有相应的源代码和一个 CMakeLists.txt 文件来构建它们。我们还有一个 test_package,我们将用它来测试独立组件的消费。
首先,让我们看看 conanfile.py 中的 package_info() 方法,以及我们如何声明游戏引擎包的消费者想要提供的每个组件的信息
...
def package_info(self):
self.cpp_info.components["algorithms"].libs = ["algorithms"]
self.cpp_info.components["algorithms"].set_property("cmake_target_name", "algorithms")
self.cpp_info.components["network"].libs = ["network"]
self.cpp_info.components["network"].set_property("cmake_target_name", "network")
self.cpp_info.components["ai"].libs = ["ai"]
self.cpp_info.components["ai"].requires = ["algorithms"]
self.cpp_info.components["ai"].set_property("cmake_target_name", "ai")
self.cpp_info.components["rendering"].libs = ["rendering"]
self.cpp_info.components["rendering"].requires = ["algorithms"]
self.cpp_info.components["rendering"].set_property("cmake_target_name", "rendering")
有几点是相关的
我们通过设置
cpp_info.components
属性中的信息来声明每个组件生成的库。您可以为每个组件设置与self.cpp_info
对象相同的信息。组件的cpp_info
具有一些默认定义,就像 self.cpp_info 一样。例如,cpp_info.components
对象提供了.includedirs
和.libdirs
属性来定义这些位置,但 Conan 默认将它们的值设置为["lib"]
和["include"]
,因此在这种情况下没有必要添加它们。我们还使用
.requires
属性来声明组件的依赖关系。通过此属性,您可以声明组件级别的要求,不仅限于同一配方中的组件,还可以是声明为 Conan 包的 requires 的其他包中的组件。我们正在使用 属性模型 更改组件的默认目标名称。默认情况下,Conan 为组件设置的目标名称类似于
<package_name::component_name>
,但对于本教程,我们将组件目标名称设置为仅包含组件名称,省略::
。当
cpp_info
具有全局构建信息(例如cpp_info.defines
)时,它不会继承到组件。如果您希望与组件共享此信息,则需要为每个组件显式设置它。
您可以查看消费者部分,检查 test_package 文件夹。首先是 conanfile.py
...
def generate(self):
deps = CMakeDeps(self)
deps.check_components_exist = True
deps.generate()
您可以看到我们为 CMakeDeps 设置了 check_components_exist 属性。这并非必需,只是为了展示如果您希望在组件不存在时让消费者失败,可以如何进行。因此,CMakeLists.txt 可能看起来像这样
cmake_minimum_required(VERSION 3.15)
project(PackageTest CXX)
find_package(game-engine REQUIRED COMPONENTS algorithms network ai rendering)
add_executable(example src/example.cpp)
target_link_libraries(example algorithms
network
ai
rendering)
并且,如果任何组件目标不存在,find_package()
调用将失败。
让我们运行示例
$ conan create .
...
game-engine/1.0: RUN: cmake --build "/Users/barbarian/.conan2/p/t/game-d6e361d329116/b/build/Release" -- -j16
[ 12%] Building CXX object CMakeFiles/algorithms.dir/src/algorithms.cpp.o
[ 25%] Building CXX object CMakeFiles/network.dir/src/network.cpp.o
[ 37%] Linking CXX static library libnetwork.a
[ 50%] Linking CXX static library libalgorithms.a
[ 50%] Built target network
[ 50%] Built target algorithms
[ 62%] Building CXX object CMakeFiles/ai.dir/src/ai.cpp.o
[ 75%] Building CXX object CMakeFiles/rendering.dir/src/rendering.cpp.o
[ 87%] Linking CXX static library libai.a
[100%] Linking CXX static library librendering.a
[100%] Built target ai
[100%] Built target rendering
...
======== Launching test_package ========
...
-- Conan: Component target declared 'algorithms'
-- Conan: Component target declared 'network'
-- Conan: Component target declared 'ai'
-- Conan: Component target declared 'rendering'
...
[ 50%] Building CXX object CMakeFiles/example.dir/src/example.cpp.o
[100%] Linking CXX executable example
[100%] Built target example
======== Testing the package: Executing test ========
game-engine/1.0 (test package): Running test()
game-engine/1.0 (test package): RUN: ./example
I am the algorithms component!
I am the network component!
I am the ai component!
└───> I am the algorithms component!
I am the rendering component!
└───> I am the algorithms component!
您可以检查要求一个不存在的组件将引发错误。将 nonexistent 组件添加到 find_package() 调用中
cmake_minimum_required(VERSION 3.15)
project(PackageTest CXX)
find_package(game-engine REQUIRED COMPONENTS nonexistent algorithms network ai rendering)
add_executable(example src/example.cpp)
target_link_libraries(example algorithms
network
ai
rendering)
然后再次测试包
$ conan test test_package game-engine/1.0
...
Conan: Component 'nonexistent' NOT found in package 'game-engine'
Call Stack (most recent call first):
CMakeLists.txt:4 (find_package)
-- Configuring incomplete, errors occurred!
...
ERROR: game-engine/1.0 (test package): Error in build() method, line 22
cmake.configure()
ConanException: Error 1 while executing
另请参阅
如果您想使用在 editable
模式下定义的配方组件,请参阅 使用组件和可编辑包 中的示例。