为消费者定义信息:package_info() 方法¶
在前一个教程部分,我们解释了如何使用 package 方法 在 Conan 包中存储库的头文件和二进制文件。依赖于该包的消费者将重用这些文件,但我们必须提供一些额外的信息,以便 Conan 可以将其传递给构建系统,并且消费者可以使用该包。
例如,在我们的示例中,我们正在构建一个名为 *hello* 的静态库,它将在 Linux 和 macOS 中生成一个 *libhello.a* 文件,在 Windows 中生成一个 *hello.lib* 文件。此外,我们正在打包一个带有库函数声明的头文件 *hello.h*。 Conan 包最终在 Conan 本地缓存中具有以下结构
.
├── include
│ └── hello.h
└── lib
└── libhello.a
然后,想要链接到该库的消费者将需要一些信息
Conan 本地缓存中 *include* 文件夹的位置,以搜索 *hello.h* 文件。
要链接到的库文件的名称(*libhello.a* 或 *hello.lib*)
Conan 本地缓存中 *lib* 文件夹的位置,以搜索库文件。
Conan 提供了对消费者可能需要的全部信息的抽象,这些信息位于 ConanFile 的 cpp_info 属性中。此属性的信息必须在 package_info() 方法中设置。 让我们看一下我们的 *hello/1.0* Conan 包的 package_info()
方法
...
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
...
def package_info(self):
self.cpp_info.libs = ["hello"]
我们可以看到几件事
我们将 *hello* 库添加到
cpp_info
的libs
属性,以告知消费者他们应该链接来自该列表的库。我们**没有添加**有关库和头文件打包所在的 *lib* 或 *include* 文件夹的信息。
cpp_info
对象提供了.includedirs
和.libdirs
属性来定义这些位置,但 Conan 默认将其值设置为lib
和include
,因此在这种情况下无需添加。 如果您要将包文件复制到其他位置,则必须明确设置这些文件。 我们 Conan 包中package_info
方法的声明将等效于此方法
...
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
...
def package_info(self):
self.cpp_info.libs = ["hello"]
# conan sets libdirs = ["lib"] and includedirs = ["include"] by default
self.cpp_info.libdirs = ["lib"]
self.cpp_info.includedirs = ["include"]
在 package_info() 方法中设置信息¶
除了我们上面解释的有关可以在 package_info()
方法中设置的信息之外,还有一些典型的用例
为依赖于 settings 或 options 的使用者定义信息
自定义生成器提供给消费者的某些信息,例如 CMake 的目标名称或 pkg-config 的生成文件名
将配置值传播给消费者
将环境信息传播给消费者
为提供多个库的 Conan 包定义组件
让我们看看其中一些实际应用。 首先,如果您尚未克隆项目源,请克隆它们。 您可以在 GitHub 上的 examples2 仓库 中找到它们
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/creating_packages/package_information
为依赖于 settings 或 options 的使用者定义信息¶
对于本教程的这一部分,我们对库和 recipe 进行了一些更改。 让我们检查相关部分
库源中引入的更改¶
首先,请注意,我们正在使用来自 libhello 库的 另一个分支。 让我们检查库的 *CMakeLists.txt*
cmake_minimum_required(VERSION 3.15)
project(hello CXX)
...
add_library(hello src/hello.cpp)
if (BUILD_SHARED_LIBS)
set_target_properties(hello PROPERTIES OUTPUT_NAME hello-shared)
else()
set_target_properties(hello PROPERTIES OUTPUT_NAME hello-static)
endif()
...
如您所见,我们正在设置库的输出名称,具体取决于我们将库构建为静态(*hello-static*)还是共享(*hello-shared*)。 现在让我们看看如何将这些更改转换为 Conan recipe。
recipe 中引入的更改¶
要根据库的 *CMakeLists.txt* 中的更改更新我们的 recipe,我们必须根据 package_info()
方法中的 self.options.shared
选项有条件地设置库名称
class helloRecipe(ConanFile):
...
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("package_info")
...
def package_info(self):
if self.options.shared:
self.cpp_info.libs = ["hello-shared"]
else:
self.cpp_info.libs = ["hello-static"]
现在,让我们使用 shared=False
(这是默认值,因此无需显式设置)创建 Conan 包,并检查我们是否正在打包正确的库(*libhello-static.a* 或 *hello-static.lib*),并且我们正在链接 *test_package* 中的正确库。
$ conan create . --build=missing
...
-- Install configuration: "Release"
-- Installing: /Users/user/.conan2/p/tmp/a311fcf8a63f3206/p/lib/libhello-static.a
-- Installing: /Users/user/.conan2/p/tmp/a311fcf8a63f3206/p/include/hello.h
hello/1.0 package(): Packaged 1 '.h' file: hello.h
hello/1.0 package(): Packaged 1 '.a' file: libhello-static.a
hello/1.0: Package 'fd7c4113dad406f7d8211b3470c16627b54ff3af' created
...
-- Build files have been written to: /Users/user/.conan2/p/tmp/a311fcf8a63f3206/b/build/Release
hello/1.0: CMake command: cmake --build "/Users/user/.conan2/p/tmp/a311fcf8a63f3206/b/build/Release" -- -j16
hello/1.0: RUN: cmake --build "/Users/user/.conan2/p/tmp/a311fcf8a63f3206/b/build/Release" -- -j16
[ 25%] Building CXX object CMakeFiles/hello.dir/src/hello.cpp.o
[ 50%] Linking CXX static library libhello-static.a
[ 50%] Built target hello
[ 75%] Building CXX object tests/CMakeFiles/test_hello.dir/test.cpp.o
[100%] Linking CXX executable test_hello
[100%] Built target test_hello
hello/1.0: RUN: tests/test_hello
...
[ 50%] Building CXX object CMakeFiles/example.dir/src/example.cpp.o
[100%] Linking CXX executable example
[100%] Built target example
-------- 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! (with color!)
如您所见,库的测试和 Conan *test_package* 都成功地链接到 *libhello-static.a* 库。
属性模型:为特定生成器设置信息¶
CppInfo 对象提供了 set_property
方法来设置特定于每个生成器的信息。 例如,在本教程中,我们使用 CMakeDeps 生成器来生成 CMake 构建需要我们的库的项目所需的信息。 默认情况下,CMakeDeps
将使用与 Conan 包相同的名称为库设置目标名称。 如果您查看 *test_package* 中的 *CMakeLists.txt*
cmake_minimum_required(VERSION 3.15)
project(PackageTest CXX)
find_package(hello CONFIG REQUIRED)
add_executable(example src/example.cpp)
target_link_libraries(example hello::hello)
您可以看到我们正在使用目标名称 hello::hello
进行链接。 Conan 默认设置此目标名称,但我们可以使用*属性模型*来更改它。 让我们尝试将其更改为名称 hello::myhello
。 为此,我们必须在我们的 *hello/1.0* Conan 包的 package_info
方法中设置属性 cmake_target_name
class helloRecipe(ConanFile):
...
def package_info(self):
if self.options.shared:
self.cpp_info.libs = ["hello-shared"]
else:
self.cpp_info.libs = ["hello-static"]
self.cpp_info.set_property("cmake_target_name", "hello::myhello")
然后,将我们在 *test_package* 文件夹的 *CMakeLists.txt* 中使用的目标名称更改为 hello::myhello
cmake_minimum_required(VERSION 3.15)
project(PackageTest CXX)
# ...
target_link_libraries(example hello::myhello)
并重新创建包
$ conan create . --build=missing
Exporting the recipe
hello/1.0: Exporting package recipe
hello/1.0: Using the exported files summary hash as the recipe revision: 44d78a68b16b25c5e6d7e8884b8f58b8
hello/1.0: A new conanfile.py version was exported
hello/1.0: Folder: /Users/user/.conan2/p/a8cb81b31dc10d96/e
hello/1.0: Exported revision: 44d78a68b16b25c5e6d7e8884b8f58b8
...
-------- Testing the package: Building --------
hello/1.0 (test package): Calling build()
...
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Conan: Target declared 'hello::myhello'
...
[100%] Linking CXX executable example
[100%] Built target example
-------- 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! (with color!)
您可以看到 Conan 现在如何声明目标 hello::myhello
而不是默认的 hello::hello
,并且 *test_package* 成功构建。
目标名称不是您可以在 CMakeDeps
生成器中设置的唯一属性。 有关影响 CMakeDeps
生成器行为的完整属性列表,请查看参考。
将环境或配置信息传播给消费者¶
您可以在 package_info()
中为消费者提供环境信息。 为此,您可以使用 ConanFile 的 runenv_info 和 buildenv_info 属性
runenv_info
Environment 对象,用于定义使用该包的消费者在**运行时**可能需要的环境信息。buildenv_info
Environment 对象,用于定义使用该包的消费者在**构建时**可能需要的环境信息。
请注意,没有必要将 cpp_info.bindirs
添加到 PATH
或将 cpp_info.libdirs
添加到 LD_LIBRARY_PATH
,VirtualBuildEnv 和 VirtualRunEnv 会自动添加这些内容。
你也可以在 package_info()
中定义配置值,以便消费者可以使用这些信息。 为此,请设置 ConanFile
的 conf_info 属性。
要了解有关此用例的更多信息,请查看相应的示例。
为提供多个库的 Conan 包定义组件¶
在某些情况下,一个 Conan 包可能提供多个库,对于这些情况,你可以使用 CppInfo 对象的 components 属性为每个库设置单独的信息。
要了解更多关于此用例的信息,请查看示例部分中的components 示例。