自定义二进制兼容性

默认的二进制兼容性要求设置和选项几乎完全匹配,并且依赖项版本需要进行版本匹配,正如在上一节关于依赖项的说明中所解释的那样。

总而言之,安装依赖项时所需的二进制 package_id 默认情况下应匹配

  • 除了 compiler.cppstd 之外,package_id 中的所有设置都应与输入配置文件中提供的设置完全匹配,包括编译器版本。因此 compiler.version=9compiler.version=9.1 不同。

  • 默认行为将假定 C++ 包在不同的 compiler.cppstd 值之间存在二进制兼容性,如果输入配置文件所需的 cppstd 不存在,则可以回退到除输入配置文件中指定的值之外的其他值。这由 compatibility.py 插件控制,用户可以对其进行自定义。

  • package_id 中的所有选项都应与输入配置文件中提供的选项完全匹配。

  • 依赖项的版本应匹配

    • 对于“嵌入式依赖项”,应匹配确切的版本,包括配方修订版本和依赖项 package_idpackage_revision 永远不会包含在内,因为假定对于相同的 package_id 有多个 package_revision 是不合理的。

    • 对于“非嵌入式依赖项”,依赖项版本应匹配到 minor 版本,而 patchrecipe_revision 和更进一步的信息则不予考虑。

    • 对于“工具依赖项”,依赖项的版本默认情况下完全不影响消费者 package_id

这些规则可以通过不同的方法进行自定义和更改,具体取决于需求,如下节所述。

自定义设置和选项的二进制兼容性

在 package_id() 方法中擦除信息

配方可以通过其 package_id() 方法从其 package_id擦除信息。例如,一个只包含可执行文件的包可以决定从其 package_id 中删除 settings.compilersettings.build_type 的信息,假设用任何编译器构建的可执行文件都是有效的,并且没有必要存储用不同编译器构建的不同二进制文件。

def package_id(self):
    del self.info.settings.compiler
    del self.info.settings.build_type

也可以为给定的设置分配一个值,例如,如果我们希望在 [>=5 <7>] 范围内的所有 gcc 版本有一个单一的二进制文件,我们可以这样做。

def package_id(self):
    if self.info.settings.compiler == "gcc":
        version = Version(self.info.settings.compiler.version)
        if version >= "5.0" and version < "7.0":
            self.info.settings.compiler.version = "gcc5-6"

注意

最佳实践

请注意,在 package_id() 中擦除信息意味着 1 个 package_id 将代表一整套不同的设置,但创建二进制文件时使用的确切设置信息将丢失,并且该范围只能创建一个二进制文件。使用范围内的不同设置重新创建包将创建一个新二进制文件,覆盖之前的二进制文件(带有新的 package-revision)。

如果我们希望能够为不同的输入设置创建、存储和管理不同的二进制文件,则不能使用信息擦除,并建议使用以下 compatibility 方法。

compatibility() 方法

配方可以使用其 compatibility() 方法定义其二进制兼容性规则。例如,如果我们希望用 gcc 版本 4.8、4.7 和 4.6 构建的二进制文件被视为与用 4.9 编译的二进制文件兼容,我们可以声明一个 compatibility() 方法如下:

def compatibility(self):
    if self.settings.compiler == "gcc" and self.settings.compiler.version == "4.9":
        return [{"settings": [("compiler.version", v)]}
                for v in ("4.8", "4.7", "4.6")]

compatibility() 方法参考 中阅读更多关于 compatibility() 方法的信息。

compatibility.py 插件

兼容性可以通过 compatibility.py 插件全局定义,方式与 compatibility() 方法对单个配方所做的一样,但适用于所有包全局。

查看二进制兼容性 compatibility.py 扩展

自定义依赖项版本的二进制兼容性

全局默认 package_id 模式

global.conf 中定义的 core.package_id:default_xxx 配置可用于全局更改依赖项如何影响其消费者的默认行为。

core.package_id:default_build_mode: By default, 'None'
core.package_id:default_embed_mode: By default, 'full_mode'
core.package_id:default_non_embed_mode: By default, 'minor_mode'
core.package_id:default_python_mode: By default, 'minor_mode'
core.package_id:default_unknown_mode: By default, 'semver_mode'

这些配置会影响 package id 的计算方式,因此更改它们会影响您生成的二进制文件。因此,建议它们在您的组织内部保持一致。

注意

最佳实践

强烈建议 core.package_id:default_xxx 在整个组织内应全局、一致且不可变。为不同的项目或团队更改这些默认设置可能会导致混淆,因为这将导致二进制文件丢失。

如果生成的包将在组织外部共享,那么它也应该与这些包的消费者保持一致。在这种情况下,可以通过 conan config install 来共享 global.conf 文件。

考虑使用 Conan 的默认设置,它们应该在效率和安全性之间取得良好的平衡,确保嵌入式用例的精确重建,并在非嵌入式用例中提供良好的版本控制。

为配方消费者自定义 package_id 模式

配方可以通过一些 package_id_xxxx_mode 属性来定义其对消费者的默认影响。

package_id_embed_mode, package_id_non_embed_mode, package_id_unknown_mode 是可以在配方中定义的类属性,用于定义它们对消费者 package_id 的影响,当它们被用作 requires 时。build_mode(实验性)是一个类属性,当这些消费者将它用作 tool_requires 时,会影响包的消费者。可以声明为:

from conan import ConanFile

class Pkg(ConanFile):
    ...
    package_id_embed_mode = "full_mode"
    package_id_non_embed_mode = "patch_mode"
    package_id_unknown_mode = "minor_mode"
    build_mode = "patch_mode"  # when this is used with tool_requires

package_id_{embed,non_embed,python,unknown}_mode, build_mode 中阅读更多信息。

从配方依赖项自定义 package_id

配方可以使用 package_id_mode trait 来定义其依赖项如何影响其 package_id

from conan import ConanFile

class Pkg(ConanFile):
    def requirements(self):
        self.requires("mydep/1.0", package_id_mode="patch_mode")

使用 package_id_mode trait 不区分“嵌入式”和“非嵌入式”情况,由用户自行定义正确的值。这种情况可能只适用于那些通过 options 控制共享/静态库可变性非常特殊的情况。

请注意,在图表展开时会评估 requirements() 方法,依赖项尚不存在(尚未计算),因此无法知道依赖项的选项。在这种情况下,最好使用 package_id() 方法。

package_id() 方法可以定义依赖项如何通过以下方式影响当前包:

from conan import ConanFile

class Pkg(ConanFile):
    def package_id(self):
        self.info.requires["mydep"].major_mode()

可用的不同模式定义在 package_id_{embed,non_embed,python,unknown}_mode, build_mode 中。