自定义二进制兼容性¶
默认的二进制兼容性要求设置和选项几乎完全匹配,并且依赖项版本必须进行版本匹配,如关于依赖项的上一节所述。
总之,安装依赖项时所需的二进制 package_id
默认应匹配:
package_id
中的所有设置(除了compiler.cppstd
)都应该与输入配置文件中提供的设置完全匹配,包括编译器版本。因此,compiler.version=9
不同于compiler.version=9.1
。默认行为将假定 C++ 包的不同
compiler.cppstd
值之间存在二进制兼容性,能够在输入配置文件中指定的cppstd
不存在时回退到其他值。这由compatibility.py
插件控制,用户可以自定义。package_id
中的所有选项都应该与输入配置文件中提供的选项完全匹配。依赖项的版本应匹配:
对于“嵌入式依赖项”,应匹配精确版本,包括菜谱修订版和依赖项
package_id
。package_revision
从不包含在内,因为它被认为是同一个package_id
有多个package_revision
是格式错误的。对于“非嵌入式依赖项”,依赖项的版本应匹配到
minor
版本,而patch
、recipe_revision
及其他信息则不予考虑。对于“工具依赖项”,依赖项的版本默认对消费者
package_id
完全没有影响。
根据需要,可以使用不同的方法来自定义和更改这些规则,如下文所述:
自定义设置和选项的二进制兼容性¶
在 package_id() 方法中擦除信息¶
菜谱可以使用其 package_id()
方法从它们的 package_id
中擦除信息。例如,一个只包含可执行文件的包可以决定从其 package_id
中移除 settings.compiler
和 settings.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() 方法¶
菜谱可以使用其 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。