自定义二进制兼容性¶
默认的二进制兼容性要求设置和选项几乎完全匹配,以及依赖项版本的版本化匹配,如前面关于依赖项的部分所述。
总而言之,安装依赖项时所需的二进制文件 package_id
默认情况下应该匹配
package_id
中的所有设置(除了compiler.cppstd
)都应该与输入配置文件中提供的设置完全匹配,包括编译器版本。因此compiler.version=9
与compiler.version=9.1
不同。默认行为将假设 C++ 包的不同
compiler.cppstd
值之间具有二进制兼容性,如果输入配置文件所需的cppstd
不存在,则能够回退到其他值。这由compatibility.py
插件控制,用户可以自定义该插件。package_id
中的所有选项都应与输入配置文件中提供的选项完全匹配。依赖项的版本应匹配
如果是“嵌入依赖项”,则应匹配确切的版本,包括 recipe-revision 和依赖项
package_id
。package_revision
从不包含在内,因为它被认为是为同一个package_id
拥有多个package_revision
是不规范的。如果是“非嵌入依赖项”,则依赖项的版本应匹配到
minor
版本,其中patch
、recipe_revision
和其他信息不考虑在内。如果是“工具依赖项”,则依赖项的版本默认情况下根本不会影响消费者的
package_id
。
这些规则可以使用不同的方法进行自定义和更改,具体取决于需求,如下几节所述
自定义设置和选项的二进制兼容性¶
package_id() 方法中的信息擦除¶
Recipe 可以使用其 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()
中的信息擦除意味着 1 个 package_id
将代表整个不同设置范围,但用于创建二进制的确切设置的信息将丢失,并且只能为该范围创建一个二进制文件。使用该范围内的不同设置重新创建包,将创建一个新的二进制文件,该文件会覆盖之前的二进制文件(使用新的 package-revision)。
如果我们希望能够为不同的输入设置创建、存储和管理不同的二进制文件,则不能使用信息擦除,建议使用下面的 compatibility
方法。
compatibility() 方法¶
Recipe 可以使用其 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()
方法对一个 recipe 的操作方式相同,但适用于全局的所有包。
查看二进制兼容性 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'
注意
最佳实践
强烈建议 core.package_id:default_xxx
在组织中应该是全局的、一致的且不可变的。为不同的项目或团队更改这些默认设置可能会造成混淆,因为它会导致二进制文件缺失。
如果生成的包在组织外部共享,则它也应与这些包的消费者保持一致和共享,在这种情况下,建议通过 conan config install
共享 global.conf
文件。
请考虑使用 Conan 默认值,它们应该在效率和安全性之间取得良好的平衡,确保为嵌入情况进行精确重建,并通过版本为非嵌入情况提供良好的控制。
为 recipe 消费者自定义 package_id 模式¶
Recipe 可以通过一些 package_id_xxxx_mode
属性定义其对其消费者的默认影响。
package_id_embed_mode, package_id_non_embed_mode, package_id_unknown_mode
是可以在 recipe 中定义的类属性,用于定义它们对其消费者 package_id
的影响。可以声明为
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"
从 recipe 依赖项自定义 package_id¶
Recipe 可以使用 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中定义。