package_id()

Conan 为每个配置计算一个唯一的 package_id 引用,包括 settingsoptionsdependencies 版本。这个 package_id() 方法允许对计算出的 package_id 进行一些自定义和更改,通常是为了放宽全局二进制兼容性假设。

一般规则是,settingsoptions 的每个不同值都会创建一个不同的 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.settingsself.options

如果一个包只是一个 C 库,但它无法在 configure() 方法中删除 compiler.cppstdcompiler.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

请注意,这并不意味着应该为每个应用程序可执行文件删除 compilerbuild_type。 对于其他不是工具,而是最终发布产品的情况,最佳方法通常是为不同的编译器、编译器版本、构建类型等维护不同的构建。 这也意味着我们正在清除一些信息。 我们将不会拥有用于我们正在使用的二进制文件的编译器和构建类型的信息(它不会在 conan list 输出中,也不会在服务器元数据中)。 如果我们使用不同的编译器或构建类型编译一个新的二进制文件,它将在相同的 package_id 下创建一个新的包版本。

部分信息清除

也可以为给定的值子集部分清除信息。 例如,如果我们希望为所有使用 gcc 编译的二进制文件获得相同的 package_id,版本在 4.5 到 5.0 之间,我们可以这样做

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"

这将导致所有其他编译器以及该范围之外的其他版本具有不同的 package_id,但对于所有 gcc 4.5-5.0 版本,将只有 1 个 package_id 二进制文件。 这也具有上述关于丢失创建此二进制文件信息的缺点。

通常不建议采用这种方法,最好使用全局 compatibility 插件或配方 compatibility() 方法来解决。

注意

不仅可以清除 settings,还可以清除其他类型的信息,例如 optionsconf 项目。

添加信息

有些信息默认情况下不会添加到 package_id 中。 如果我们正在为用作 tool_require 的工具创建一个包,并且恰好该包二进制文件对于每个“目标”配置都不同,例如对于某些交叉编译器,如果编译器本身可能不同于它所针对的不同架构,那么有必要使用以下方法将 settings_target 添加到 package_id

def package_id(self):
    self.info.settings_target = self.settings_target

默认情况下,conf 项目不会影响 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”开头的配置。

另请参阅