扩展二进制模型¶
有几种机制可以扩展默认的 Conan 二进制模型
自定义设置¶
可以在 settings.yml 文件中添加新的设置或子设置,例如
os:
Windows:
new_subsetting: [null, "subvalue1", "subvalue2"]
new_root_setting: [null, "value1", "value2"]
其中 null 值允许在配置文件中将设置留空。如果不包含,配置文件则必须为它们定义一个值。
自定义设置将在 recipe 和包中显式或隐式地使用
class Pkg(ConanFile):
# If we explicitly 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 部分。实际上,没有必要修改 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 conf¶
何时使用设置、选项或配置?
设置 是项目范围的配置,通常会影响正在构建的整个项目并影响最终的二进制包。例如,操作系统或架构对于依赖图中的所有包来说通常是相同的,将 Linux 库链接到构建 Windows 应用程序,或者混合架构是不可能的。设置不能在包 recipe 中设置默认值。一个给定库的 recipe 不能说它的默认值是
os=Windows。os将由处理该 recipe 的环境决定。它是一个在输入配置文件中定义的强制性输入。另一方面,选项 是影响最终二进制包的包特定配置。静态或共享库不是适用于所有包的设置。有些可以是头文件库,而其他包可以只是数据,或者包可执行文件。例如,
shared是一个常见的选项(用于指定库是静态还是共享的默认值),但包可以定义和使用它们想要的任何选项。选项在包conanfile.pyrecipe 中定义,包括它们支持的默认值,使用options和default_options。通过
conf进行的配置旨在调整一些工具和行为,但实际上不影响最终的二进制包。例如,使用tools.cmake.cmaketoolchain:generator=Ninja构建一个库,其二进制结果不应与使用 Visual Studio 构建的库不同(只是通常由于 Ninja 而构建速度更快)。
以上存在一些例外。例如,可以使用 ` 这将为“mypkg”使用 有时许多包使用相同的选项值,从而允许您使用模式一次性设置其值,例如$ conan install . -s mypkg/*:compiler=gcc -s compiler=clang ..
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配置,接收一个现有配置列表作为参数,这些配置将成为包 ID 的一部分,因此您可以在配置文件中定义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` 将运行的设置。由于它是一个工具要求,它将在 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 可以通过将 `package_id` 扩展到这些 `settings_target` 的值来反映这种情况。
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")