核心指南¶
良好实践¶
build() 方法应保持简单,而应在 generate() 中准备构建:包的
generate()
方法的目的是尽可能多地准备构建。调用conan install
的用户将执行此方法,生成的文件应使用户能够尽可能轻松地进行“原生”构建(直接调用 “cmake”、“meson” 等)。因此,尽可能避免在build()
方法中包含任何逻辑,并将其移至generate()
方法,这有助于开发人员在本地实现与conan create
在本地缓存中构建的相同构建。在生产环境始终使用您自己的配置文件,而不是依赖自动检测到的配置文件,因为此类自动检测的输出可能会随时间而变化,导致意外结果。配置文件(以及许多其他配置)可以使用
conan config install
进行管理。开发人员不应能够上传到服务器上的“开发”和“生产”仓库。只有 CI 构建在服务器上具有写入权限。开发人员应只有读取权限,并且最多只能访问一些用于工作和与同事共享内容的“游乐场”仓库,但这些仓库中的包绝不会被使用、移动或复制到开发或生产仓库。
test_package 的目的是验证包是否正确创建,而不是进行功能测试。
test_package
的目的是检查包是否已正确创建(即,它是否已正确地将头文件、库文件等打包到正确的文件夹中),而不是检查包的功能是否正确。然后,它应尽可能保持简单,例如构建和运行一个使用头文件并链接到打包库的可执行文件应该足够了。此类执行也应尽可能简单。任何单元测试和功能测试都应在build()
方法中完成。所有输入源必须对所有二进制配置都是通用的:所有“源”输入,包括
conanfile.py
、conandata.yml
、exports
和exports_source
、source()
方法、在source()
方法中应用的补丁,都不能依赖于任何平台、操作系统或编译器,因为它们是所有配置共享的。此外,所有这些内容的文件行尾应相同,建议在所有平台上始终只使用换行符,并且不要在 Windows 上转换或检出为crlf
,因为这会导致不同的配方修订版本。尽量保持 ``python_requires`` 简单。避免传递性
python_requires
,尽量减少它们,最多在“扁平”结构中显式要求它们,而不要让python_requires
要求其他python_requires
。除非绝对必要,否则避免继承(通过python_requires_extend
),并绝对避免多重继承,因为它非常复杂,而且与内置的 Python 继承方式不同。目前 **Conan 缓存不是并发的**。避免任何类型的并发或并行,例如,不同的并行 CI 作业应使用不同的缓存(通过 CONAN_HOME 环境变量)。这将来可能会改变,我们将努力在缓存中提供并发性,但在此之前,请为并发任务使用隔离的缓存。
避免将“force”和“override”特性作为版本控制机制。不建议将
force
和override
特性作为常规版本控制解决方案来解决冲突,仅作为解决版本冲突的临时解决方法。应尽可能避免使用它们,推荐的方法是更新图中的版本或版本范围来解决冲突,而无需覆盖和强制。请不要滥用“tool_requires”。它们仅用于在“构建”上下文中运行的
cmake
和ninja
等可执行文件,而不是用于必须使用requires
或test_requires
的库或类似库的依赖项。调用 Conan 时,位置参数应先于任何命名参数指定。例如,
conan install . -s="os=Windows"
是正确的,而conan install -s="os=Windows" .
则不正确。同样,建议在命名参数的名称和值之间使用=
而不是空格。这是为了避免解析命令行参数时出现一些歧义情况。强烈不建议使用 user/channel 来表示任何质量、阶段、成熟度或变量信息。
channel
部分非常陈旧,在大多数情况下应避免使用,或者使用固定字符串,如stable
。user
可用于组织内部的私有包,而对于来自 ConanCenter 或conan-center-index
Github 仓库的包,建议不带任何 user 或 channel 使用它们,就像zlib/1.3.1
ConanCenter 引用一样,即使是为这些第三方库定制配方和包也是如此。管理包质量、阶段或成熟度**升级的方式是使用不同的服务器仓库**,并且众所周知的开发人员最佳实践建议通过在这些不同的服务器仓库之间进行不可变工件或包的升级(复制)来管理管道,例如,在包通过某些质量检查后,将包从
staging
仓库复制到production
仓库。但非常重要的是,这种升级不应以任何方式改变这些包,这些包必须完全不可变,甚至不改变其user/channel
,这就是为什么上述一点不建议使用 user 和 channel,包和工件必须是不可变的。
如果您想使用次要版本,请不要导出具有字母数字主版本的包。也就是说,不要使用
v1.3
等(但system
、develop
等是可以的)。原因是 Conan 执行的包二进制兼容性检查会特殊处理主版本作为字母数字情况,并且会认为v1.0
和v1.3
是相同版本,而不管次要版本如何,这可能会导致意外结果,例如,一个缺失的二进制文件在应该构建时未构建,或者一个包在没有关键错误修复的情况下发布。这可以通过确保在配方中显式设置package_id_non_embed_mode
和package_id_unknown_mode
为除major_mode
、minor_mode
、patch_mode
或semver_mode
(这些是受影响的模式)以外的其他值来缓解。但如果您想同时使用次要版本,避免使用字母数字主版本会更简单。
禁止实践¶
Conan 不支持重入:不能从 Conan 自身调用 Conan 进程。这包括从配方代码、hook、插件以及 Conan 调用时执行的几乎所有代码中调用 Conan。这样做将导致未定义行为。例如,从
conanfile.py
运行conan search
是无效的。这包括间接调用,例如在 Conan 调用导致执行的构建脚本(如CMakeLists.txt
)中运行 Conan。出于相同的原因,Conan Python API 不能从配方中使用:Conan Python API 只能从 Conan 自定义命令或用户 Python 脚本中调用,但绝不能从conanfile.py
配方、hook、扩展、插件或 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()
必须使用校验和来验证服务器文件不会更改。不使用不可变源将导致未定义行为。