扩展二进制模型¶
有几种机制可以扩展默认的 Conan 二进制模型
自定义设置¶
可以在 settings.yml 文件中添加新的设置或子设置,例如
os:
Windows:
new_subsetting: [null, "subvalue1", "subvalue2"]
new_root_setting: [null, "value1", "value2"]
其中 null
值允许在 profile 中不定义该设置。如果不包含 null
,则 profile 必须为它们定义一个值。
自定义设置将在 recipes 和 packages 中显式或隐式使用
class Pkg(ConanFile):
# If we explicilty want this package binaries to vary according to 'new_root_setting'
settings = "os", "compiler", "build_type", "arch", "new_root_setting"
# While all packages with 'os=Windows' will implicitly vary according to 'new_subsetting'
另请参阅
有关如何自定义 settings.yml
文件的完整参考,请访问设置部分。实际上,不需要修改 settings.yml
文件,而是可以提供 settings_user.yml
文件来扩展现有设置。请参阅settings_user.yml 文档。
自定义选项¶
Options
对每个 recipe 来说都是自定义的,不像 settings.yml
那样有全局定义。
包的 conanfile.py
recipe 定义自己的选项,以及它们自己的有效值范围和默认值
class MyPkg(ConanFile):
...
options = {"build_tests": [True, False],
"option2": ["ANY"]}
default_options = {"build_tests": True,
"option1": 42,
"z*:shared": True}
选项 shared
、fPIC
和 header_only
对 Conan 具有特殊含义,并且大多数内置构建系统集成会自动考虑它们。它们也是推荐的默认选项,用于表示库是共享库、静态库还是仅头文件库。
设置 vs 选项 vs 配置¶
何时使用设置、选项或配置?
设置 (Settings) 是项目范围的配置,通常会影响整个正在构建的项目并影响最终的包二进制文件。例如,操作系统或架构对于依赖图中的所有包自然是相同的,将 Linux 库链接到构建 Windows 应用程序或混合架构是不可能的。设置不能在包 recipe 中设置默认值。给定库的 recipe 不能说其默认值为
os=Windows
。os
将由处理该 recipe 的环境提供。它是必须在输入 profile 中定义的强制输入。另一方面,选项 (options) 是包特定的配置,会影响最终的包二进制文件。静态库或共享库并不是适用于所有包的设置。有些可以是仅头文件库,而其他包可能只是数据或可执行文件包。例如,
shared
是一个常用选项(指定库是静态库还是共享库的默认选项),但包可以定义和使用它们想要的任何选项。选项在包的conanfile.py
recipe 中定义,包括使用options
和default_options
指定的受支持值和默认值。通过
conf
进行的配置通常不影响最终的包二进制文件。例如,使用tools.cmake.cmaketoolchain:generator=Ninja
构建库不应导致与使用 Visual Studio 构建的二进制文件不同(仅是由于 Ninja 通常构建速度更快)。
上述规则有一些例外。例如,可以使用 <pattern:>setting=value
在 profile 和命令行中为每个包定义设置
$ conan install . -s mypkg/*:compiler=gcc -s compiler=clang ..
这将对“mypkg”使用 gcc
,对其余依赖项使用 clang
(在大多数情况下,建议对整个依赖图使用相同的编译器,但在某些二进制兼容性有强力保证的场景下,可以混合使用不同编译器构建的库)。
在某些情况下,许多包使用相同的选项值,因此可以使用模式一次性设置其值,例如
$ conan install . -o *:shared=True
自定义配置¶
如上所述,Conan 的 conf
配置系统旨在调整一些工具和行为,但不会真正影响最终的包二进制文件。一些典型的 conf
项目包括激活并行构建、配置上传到服务器时的“重试”次数,或更改 CMake 生成器。更多关于 Conan 配置系统的信息请参阅本节。
还可以为用户自定义配置定义 user.xxxx:conf=value
,这与核心和工具内置配置的理念相同,不影响二进制文件的 package_id
。
但在某些特殊情况下,可能确实希望某些 conf
定义不同的 package_ids
,从而创建不同的包二进制文件。可以在两个不同的位置执行此操作
在本地,通过 recipe 的
package_id
方法中的self.info.conf
属性def package_id(self): # We can get the value from the actual current conf value, or define a new value value = self.conf.get("user.myconf:myitem") # This ``self.info.conf`` will become part of the ``package_id`` self.info.conf.define("user.myconf:myitem", value)
全局,使用
tools.info.package_id:confs
配置,该配置接收一个现有配置列表作为参数,这些配置将成为 package ID 的一部分,因此您可以在 profile 中定义tools.info.package_id:confs=["tools.build:cxxflags", ...]
package_id
的值将包含在tools.build:cxxflags
和其他配置中提供的值。请注意,此值是作为字符串管理的,更改字符串会产生不同的结果和不同的package_id
,因此如果使用此方法,对于tools.build:cxxflags
等不同配置,提供的数值必须非常一致。也可以使用正则表达式匹配多个
confs
,而不是列出所有confs
,例如.*cmake
可以匹配名称中包含“cmake”的任何配置(不推荐这样做,请参阅下面的最佳实践)。
注意
最佳实践
一般来说,通过 conf
定义二进制文件 package_id
的变动性应保留给特殊情况,并始终小心管理。将许多不同的 confs
传递给 tools.info.package_id:confs
很容易导致诸如缺少二进制文件或不必要地构建太多二进制文件的问题。如果出现这种情况,请考虑使用新的自定义设置或选项在二进制文件之上构建更高级别的抽象。
交叉构建目标设置¶
self.settings_target
是 conanfile.py
的一个属性,它在交叉编译场景中对“build”上下文中的 tool_requires
工具变得相关。当我们有一个像 CMake 这样的 tool_requires
,例如 cmake/3.25.3
,包二进制文件独立于交叉编译可能针对的平台,它是所有不同目标平台的同一个 cmake
可执行文件。对于从 Windows-X64 到 Linux-armv8 的交叉构建场景,cmake
conanfile recipe 的 settings
将是
self.settings
: 当前cmake/3.25.3
运行的设置。由于它是一个 tool-require,它将在 Windows 机器上运行,因此self.settings.os = Windows
和self.settings.arch = x86_64
。self.settings_build
: 如果需要,当前构建机器构建此包的设置。这也是 Windows-x64 机器,因此self.settings_build.os = Windows
和self.settings_build.arch = x86_64
。self.settings_target
: 当前应用程序结果将要针对的设置。在这种情况下,它将是self.settings_target.os = Linux
和self.settings_target.arch = armv8
在 cmake
包的场景中,正如我们指出的,目标是无关紧要的。它完全没有在 cmake
conanfile recipe 中使用,并且不影响 cmake
二进制包的 package_id
。
但有些情况下,二进制包可能会根据目标平台而不同。例如,交叉编译器 gcc
会根据它将要编译的目标拥有不同的 gcc
可执行文件。这在 GNU 生态系统中很典型,例如我们可以找到特定于给定架构的 arm-gcc
工具链。Conan 可以通过使用这些 settings_target
的值来扩展 package_id
,从而反映此场景。
def package_id(self):
self.info.settings_target = self.settings_target
# If we only want the ``os`` and ``arch`` settings, then we remove the other:
self.info.settings_target.rm_safe("compiler")
self.info.settings_target.rm_safe("build_type")