自定义二进制兼容性

默认的二进制兼容性要求设置和选项几乎完全匹配,并且依赖项版本进行版本匹配,如关于依赖项的上一节所述。

总之,安装依赖项时所需的二进制文件package_id默认应匹配

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

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

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

  • 依赖项的版本应匹配

    • 在“嵌入式依赖项”的情况下,应匹配精确版本,包括配方修订版和依赖项package_idpackage_revision从不包含在内,因为它被认为是同一package_id有多个package_revision是不规范的。

    • 在“非嵌入式依赖项”的情况下,依赖项的版本应匹配到次要版本,而不考虑补丁recipe_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()中的信息擦除意味着一个package_id将代表一系列不同的设置,但用于创建二进制文件的确切设置信息将丢失,并且该范围只能创建一个二进制文件。使用该范围内的不同设置重新创建包将创建一个覆盖以前二进制文件的新二进制文件(具有新的包修订版)。

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

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'

这些配置会影响包 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是可以在配方中定义的类属性,用于定义当它们被用作requires时,它们对其使用者package_id的影响。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特性定义其依赖项如何影响其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特性不区分“嵌入式”和“非嵌入式”情况,由用户定义正确的值。这种方法可能只应用于没有通过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