扩展二进制模型

有几种机制可以扩展默认的 Conan 二进制模型

自定义设置

可以在 settings.yml 文件中添加新的设置或子设置,例如

os:
    Windows:
        new_subsetting: [null, "subvalue1", "subvalue2"]
new_root_setting: [null, "value1", "value2"]

其中 null 值允许在配置文件中将设置保留为未定义。如果不包含,则配置文件必须为它们定义值。

自定义设置将在配方和包中显式或隐式使用

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 对每个配方都是自定义的,没有像 settings.yml 那样的全局选项定义。

conanfile.py 配方定义了自己的选项,以及它们自己的有效值范围和默认值

class MyPkg(ConanFile):
    ...
    options = {"build_tests": [True, False],
               "option2": ["ANY"]}
    default_options = {"build_tests": True,
                        "option1": 42,
                        "z*:shared": True}

选项 sharedfPICheader_only 对 Conan 具有特殊含义,并且大多数内置构建系统集成会自动考虑它们。它们也是表示库是共享库、静态库还是仅包含头文件的推荐默认值。

设置与选项与配置

何时使用设置或选项或配置?

  • 设置是项目范围的配置,通常会影响正在构建的整个项目并影响生成的包二进制文件。例如,操作系统或架构通常在依赖关系图中的所有包中都相同,将 Linux 库链接到构建 Windows 应用程序,或者混合架构是不可能的。设置不能在包配方中设置默认值。给定库的配方不能说其默认值为 os=Windowsos 将由处理该配方的环境提供。它是输入配置文件中必须定义的强制输入。

  • 另一方面,选项是特定于包的配置,会影响生成的包二进制文件。静态库或共享库不是应用于所有包的设置。有些可能是仅包含头文件的库,而其他包可能只是数据或包可执行文件。例如,shared 是一个常见的选项(指定库是静态库还是共享库的默认值),但包可以定义和使用任何它们想要的选项。选项在包 conanfile.py 配方中定义,包括其支持的值和默认值,以及 optionsdefault_options

  • 通过 conf 进行配置旨在用于在一般情况下不会影响生成的包二进制文件的配置。例如,使用 tools.cmake.cmaketoolchain:generator=Ninja 构建一个库不应该产生与使用 Visual Studio 构建的二进制文件不同的结果(只是由于 Ninja 而导致构建速度更快)。

以上内容有一些例外。例如,可以使用 <pattern:>setting=value 在配置文件和命令行中为每个包定义设置

$ 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,从而创建不同的包二进制文件。这可以在两个不同的地方完成

  • 在本地,在配方的 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 属性,在“构建”上下文中针对 tool_requires 工具的交叉编译场景中变得相关。当我们有一个像 CMake 那样的 tool_requires 时,例如 cmake/3.25.3,包二进制文件与可能进行交叉编译的目标平台无关,它是所有不同目标平台的相同 cmake 可执行文件。settings 用于从 Windows-X64 到 Linux-armv8 的交叉构建场景的 cmake conanfile 配方将是

  • self.settings:当前 cmake/3.25.3 将运行的设置。由于它是一个工具需求,它将在 Windows 机器上运行,因此 self.settings.os = Windowsself.settings.arch = x86_64

  • self.settings_build:当前构建机器的设置,如果需要,将构建此包。这也是 Windows-x64 机器,因此 self.settings_build.os = Windowsself.settings_build.arch = x86_64 也是。

  • self.settings_target:当前应用程序结果将针对的设置。在这种情况下,它将是 self.settings_target.os = Linuxself.settings_target.arch = armv8

cmake 包场景中,正如我们指出的那样,目标无关紧要。它根本没有在 cmake conanfile 配方中使用,也不会影响 cmake 二进制包的 package_id

但是,在某些情况下,二进制包可能因目标平台而异。例如,一个交叉编译器 gcc,它具有基于其将编译的目标的不同 gcc 可执行文件。这在 GNU 生态系统中很常见,在 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")