package_info()¶
package_info()
方法负责定义软件包信息给其消费者,以便这些消费者能够轻松且自动地使用该软件包。消费者的 generate()
方法会将 package_info()
中定义的信息映射到消费者特定的构建系统。因此,如果一个软件包需要被不同的构建系统使用(例如 ConanCenter 社区配方),确保这些信息完整就非常重要。
重要
此方法仅为该软件包的消费者定义信息,而不是为软件包本身。此方法在二进制文件构建和打包后执行。构建时需要使用的信息应该在 generate()
方法中处理。
cpp_info: 库和构建信息¶
每个软件包都必须为其消费者指定某些构建信息。这可以通过 cpp_info
属性来完成。
# Binaries to link
self.cpp_info.libs = [] # The libs to link against
self.cpp_info.system_libs = [] # System libs to link against
self.cpp_info.frameworks = [] # OSX frameworks that consumers will link against
self.cpp_info.objects = [] # precompiled objects like .obj .o that consumers will link
# Directories
self.cpp_info.includedirs = ['include'] # Ordered list of include paths
self.cpp_info.libdirs = ['lib'] # Directories where libraries can be found
self.cpp_info.bindirs = ['bin'] # Directories where executables and shared libs can be found
self.cpp_info.resdirs = [] # Directories where resources, data, etc. can be found
self.cpp_info.srcdirs = [] # Directories where sources can be found (debugging, reusing sources)
self.cpp_info.builddirs = [] # Directories where build scripts for consumers can be found
self.cpp_info.frameworkdirs = [] # Directories where OSX frameworks can be found
# Flags
self.cpp_info.defines = [] # preprocessor definitions
self.cpp_info.cflags = [] # pure C flags
self.cpp_info.cxxflags = [] # C++ compilation flags
self.cpp_info.sharedlinkflags = [] # linker flags
self.cpp_info.exelinkflags = [] # linker flags
# Properties
self.cpp_info.set_property("property_name", "property_value")
# Structure
self.cpp_info.components # Dictionary-like structure to define the different components a package may have
self.cpp_info.requires # List of components from requirements that need to be propagated downstream
要链接的二进制文件
libs: 消费者应该链接的已编译库(包含在软件包中)的有序列表。默认为空。
system_libs: 消费者应该链接的系统库(不包含在软件包中)的有序列表。默认为空。
frameworks: 消费者应该链接的 OSX 框架(包含或不包含在软件包中)的有序列表。默认为空。
objects: 消费者应该链接的预编译对象(.obj, .o,包含在软件包中)的有序列表。默认为空。
目录
includedirs: 头文件所在的目录的相对路径列表(从软件包根目录开始)。默认初始化为
['include']
,很少更改。libdirs: 查找库对象二进制文件(*.lib, *.a, *.so, *.dylib)所在目录的相对路径列表(从软件包根目录开始)。默认初始化为
['lib']
,很少更改。bindirs: 查找库运行时二进制文件(如 Windows 可执行 .dll 文件)所在目录的相对路径列表(从软件包根目录开始)。默认初始化为
['bin']
,很少更改。resdirs: 查找资源文件(图片、xml 等)所在目录的相对路径列表(从软件包根目录开始)。默认为空。
srcdirs: 查找源文件(如 .c, .cpp)所在目录的相对路径列表(从软件包根目录开始)。默认为空。可以用于存储源代码(用于调试软件包或在其他软件包中重用这些源代码)。
builddirs: 包含可供消费者使用的构建脚本的目录的相对路径列表(从软件包根目录开始)。默认为空。
frameworkdirs: 包含 OSX 框架的目录的相对路径列表(从软件包根目录开始)。
标志
defines: 预处理器指令的有序列表。在某些情况下,消费者通常需要指定一些定义,以便包含库头文件与二进制文件匹配。
cflags, cxxflags, sharedlinkflags, exelinkflags: 消费者为确保正确行为而应激活的标志列表。很少使用。
属性
set_property()
允许定义一些内置的和用户自定义的通用属性,这些属性会随着cpp_info
模型传播给消费者。它们可能包含特定于构建系统的. 一些内置属性包括cmake_file_name
、cmake_target_name
、pkg_config_name
,它们可以为CMakeDeps
或PkgConfigDeps
生成器定义特定行为。有关更多信息,请阅读特定构建系统集成文档。
结构
components: 一个字典,键为名称,值为组件对象,用于描述软件包可能拥有的不同组件:库、可执行文件等。
requires: 实验性功能 软件包(及其消费者)应该链接的组件的需求列表。它将由支持组件功能的生成器使用。
不同的配置通常会产生不同的 package_info
,例如,库名称在不同操作系统上可能不同,或者根据编译器和操作系统使用不同的 system_libs
。
settings = "os", "compiler", "arch", "build_type"
options = {"shared": [True, False]}
def package_info(self):
if not self.settings.os == "Windows":
self.cpp_info.libs = ["zmq-static"] if not self.options.shared else ["zmq"]
else:
...
if not self.options.shared:
self.cpp_info.defines = ["ZMQ_STATIC"]
if self.settings.os == "Windows" and self.settings.compiler == "msvc":
self.cpp_info.system_libs.append("ws2_32")
属性¶
任何 CppInfo 对象都可以声明“属性”,这些属性可以被生成器读取。属性的值可以是任何类型。请查看每个生成器的参考文档以了解其使用的属性。
def set_property(self, property_name, value)
def get_property(self, property_name, check_type=None):
示例
def package_info(self):
self.cpp_info.set_property("cmake_find_mode", "both")
self.cpp_info.get_property("cmake_find_mode", check_type=str)
组件¶
如果您的软件包由多个库组成,您可以声明组件。这允许为每个库定义一个 CppInfo
对象,还可以定义它们之间的依赖关系以及与其他软件包组件的依赖关系(以下情况并非真实示例)。
def package_info(self):
self.cpp_info.set_property("cmake_file_name", "OpenSSL")
self.cpp_info.components["crypto"].set_property("cmake_target_name", "OpenSSL::Crypto")
self.cpp_info.components["crypto"].libs = ["libcrypto"]
self.cpp_info.components["crypto"].defines = ["DEFINE_CRYPTO=1"]
self.cpp_info.components["crypto"].requires = ["zlib::zlib"] # Depends on all components in zlib package
self.cpp_info.components["ssl"].set_property("cmake_target_name", "OpenSSL::SSL")
self.cpp_info.components["ssl"].includedirs = ["include/headers_ssl"]
self.cpp_info.components["ssl"].libs = ["libssl"]
self.cpp_info.components["ssl"].requires = ["crypto",
"boost::headers"] # Depends on headers component in boost package
obj_ext = "obj" if platform.system() == "Windows" else "o"
self.cpp_info.components["ssl-objs"].objects = [os.path.join("lib", "ssl-object.{}".format(obj_ext))]
组件之间的依赖关系以及与其他需求组件的依赖关系可以使用 requires
属性和组件名称来定义。将计算组件的依赖关系图,并按正确的顺序为每个字段聚合值。
buildenv_info, runenv_info¶
buildenv_info
和 runenv_info
属性是 Environment
对象,允许以环境变量的形式为消费者定义信息。它们可以使用 Environment
的任何方法来定义这些信息。
settings = "os", "compiler", "arch", "build_type"
def package_info(self):
self.buildenv_info.define("MYVAR", "1")
self.buildenv_info.prepend_path("MYPATH", "my/path")
if self.settings.os == "Android":
arch = "myarmarch" if self.settings.arch=="armv8" else "otherarch"
self.buildenv_info.append("MY_ANDROID_ARCH", f"android-{arch})
self.runenv_info.append_path("MYRUNPATH", "my/run/path")
if self.settings.os == "Windows":
self.runenv_info.define_path("MYPKGHOME", "my/home")
请注意,这些对象既不与常规的 requires
绑定,也不与 tool_requires
绑定,任何软件包配方都可以使用它们。buildenv_info
和 runenv_info
之间的区别在于,前者在 Conan 从源码构建某些内容时应用,例如在 build()
方法中;后者则在“host”上下文中执行需要激活运行时环境的操作时使用。
Conan 的 VirtualBuildEnv
生成器将默认在消费者中使用,它会收集 buildenv_info
(以及来自“build”上下文的一些 runenv_info
)信息,以创建 conanbuild
环境脚本。该脚本默认在所有 self.run(cmd, env="conanbuild")
调用中运行。VirtualRunEnv
生成器也将默认在消费者中使用,它会收集“host”上下文的 runenv_info
信息,创建 conanrun
环境脚本,该脚本可以通过 self.run(<cmd>, env="conanrun")
显式使用。
注意
最佳实践
无需将 bindirs
添加到 PATH
环境变量中,这会自动由消费者 VirtualBuildEnv
和 VirtualRunEnv
生成器完成。同样,也不需要将 includedirs
、libdirs
或其他目录添加到环境变量中,因为这些信息通常由其他生成器管理。
conf_info¶
“build”上下文中的 tool_requires
包可以通过 conf_info
属性将其一些 conf
配置传递给其直接消费者。例如,打包 AndroidNDK 的 Conan 包可以这样做:
def package_info(self):
self.conf_info.define_path("tools.android:ndk_path", "path/to/ndk/in/package")
来自软件包的 conf_info
仍然可以被 profile 值覆盖,因为用户 profile 具有更高的优先级。
- Conf.define(name, value)¶
定义给定配置名称的值。
- 参数:
name – 配置的名称。
value – 配置的值。
def package_info(self): # Setting values self.conf_info.define("tools.build:verbosity", "verbose") self.conf_info.define("tools.system.package_manager:sudo", True) self.conf_info.define("tools.microsoft.msbuild:max_cpu_count", 2) self.conf_info.define("user.myconf.build:ldflags", ["--flag1", "--flag2"]) self.conf_info.define("tools.microsoft.msbuildtoolchain:compile_options", {"ExceptionHandling": "Async"})
- Conf.append(name, value)¶
将值追加到给定的配置名称。
- 参数:
name – 配置的名称。
value – 要追加的值。
def package_info(self): # Modifying configuration list-like values self.conf_info.append("user.myconf.build:ldflags", "--flag3") # == ["--flag1", "--flag2", "--flag3"]
- Conf.prepend(name, value)¶
将值前置到给定的配置名称。
- 参数:
name – 配置的名称。
value – 要前置的值。
def package_info(self): self.conf_info.prepend("user.myconf.build:ldflags", "--flag0") # == ["--flag0", "--flag1", "--flag2", "--flag3"]
- Conf.update(name, value)¶
更新给定配置名称的值。
- 参数:
name – 配置的名称。
value – 配置的值。
def package_info(self): # Modifying configuration dict-like values self.conf_info.update("tools.microsoft.msbuildtoolchain:compile_options", {"ExpandAttributedSource": "false"})
- Conf.remove(name, value)¶
从给定的配置名称中移除一个值。
- 参数:
name – 配置的名称。
value – 要移除的值。
def package_info(self): # Remove self.conf_info.remove("user.myconf.build:ldflags", "--flag1") # == ["--flag0", "--flag2", "--flag3"]
- Conf.unset(name)¶
清除变量,相当于 unset 或 set XXX=。
- 参数:
name – 配置的名称。
def package_info(self): # Unset any value self.conf_info.unset("tools.microsoft.msbuildtoolchain:compile_options")
可以在是 tool_requires
的软件包中定义配置。例如,假设有一个打包 AndroidNDK 的软件包,它可以将其 NDK 位置定义到 tools.android:ndk_path
配置中,如下所示:
import os
from conan import ConanFile
class Pkg(ConanFile):
name = "android_ndk"
def package_info(self):
self.conf_info.define("tools.android:ndk_path", os.path.join(self.package_folder, "ndk"))
请注意,这仅从配方的直接、首个 tool_requires
传播。
generator_info¶
警告
此功能是实验性的,可能会发生重大更改。有关更多信息,请参阅 Conan 稳定性 部分。
“build”上下文中的 tool_requires
可以通过在 package_info
方法中的 generator_info
属性中添加生成器来注入到配方中。这对于将自定义生成器注入到配方中非常有用,这些生成器将像在消费者的 generators
属性中声明一样被使用。
class MyGenerator:
def __init__(self, conanfile):
self._conanfile = conanfile
def generate(self):
self.output.info(f"Calling custom generator for {conanfile}")
def package_info(self):
self.generator_info = [MyGenerator]
请注意,这仅从配方的直接、首个 tool_requires
传播,并且默认情况下 self.generator_info
为 None
。
注意
最佳实践
如果存在其他传播消费者信息的方式,则
package_info()
方法并非严格必需。例如,如果您的软件包在构建时创建了xxx-config.cmake
文件,并且这些文件被放入最终软件包中,那么可能完全不需要定义package_info()
,在消费者端也就不需要CMakeDeps
,因为CMakeToolchain
能够注入路径来定位软件包中的xxx-config.cmake
文件。这种方法对于 Conan 的私有使用可能很好,但存在 CMake 的一些限制,例如无法管理多配置项目(如 Visual Studio 在 IDE 中切换 Debug/Release,而CMakeDeps
可以提供此功能),以及在某些交叉编译场景中使用既是库又是构建工具的软件包(如protobuf
,CMakeDeps
也可以处理)。如果计划使用不同构建系统(如 ConanCenter),提供
package_info()
就非常必要了。在这种情况下,需要一点重复,并且编写package_info()
可能会感觉像是复制了软件包的xxx-config.cmake
,但目前自动从 CMake 提取信息是不可行的。如果您计划使用可编辑模式或本地开发流程,则需要检查
layout()
并为self.cpp.build
和self.cpp.source
定义信息。无需将
bindirs
添加到PATH
环境变量中,这会自动由消费者VirtualBuildEnv
和VirtualRunEnv
生成器完成。在
package_info()
中定义的路径不应转换为任何特定格式(例如 Windows 子系统所需的格式)。相反,由消费者负责将这些路径转换为合适的格式。
另请参阅
有关更多信息,请参阅 定义包信息的教程。