3.使用conan进行包管理-conan依赖版本冲突问题
1. 间接依赖冲突
1.1. 问题描述
conan install报错:this binary has same settings and options,but different dependencies
1.2. 问题分析
这个错误通常发生在 *Conan 2.0+* 中,表示你当前请求的依赖图(Dependency Graph)与远程或本地缓存中已有的二进制包(Binary Package)在依赖版本上不匹配。
简单来说:你的 settings(编译器、架构等)和
options(是否为 shared
等)都对上了,但该二进制包在编译时使用的某个*上游依赖的版本/Revision*,与你现在项目中解析出的版本不一样。
常见原因:
- 依赖冲突或版本升级:你项目中的某个依赖(如
OpenSSL/3.0.1)被升级到了新版本(如3.0.2),而现有的二进制包是针对旧版本编译的。 - Revision(修订号)不一致:即使版本号相同,如果上游依赖的 recipe 发生了微调并重新上传(产生了新的 Recipe Revision),Conan 也会认为这是一个“不同的依赖”。
- Diamond Dependency(菱形依赖):两个不同的库依赖了同一个库的不同版本,导致最终解析出的版本与其中一个库编译时所用的不符。
解决办法:
- 强制重新编译(最直接的办法)
使用 --build=missing 参数。Conan
会检测到依赖不匹配并为你本地编译一个符合当前依赖版本的新二进制包:
1 | |
- 诊断具体差异
如果你想知道到底是哪个依赖对不上,可以使用 Conan 2.x 提供的解释工具:
1 | |
输出结果会明确显示 expected(你现在的需求)和
existing(已有二进制包的需求)之间的差异。
- 锁定版本(Lockfiles)
如果你在 CI/CD 环境中遇到此问题,建议使用 Lockfiles 来固定整个依赖树的 Revisions,确保安装环境与构建环境完全一致。
- 清理本地缓存
有时候本地过时的元数据会导致识别错误,可以尝试清理后重新拉取:
1 | |
1.3. 进一步分析
安装protobuf/3.20.3,requires: zlib/1.3.Z,我的仓库中有zlib/1.3.1,为什么还是不行?
在 Conan 2.x 中,看到 zlib/1.3.Z 这种写法,并不是指你的仓库里缺这个版本,而是 Conan 的一种二进制匹配机制(Binary Compatibility)的体现。
即便你本地有 zlib/1.3.1,报错仍然发生的原因主要有以下几点:
- 为什么显示 “zlib/1.3.Z”?
在 Conan 的二进制哈希计算中,Z 是一个占位符,代表 “任意补丁版本”。
• 如果你依赖的是 zlib 的动态库(Shared Library),Conan 默认认为 1.3.0、1.3.1 甚至未来的 1.3.2 在二进制上是兼容的。
• 因此,它会将依赖标记为 zlib/1.3.Z,意思是一个针对 1.3.x 编译的包可以复用于整个 1.3 系列。
为什么还是报错 “different dependencies”?
报错是因为虽然 settings 一样,但依赖树的“指纹”对不上。常见场景包括:
静态链接 vs 动态链接的不一致:
如果你现在的项目要求 zlib 是静态库(shared=False),而仓库里现有的 protobuf 二进制包在编译时是连接的 zlib 动态库(或者反之)。
当依赖是静态库时,Conan 通常会要求精确匹配(包括具体的 Revision),因为它认为静态库的代码已经“嵌入”到了二进制中,补丁版本的微小差异也可能被视为不安全。
Recipe Revision (RREV) 不匹配:
- 即便版本号都是 1.3.1,如果远程仓库的 zlib 脚本更新了(产生了新的 RREV),而你的 protobuf 二进制包是基于旧版 zlib 脚本编译生成的,Conan 就会认为依赖发生了变化。
传递依赖冲突:
- 可能你的项目里还有其他库依赖了不同版本的 zlib,导致 Conan 在解析最终依赖图时,选出的 zlib 实例与 protobuf 二进制包预期的那一个不一致。
- 解决办法:
- 强制本地重编译
直接让 Conan 针对你当前的 zlib/1.3.1 环境重新编译一个
protobuf:
1 | |
或者更保险一点,编译所有缺失二进制的包:
1 | |
- 查看具体差异
使用 Conan 内置工具分析到底是哪个依赖项、哪个属性导致了不匹配:
1 | |
在输出中找 diff
部分,它会明确告诉你:仓库里的包预期的是哪个 zlib
哈希,而你提供的是哪个。
- 检查 Options 冲突
确保你的 shared
选项在整个依赖链中是一致的。可以在命令行检查:
1 | |
建议操作:由于 C++ 库经常涉及静态链接和复杂的兼容性,遇到此类“指纹”对不上的问题,使用 –build=missing 重新生成二进制通常是最快且最稳妥的办法。
1.4 继续分析
显示:expected: fPIC=True,shared=False existing: fPIC=None,shared=true。
这就是问题的根源:Options(选项)冲突。
报错信息明确指出,你本地项目期望(Expected)的配置与仓库中已有(Existing)的二进制包配置不符:
- 你的项目(Expected):要求
fPIC=True(生成位置无关代码)且shared=False(静态库)。 - 远程/缓存包(Existing):该二进制包是按
fPIC=None且shared=True(动态库)编译的。
由于 Conan 默认不会自动混合链接静态库和动态库(为了避免符号冲突和运行时错误),它发现现有的二进制包无法满足你的静态链接需求,因此报错。
解决方法:
你可以通过以下两种方式统一配置:
方案 A:强制本地编译(最推荐)
既然仓库里只有动态库版本,而你需要静态库,直接让 Conan
在本地针对你的配置编译一个 protobuf 静态库:
1 | |
或者包含所有依赖:
1 | |
方案 B:修改你的请求,改用动态库
如果你其实不介意使用动态库,可以在命令行或 conanfile
中显式指定:
1 | |
方案 C:在 profile 中统一设置
如果你希望全局都使用静态库,检查你的 default
profile(或你正在使用的 profile),确保设置一致: 1
2
3
4[options]
protobuf/*:shared=False
zlib/*:shared=False
*:fPIC=True
为什么会出现 fPIC=None?
通常是因为 shared=True(动态库)时,fPIC
属性会自动失效或变为不可用状态(因为动态库本身就必须是位置无关的),而当你切换到
shared=False(静态库)时,fPIC
就变成了必须明确定义的关键选项。
1.5. 最终解决方案
指定protobuf依赖的zlib为动态库。
conanfile.txt:
1 | |
建议使用lockfile记录依赖图的状态,防止后续依赖变化导致构建失败
- 首次创建
lockfile
构建成功后,使用conan install . --lockfile-out=conan.lock生成lockfile
- 把
lockfile上传到gitlab,与conanfile.txt在同级目录 - 后续使用锁文件:
后续构建时,使用conan install . --lockfile=conan.lock 指定lockfile
1.6. 总结
排查流程:
- 先用
conan graph explain .,查看所有依赖冲突的包 - 再用
conan graph explain --requires=<包名/版本>,查看具体冲突信息 - 根据具体信息,提供解决方案:
- 如果是缺少间接依赖版本,那就上传缺失的间接依赖,注意区分动态或静态库
- 如果间接依赖版本存在,那么查看是否是间接依赖的配置问题:动态库、静态库。根据配置问题进行更改,如果可以选择配置,那就选择对应的配置;如果没有对应的配置,那就只能再编译一个符合要求的版本并上传