定制二进制兼容性¶
默认的二进制兼容性要求设置(settings)和选项(options)几乎精确匹配,以及依赖项(dependencies)的版本匹配,如之前关于依赖项的部分所解释。
总而言之,在安装依赖项时,所需二进制文件的 package_id
默认应匹配:
`package_id` 中的所有设置,除了
compiler.cppstd
之外,都应该与输入配置文件(profiles)中提供的设置精确匹配,包括编译器版本。因此compiler.version=9
与compiler.version=9.1
是不同的。默认行为将假定C++包的不同
compiler.cppstd
值之间存在二进制兼容性,如果输入配置文件中所需的cppstd
不存在,则能够回退到其他值而不是输入配置文件中指定的值。这由compatibility.py
插件控制,用户可以对其进行定制。`package_id` 中的所有选项(options)都应与输入配置文件中提供的值精确匹配。
依赖项的版本应匹配:
在“嵌入式依赖项”(embedding dependencies)的情况下,应匹配精确版本,包括配方修订版(recipe-revision)和依赖项的
package_id
。package_revision
从不包含在内,因为对于同一个package_id
拥有多个package_revision
被认为是格式不正确的。在“非嵌入式依赖项”(non-embedding dependencies)的情况下,依赖项的版本应匹配到次要版本(
minor
version),而补丁版本(patch
)、配方修订版(recipe_revision
)以及其他信息不予考虑。在“工具依赖项”(tool dependencies)的情况下,依赖项的版本默认完全不影响消费方(consumer)的
package_id
。
这些规则可以根据需要,使用不同的方法进行定制和修改,具体将在以下章节中解释:
定制设置和选项的二进制兼容性¶
`package_id()` 方法中的信息擦除¶
配方(Recipes)可以使用其 package_id()
方法从其 package_id
中**擦除(erase)**信息。例如,一个只包含可执行文件的包可以决定从其 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()
方法¶
配方(Recipes)可以使用其 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'
这些配置会影响包ID(package id)的计算方式,因此更改它们会影响您生成的二进制文件。因此建议它们在您的组织内部保持一致。
注意
最佳实践
强烈建议 core.package_id:default_xxx
在组织内部应保持全局、一致和不可变。为不同的项目或团队更改这些默认值可能会造成混淆,因为它会导致二进制文件缺失。
如果生成的包在组织外部共享,它们也应与包的消费方保持一致和共享,在这种情况下,建议通过 conan config install
共享 global.conf
文件。
考虑使用Conan的默认设置,它们在效率和安全性之间取得了良好的平衡,确保嵌入式(embed)情况的精确重建,以及非嵌入式(non-embed)情况通过版本进行良好控制。
为配方消费者定制 `package_id` 模式¶
配方(Recipes)可以通过一些 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`¶
配方(Recipes)可以使用 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
特性不区分“嵌入式”(embed)和“非嵌入式”(non-embed)情况,由用户自行定义正确的值。这种方法可能只适用于非常特殊的场景,即共享/静态库的变体不受 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 中定义。