package_info()¶
`package_info()` 方法负责定义提供给包的使用者的信息,以便使用者可以轻松、自动地使用此包。使用者的 `generate()` 方法是将 `package_info()` 中定义的信息映射到使用者特定构建系统的地方。因此,如果我们希望一个包被不同的构建系统使用(例如 ConanCenter 社区的 recipe),那么完整的信息至关重要。
重要
此方法专门为包的**使用者**定义信息,而不是为自身定义信息。此方法在二进制文件构建和打包后执行。构建中使用的信息应在 `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 frameworks(包含或不包含在包中)的有序列表。默认为空。
**objects**:使用者应链接的预编译对象 (.obj, .o) 的有序列表,这些对象包含在包中。默认为空。
目录
**includedirs**:可以在其中找到头文件的目录的相对路径列表(从包根目录开始)。默认情况下,它初始化为 `['include']`,并且很少更改。
**libdirs**:在其中查找库对象二进制文件 (*.lib, *.a, *.so, *.dylib) 的目录的相对路径列表(从包根目录开始)。默认情况下,它初始化为 `['lib']`,并且很少更改。
**bindirs**:在其中查找库运行时二进制文件(例如可执行的 Windows .dlls)的目录的相对路径列表(从包根目录开始)。默认情况下,它初始化为 `['bin']`,并且很少更改。
**resdirs**:在其中查找资源文件(图像、xml 等)的目录的相对路径列表(从包根目录开始)。默认情况下为空。
**srcdirs**:在其中查找源文件(例如 .c、.cpp)的目录的相对路径列表(从包根目录开始)。默认情况下为空。它可能用于存储源文件(用于以后调试包,或在其他包中构建时重用这些源文件)。
**builddirs**:可以包含使用者可能使用的构建脚本的目录的相对路径列表(从包根目录开始)。默认为空。
**frameworkdirs**:包含 OSX frameworks 的目录的相对路径列表(从包根目录开始)。
标志
**defines**:预处理器指令的有序列表。通常,使用者在某些情况下必须指定某种 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 对象都可以声明“properties”,这些属性可以被生成器读取。属性的值可以是任何类型。查看每个生成器参考以查看其上使用的属性。
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.components["crypto"].set_property("cmake_file_name", "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_file_name", "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`,任何包 recipe 都可以同时使用两者。`buildenv_info` 和 `runenv_info` 之间的区别在于,前者在 Conan 从源代码构建某些内容时应用,例如在 `build()` 方法中,而后者将在“主机”上下文中执行需要激活运行时的内容时使用。
Conan `VirtualBuildEnv` 生成器默认将在使用者中使用,从 `buildenv_info`(以及来自“构建”上下文的一些 `runenv_info`)收集信息,以创建 `conanbuild` 环境变量脚本,该脚本默认在所有 `self.run(cmd, env="conanbuild")` 调用中运行。`VirtualRunEnv` 生成器也将默认在使用者中使用,从“主机”上下文收集 `runenv_info`,创建 `conanrun` 环境变量脚本,该脚本可以使用 `self.run(
注意
最佳实践
没有必要将 `bindirs` 添加到 `PATH` 环境变量,使用者 `VirtualBuildEnv` 和 `VirtualRunEnv` 生成器会自动执行此操作。同样,也没有必要将 `includedirs`、`libdirs` 或任何其他 dirs 添加到环境变量,因为此信息通常将由其他生成器管理。
conf_info¶
“构建”上下文中的 `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"))
请注意,这仅从 recipe 的直接 `tool_requires` 传播。
generator_info¶
警告
此功能是实验性的,可能会发生重大更改。有关更多信息,请参阅 Conan 稳定性部分。
“构建”上下文中的 `tool_requires` 可以通过将生成器添加到 `package_info` 方法内的 `generator_info` 列表,将生成器注入到 recipe 中。这对于将自定义生成器注入到 recipe 中非常有用,这些生成器将由包的使用者使用,就像它们在 `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.append(MyGenerator)
请注意,这仅从 recipe 的直接 `tool_requires` 传播。
注意
最佳实践
如果您有其他方式为使用者传播信息,则 `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 提取信息是不可行的。
如果您计划使用 editables 或本地开发流程,则需要检查 `layout()` 并为 `self.cpp.build` 和 `self.cpp.source` 定义信息。
没有必要将 `bindirs` 添加到 `PATH` 环境变量,使用者 `VirtualBuildEnv` 和 `VirtualRunEnv` 生成器会自动执行此操作。
在 `package_info()` 中定义的**路径**不应转换为任何特定格式(例如 Windows 子系统所需的格式)。相反,使用者有责任将这些路径转换为适当的格式。
另请参阅
有关更多信息,请参阅定义包信息教程。