package_id 是 Conan 依据 settings、options 和依赖约束生成的二进制唯一指纹,源码修改不改变它,因默认不纳入计算;GCC 小版本差异导致 package_id 不同而报错缺失二进制;依赖仅 major 版本默认参与计算,启用 full_version_mode 可提升精度。

package_id 是 Conan 用来唯一标识一个二进制包的哈希值,它不等于源码版本,也不等于 recipe 版本,而是由构建上下文(settings + options + 依赖约束)共同决定的“产物指纹”。
为什么改了源码但 package_id 没变?
Conan 默认只把 settings(如 os、compiler、build_type)和 options(如 shared=True)纳入 package_id 计算,而源码变更(比如改了 hello.h)本身不会触发重新计算 —— 除非你显式让 package_id() 感知源码变动(例如通过 self.info.requires["dep"].package_revision_mode() 或启用 revision_mode)。
- 默认行为下,
conan create hello/1.1@和conan create hello/1.0@可能生成完全相同的package_id,只要它们的settings和options一致 - 这正是为什么你改了头文件再
conan create app,输出却“没变”:Conan 认为它拉的是同一个二进制 - 若要让源码变更影响
package_id,需在conanfile.py中启用 revision 模式:self.info.recipe.revision_mode = "scm"或配置全局general.revisions_enabled=1
为什么换了个 GCC 小版本就找不到包?
因为 compiler.version 是 settings 的一部分,默认直接参与 package_id 计算。GCC 4.9 和 GCC 4.8 被视为两个完全不同的构建环境,对应不同 package_id。
- 常见错误现象:
ERROR: Missing binary: xxx/package_id=abc123,但你明明有 GCC 4.8 的包,只是当前 profile 用了 4.9 - 解决方式不是硬改 profile,而是用
compatible_packages做 fallback: def package_id(self): if self.settings.compiler == "gcc" and self.settings.compiler.version == "4.9": for v in ("4.8", "4.7"): compatible = self.info.clone() compatible.settings.compiler.version = v self.compatible_packages.append(compatible)- 注意:这个逻辑只在
conan create或conan install时生效,且仅对当前 recipe 生效,不影响其依赖项
依赖版本怎么影响 package_id?
默认情况下,只有直接依赖的 major 版本号参与 package_id 计算(例如 zlib/1.2.11 → 视为 zlib/1.*),minor 和 patch 被忽略,间接依赖完全不参与。
- 这意味着:即使你把
zlib/1.2.11升级到zlib/1.2.12,只要 major 相同,package_id不变 - 若需更严格控制(比如 patch 级别安全修复必须重建),应启用
full_version_mode: conan config set general.default_package_id_mode=full_version_mode- 或在 recipe 中指定:
self.info.requires["zlib"].full_version_mode() - ⚠️ 启用后,所有依赖版本变更都会导致
package_id改变,CI 缓存命中率会显著下降
真正容易被忽略的是:package_id 的计算发生在 conan install 阶段,而不是 conan create;它反映的是“消费者视角”的兼容性判断,而非“生产者视角”的构建结果。所以哪怕你本地 conan create 成功,只要下游 consumer 的 settings/options/依赖图稍有不同,就可能触发全新构建 —— 这不是 bug,是设计使然。


















