package_id()¶
Conan 为每个配置(包括 `settings`(设置)、`options`(选项)和 `dependencies`(依赖)版本)计算唯一的 `package_id` 引用。此 `package_id()` 方法允许对计算出的 `package_id` 进行一些自定义和更改,通常旨在放宽一些全局二进制兼容性假设。
一般规则是,`settings` 和 `options` 的每个不同值都会创建一个不同的 `package_id`。此规则可以根据不同的方法进行放宽或扩展。
给定包的配方可以在其 `package_id()` 中决定最终二进制文件独立于某些设置,例如,如果它是一个仅头文件库,使用输入设置来构建一些测试,它可能会完全清除所有配置,这样无论输入如何,生成的 `package_id` 始终相同。同样,一个 C 库可能希望从其二进制 `package_id` 中移除 `compiler.cppstd` 和/或 `compiler.libcxx` 的影响,因为作为一个 C 库,其二进制文件将是独立的。
给定包的配方可以实现部分信息擦除,例如,为一系列编译器版本获得相同的 `package_id`。这种类型的二进制兼容性通常通过全局 `compatibility` 插件,或者如果全局插件不足,则通过 `compatibility()` 方法更好地解决。
包配方可以决定在其计算出的 `package_id` 中注入额外的可变性,添加 `conf` 项或“目标”设置。
可用的自动实现¶
警告
此功能是实验性的,可能会发生重大更改。有关更多信息,请参阅 Conan 稳定性 部分。
当未定义 `package_id()` 方法时,可以在 implements ConanFile 属性中指定以下自动实现。
auto_header_only¶
当配方声明选项 `header_only=True` 或 `package_type` 为 `"header-library"` 时,Conan 将自动管理包 ID 清除设置和选项。可以像这样添加到配方中:
from conan import ConanFile
class Pkg(ConanFile):
implements = ["auto_header_only"]
...
接着,如果在配方中没有指定 `package_id()` 方法,Conan 将自动管理它,并在 `package_id()` 中自动调用 `self.info.clear()`,使 `package_id` 独立于设置、选项、配置和需求。
如果你需要在配方中实现自定义行为,但同时又需要此逻辑,则必须明确声明,例如,像这样:
def package_id(self):
if self.package_type == "header-library":
self.info.clear()
else:
self.info.settings.rm_safe("compiler.libcxx")
self.info.settings.rm_safe("compiler.cppstd")
信息擦除¶
这是一种 `package_id` 放宽策略。让我们来看第一个案例:一个仅头文件库,它有输入 `settings`(设置),因为它仍希望在 `build()` 方法中使用它们进行一些单元测试。为了在所有配置中只有一个最终二进制文件(因为最终的构件在所有情况下都应相同,即仅头文件),需要执行以下操作:
settings = "os", "compiler", "arch", "build_type"
def build(self):
cmake = CMake(self) # need specific settings to build
...
cmake.test() # running unit tests for the current configuration
def package_id(self):
# Completely clear all the settings from the ``package_id`` information ("info" object)
# All resulting ``package_id`` will be the same, irrespective of configuration
self.info.settings.clear()
警告
信息的修改始终发生在 `self.info` 对象上,而不是 `self.settings` 或 `self.options` 上。
如果一个包只是一个 C 库,但在 `configure()` 方法中无法移除 `compiler.cppstd` 和 `compiler.libcxx`(对于大多数情况,这是推荐的方法,以保证这些标志不会用于构建),因为有针对 C 库的 C++ 单元测试,那么由于测试未打包且最终二进制文件将独立于 C++,因此可以使用以下方法移除它们:
settings = "os", "compiler", "arch", "build_type"
def build(self):
# building C++ tests for a C library
def package_id(self):
del self.info.settings.compiler.cppstd
# Some compilers might not declare libcxx subsetting
self.info.settings.rm_safe("compiler.libcxx")
如果一个包正在构建一个可执行文件作为工具使用,并且为了更高效,希望每个操作系统和架构只有一个可执行文件,那么 `package_id()` 可以移除其他现有设置和选项。
# this will be a "tool_require"
package_type = "application"
settings = "os", "compiler", "arch", "build_type"
def package_id(self):
del self.info.settings.compiler
del self.info.settings.build_type
请注意,这并不意味着应该为每个应用程序可执行文件移除 `compiler`(编译器)和 `build_type`(构建类型)。对于非工具,而是最终发布产品的情况,最常见的方法是为不同的编译器、编译器版本、构建类型等维护不同的构建版本。这也意味着我们正在擦除一些信息。我们将无法获取所用二进制文件的编译器和构建类型信息(它不会出现在 `conan list` 输出中,也不会出现在服务器元数据中)。如果我们使用不同的编译器或构建类型编译新的二进制文件,它将在相同的 `package_id` 下创建一个新的包修订版。
部分信息擦除¶
还可以部分擦除给定值子集的信息。例如,如果希望所有使用 `gcc` 4.5 到 5.0 版本编译的二进制文件具有相同的 `package_id`,可以这样做:
def package_id(self):
v = Version(str(self.info.settings.compiler.version))
if self.info.settings.compiler == "gcc" and (v >= "4.5" and v < "5.0"):
# The assigned string can be arbitrary
self.info.settings.compiler.version = "GCC 4 between 4.5 and 5.0"
这将导致除 `gcc` 之外的其他编译器以及该范围之外的其他版本具有不同的 `package_id`,但所有 `gcc` `4.5-5.0` 版本将只有一个 `package_id` 二进制文件。这也存在上面提到的缺点,即会丢失创建此二进制文件的信息。
这种方法在一般情况下不推荐,最好使用全局 `compatibility` 插件或配方 `compatibility()` 方法来解决。
注意
不仅可以擦除 `settings`(设置),还可以擦除其他类型的信息,如 `options`(选项)和 `conf` 项。
添加信息¶
默认情况下,有一些信息不会添加到 `package_id` 中。如果正在为用作 `tool_require` 的工具创建包,并且该包的二进制文件对于每个“目标”配置都不同(例如某些交叉编译器的情况,如果编译器本身可能因其目标的不同架构而异),则需要将 `settings_target` 添加到 `package_id` 中,如下所示:
def package_id(self):
self.info.settings_target = self.settings_target
`conf` 项默认不影响 `package_id`。可以在配方级别明确地将它们作为 `package_id` 的一部分,如下所示:
def package_id(self):
self.info.conf.define("user.myconf:myitem", self.conf.get("user.myconf:myitem"))
尽管这可以通过不使用 `package_id()` 方法为所有配方实现,但需使用 `tools.info.package_id:confs = ["user.myconf:myitem"]` 配置。
使用正则表达式模式: 你可以在 `tools.info.package_id:confs` 中使用正则表达式模式。这意味着你可以使用正则表达式模式来匹配多个配置,而无需指定每个单独的配置项。这在处理大量配置或配置遵循可预测命名模式时特别有用。例如:
`tools.info.package_id:confs=[".*"]` 匹配所有配置。
`tools.info.package_id:confs=["tools\..*"]` 匹配以“tools.”开头的配置。
`tools.info.package_id:confs=["(tools\.deploy|core)"]` 匹配以“tools.deploy”或“core”开头的配置。
另请参阅
有关 `package_id()` 方法的解释,请参阅 关于仅头文件包的教程。
有关 Conan 二进制模型的完整视图,请阅读 二进制模型参考。