核心指南

良好实践

  • build() 方法应该很简单,请在 generate() 中准备构建:Recipe 的 generate() 方法的目的是尽可能多地准备构建。调用 conan install 的用户将执行此方法,生成的应允许用户尽可能轻松地进行“原生”构建(直接调用“cmake”、“meson”等)。因此,在 build() 方法中尽量避免任何逻辑,并将其移至 generate() 方法,有助于开发人员在本地实现与 conan create 在本地缓存中构建相同的构建。

  • 生产环境应始终使用自己的配置文件,而不是依赖自动检测的配置文件,因为此类自动检测的输出可能会随时间变化,从而导致意外结果。配置文件(以及许多其他配置)可以使用 conan config install 进行管理。

  • 开发人员不应能够上传到服务器上的“开发”和“生产”存储库。只有 CI 构建才有权在服务器上写入。开发人员应仅拥有读取权限,最多只能访问一些用于与同事协作和共享内容的“沙盒”存储库,但这些存储库中的包永远不会被使用、移动或复制到开发或生产存储库。

  • test_package 的目的是验证包的正确创建,而不是功能测试test_package 的目的是检查包是否已正确创建(即,它是否已正确地在正确的文件夹中打包了头文件、库文件等),而不是检查包的功能是否正确。因此,它应该尽可能保持简单,例如,构建并运行一个使用头文件并链接到已打包库的可执行文件应该足够了。此类执行也应尽可能简单。任何类型的单元和功能测试都应在 build() 方法中完成。

  • 所有输入源对于所有二进制配置都必须是通用的:所有“源”输入,包括 conanfile.pyconandata.ymlexportsexports_sourcesource() 方法,在 source() 方法中应用的补丁,不能依赖于任何条件,例如平台、操作系统或编译器,因为它们是所有配置共享的。此外,所有这些内容的换行符应相同,建议在所有平台上始终只使用换行符,而不要在 Windows 上转换为或签出 crlf,否则会导致不同的配方修订版本。

  • 保持 ``python_requires`` 尽可能简单。避免传递性 python_requires,尽量减少它们,最多在“扁平”结构中显式地要求它们,而不要让 python_requires 要求其他 python_requires。如果不是绝对必要,则避免继承(通过 python_requires_extend),并全力避免多重继承,因为它非常复杂,而且与内置的 Python 继承方式不同。

  • 目前 **Conan 缓存不是并发的**。避免任何形式的并发或并行,例如,不同的并行 CI 作业应使用不同的缓存(通过 CONAN_HOME 环境变量)。这将来可能会改变,我们将努力为缓存提供并发性,但在此之前,请在并发任务中使用隔离的缓存。

  • 避免将“force”和“override”特性作为版本控制机制。 不建议将 forceoverride 特性作为常规版本控制解决方案来解决冲突,只能作为临时解决版本冲突的权宜之计。应尽可能避免使用它们,推荐的方法是通过更新图中的版本或版本范围来避免冲突,而无需覆盖和强制。

  • 请不要滥用“tool_requires”。它们仅用于在“构建”上下文中运行的 cmakeninja 等可执行文件,而不是用于库或类似库的依赖项,这些依赖项必须使用 requirestest_requires

  • 调用 Conan 时,位置参数应首先指定,然后是任何命名参数。例如,conan install . -s="os=Windows" 是正确的,而 conan install -s="os=Windows" . 是不正确的。同样,建议在命名参数的名称和值之间使用 = 而不是空格。这是为了避免在解析命令行参数时出现某些歧义情况。

  • 强烈不建议使用 user/channel 来表示任何质量、阶段、成熟度或变量信息channel 部分非常陈旧,在大多数情况下应避免使用,或使用固定的字符串,如 stableuser 可用于组织内部的私有包,而对于来自 ConanCenter 或 conan-center-index GitHub 存储库分支的包,建议使用它们而不带任何 user 或 channel,例如 zlib/1.3.1 ConanCenter 引用,即使是针对这些第三方库的配方和包的定制。

  • 管理包质量、阶段或成熟度 **提升的方式是使用不同的服务器存储库**,并且众所周知的开发人员最佳实践建议通过在这些不同的服务器存储库之间进行不可变伪影或包的提升(复制)来管理管道,例如,一旦包通过了某些质量检查,就将它们从 staging 存储库复制到 production 存储库。但重要的是,这种提升不应以任何方式改变这些包,它们必须完全不可变,甚至不改变其 user/channel,这就是为什么上述要点不建议使用 user 和 channel,包和伪影必须是不可变的。

  • 当您想同时使用次要版本时,请不要导出具有字母数字主版本的包。也就是说,不要使用 v1.3 等(但 systemdevelop 等是可以的)。原因是 Conan 执行的包二进制兼容性检查会特殊处理主版本作为字母数字情况,并将 v1.0v1.3 视为相同的版本,而忽略次要版本,这可能导致意外结果,例如,在应该构建时却没有构建丢失的二进制文件,或者包在发布时包含一个关键的错误修复。这可以通过确保配方中明确设置了 package_id_non_embed_modepackage_id_unknown_mode 为非 major_modeminor_modepatch_modesemver_mode(这些是受影响的模式)来缓解。但如果您想同时使用次要版本,最简单的方法就是避免使用字母数字主版本。

禁止的实践

  • Conan 不可重入:不能从 Conan 内部调用 Conan 进程。这包括从配方代码、钩子、插件以及基本上所有在调用 Conan 时已经执行的代码中调用 Conan。这样做将导致未定义行为。例如,从 conanfile.py 运行 conan search 是无效的。这包括间接调用,例如在 Conan 调用导致执行的构建脚本(如 CMakeLists.txt)中运行 Conan。出于相同的原因,不能从配方中使用 Conan Python API:Conan Python API 只能从 Conan 自定义命令或用户 Python 脚本调用,但绝不能从 conanfile.py 配方、钩子、扩展、插件或 Conan 执行的任何其他代码调用。

  • 配方中的设置和配置(conf)是只读的:在配方中不能定义或分配设置和配置的值。在配方中执行类似 self.settings.compiler = "gcc" 的操作不应该发生。这是未定义行为,可能随时崩溃,或者被忽略。设置和配置只能在配置文件、命令行参数或 profile.py 插件中定义。

  • 配方保留名称:Conan conanfile.py 配方的用户属性和方法应始终以 _ 开头。Conan 为所有属性和方法保留“公共”命名空间,为私有名称保留“_conan”。使用任何未记录的 Python 函数、方法、类、属性,即使它在 Python 意义上是“公共”的,如果该元素未在此文档中记录,则属于未定义行为。

  • Conan 伪影是不可变的:一旦 Conan 包和伪影进入 Conan 缓存,就假定它们是不可变的。任何尝试修改导出的源、配方、conandata.yml 或任何导出的或打包的伪影的行为都是未定义行为。例如,不能在 package_info() 方法或 package_id() 方法中修改包的内容,这些方法不应该在包内修改、删除或创建新文件。如果您需要修改某些包,可以使用自己的自定义 deployer

  • Conan 缓存路径是内部实现细节:Conan 缓存路径是内部实现细节。Conan 配方提供抽象,如 self.build_folder,以表示文件夹的必要信息,并提供 conan cache path 等命令来获取当前文件夹的信息。Conan 缓存可以在调试时查看,作为只读的,但不允许通过 Conan 命令行或公共 API 以外的任何其他方式编辑、修改或删除 Conan 缓存中的伪影或文件。

  • 配方中使用的源必须是不可变的。一旦配方被导出到 Conan 缓存,预期源是不可变的,也就是说,将来检索源将始终检索完全相同的源。不允许使用移动目标,例如 git 分支或服务器上持续重写的文件的下载。git 签出必须是不可变标签或提交,而 download()/get() 必须使用校验和来验证服务器文件没有更改。不使用不可变源将导致未定义行为。