自定义二进制兼容性¶
默认的二进制兼容性要求设置和选项几乎完全匹配,并且依赖项版本经过版本化匹配,如前一节关于依赖项中所述。
总而言之,默认情况下,安装依赖项时所需的二进制文件 package_id 应该匹配
除了
compiler.cppstd之外,package_id中的所有设置都应该与输入配置文件的设置完全匹配,包括编译器版本。因此,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() 中擦除信息意味着 1 个 package_id 将代表整个设置范围,但将丢失用于创建二进制文件的确切设置的信息,并且只能为该范围创建 1 个二进制文件。使用该范围内的不同设置重新创建包,将创建一个覆盖先前二进制文件的新二进制文件(具有新的 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 是可以在配方中定义的类属性,以定义它们作为 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 中找到可以使用的不同模式