Conan的package ID由settings、options和revision共同决定,一个ID对应唯一二进制;options(如shared)与settings同为强制哈希维度,不同值生成不同ID,不复用二进制,且需配方明确定义才能生效。

conan package ID 由 settings × options × revision 共同决定
Conan 不是“一个包对应一个二进制”,而是“一个 package ID 对应一个二进制”。而 package ID 的计算公式里,options(如 shared=True、fPIC=True)和 settings(如 build_type=Debug、compiler.libcxx=libstdc++11)一样,是参与哈希的强制维度。
这意味着:
-
zlib/1.2.12在shared=True和shared=False下生成的是两个完全不同的 package ID,远端必须分别上传、本地必须分别缓存 - 即使源码完全相同,只要
options不同,Conan 就不会复用已有的二进制——它认为这是逻辑上不同的产物 -
options可以跨依赖传递(例如mylib/1.0依赖zlib/1.2.12,且声明requires = "zlib/1.2.12"),但是否生效取决于zlib配方是否定义了该 option(比如zlib官方配方确实有shared)
为什么 shared=True/False 必须显式声明,不能默认继承?
因为 shared 是 ABI 级别的分水岭:静态链接时符号在编译期解析,动态链接时依赖运行时加载与符号可见性。Conan 强制你为每个依赖明确选择,避免隐式混合导致的链接错误或运行时崩溃。
常见踩坑点:
- 你的项目设了
default_options = {"shared": True},但依赖的openssl配方没声明sharedoption → Conan 报错:option 'shared' doesn't exist - 你本地 build 时用了
--build=missing,但远端已有shared=False的 zlib 二进制,而你想要shared=True→ Conan 不会复用,而是重新从源码构建,除非你删掉远端那个二进制或换 profile - Windows 下 MSVC 的
MT/MD运行时选择,Conan 不直接暴露为 option,而是通过compiler.runtime(属于settings)控制,但它和shared效果等价:改它,package ID就变
options 传递不是自动的,得靠 requires + override 或 profile 匹配
假设你写了一个库 myapp,它依赖 fmt/10.2.1,你想让 fmt 也用 shared=True。仅在 myapp/conanfile.py 里写 default_options = {"fmt:shared": True} 是不够的——这行只在当前 recipe 解析时起作用,不保证下游 fmt 配方真能接收。
真正生效的前提是:
-
fmt的 conanfile.py 中明确定义了options = {"shared": [True, False]} - 你在 install/build 时使用的 profile 或命令行参数,没有覆盖这个值(例如没传
-o fmt:shared=False) - 如果你用
conan install . -o myapp:shared=True -o fmt:shared=True,那没问题;但若只写-o shared=True,它只作用于myapp自身,不透传到fmt
更稳妥的做法是把共性选项统一收口到 profile 文件里,比如在 profiles/myprofile 中写:
[options] *:shared=True zlib/*:fPIC=True
调试 package ID 不匹配时,先看 conan list 和 conan search
当你遇到 ERROR: Missing binary for xxx,别急着重装。先确认你当前上下文实际要拉的 package ID 是什么:
- 运行
conan graph info . --format=json > graph.json,查看每个节点的package_id字段 - 用
conan list "zlib/1.2.12" --rev latest --graph=graph.json查本地缓存里有没有匹配的二进制 - 如果远端也没有,
conan search "zlib/1.2.12" -r conancenter能列出 conancenter 上所有可用的zlib/1.2.12变体,包括它们的settings和options组合
最容易被忽略的一点:options 的粒度可以细到子依赖,比如 boost/1.85.0 支持 without_system=True、without_filesystem=True,这些都会独立影响其 package ID —— 换句话说,你关掉一个模块,就等于换了一个二进制。


















