核心指南

良好实践

  • build() 应该保持简单,在 generate() 中准备构建:recipe 的 generate() 方法的目的是尽可能地准备构建。调用 conan install 的用户将执行此方法,并且生成的文件应该允许用户像直接调用“cmake”、“meson”等一样轻松地进行“原生”构建。因此,尽可能避免在 build() 方法中进行任何逻辑,并将其移动到 generate() 方法,有助于开发人员实现与通过 conan create 构建在本地缓存中产生的构建相同的构建。

  • 始终在生产中使用您自己的 profile,不要依赖自动检测的 profile,因为这种自动检测的结果可能会随时间变化,导致意外的结果。可以使用 conan config install 管理 profile(以及许多其他配置)。

  • 开发人员不应能够上传到“开发”和“生产”仓库,只有 CI 构建才有服务器的写入权限。开发人员应该只有读取权限,最多只能有一些“游乐场”仓库,用于与同事一起工作和分享内容,但这些包绝不能被移动或复制到开发或生产仓库。

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

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

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

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

  • 避免使用 ‘force’ 和 ‘override’ traits 作为版本控制机制。 forceoverride traits 用于解决冲突,不建议将其作为通用的版本控制解决方案,而只是作为解决版本冲突的临时解决方法。应尽可能避免使用它们,并且更新图中的版本或版本范围以避免冲突,而无需使用覆盖和强制是推荐的方法。

  • 请不要滥用 ‘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 仓库的包的建议是,即使是针对这些第三方库的 recipe 和包的定制,也无需使用任何 user 或 channel,例如 ConanCenter 引用 zlib/1.3.1

  • 管理包质量、阶段或成熟度提升的方法是使用不同的服务器仓库,并且众所周知的开发人员最佳实践建议通过在这些不同的服务器仓库之间复制不可变的工件或包来管理管道,例如,一旦通过了一些质量检查,将包从 staging 仓库复制到 production 仓库。但重要的是,此提升不会以任何方式更改这些包,这些包必须完全不可变,甚至不能更改其 user/channel,这就是为什么上述点不鼓励使用 user 和 channel,包和工件必须是不可变的。

  • 不要导出具有字母数字主版本的包,当你想要使用次版本时。也就是说,不要使用 v1.3 等(但 systemdevelop 等是可以的)。原因是 Conan 执行的包二进制兼容性检查会特殊处理字母数字情况下的主版本,并将 v1.0v1.3 视为相同的版本,而不管次版本如何,这可能会导致意外的结果,例如未构建缺失的二进制文件,以及发布包含关键 bugfix 的包。可以通过显式地将 recipe 中的 package_id_non_embed_modepackage_id_unknown_mode 设置为除 major_modeminor_modepatch_modesemver_mode 之外的其他内容来缓解此问题,这些是受影响的模式。但如果你想使用次版本,最好避免使用字母数字主版本。

禁止的实践

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

  • recipe 中的设置和配置 (conf) 是只读的:设置和配置不能在 recipe 中定义或分配值。例如,在 recipe 中进行 self.settings.compiler = "gcc" 是不应该做的。这是未定义行为,并且随时可能崩溃或被忽略。设置和配置只能在 profile 中、命令行参数中或 profile.py 插件中定义。

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

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

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

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