虚拟配方与提供者:PROVIDES与RPROVIDES机制
包依赖冲突,说白了就是两个配方都想提供同一个东西。比如 libjpeg 和 libjpeg-turbo,都声称自己能提供 JPEG 解码功能。这时候系统就懵了——到底该用哪个?
Yocto 的解决方案很巧妙:虚拟配方。它不直接解决冲突,而是让冲突变得「不冲突」。我刚开始接触这个机制时也觉得绕,但用顺手了就会发现,这其实是嵌入式构建系统里最优雅的设计之一。
PROVIDES:给配方起个「艺名」
每个配方都有一个默认的「本名」,也就是 PN(Package Name)。但有时候,一个配方可以干好几件事。比如 libjpeg-turbo 这个包,它不仅能提供 libjpeg-turbo,还能提供标准的 libjpeg 接口。
这时候就用 PROVIDES 给它加个「艺名」:
# libjpeg-turbo_2.1.5.bb
PROVIDES += "libjpeg"
这样一来,任何依赖 libjpeg 的配方,都能自动匹配到 libjpeg-turbo。系统会认为「libjpeg 这个功能,libjpeg-turbo 也能干」。
RPROVIDES:运行时也认这个「艺名」
PROVIDES 解决的是构建时的依赖。但包装到目标系统上后,运行时依赖怎么处理?比如你的应用在运行时需要 libjpeg.so,而 libjpeg-turbo 提供的是 libturbojpeg.so,名字对不上。
这时候需要 RPROVIDES:
# libjpeg-turbo_2.1.5.bb
RPROVIDES:${PN} += "libjpeg"
嗯,这里要注意:RPROVIDES 通常配合 RREPLACES 和 RCONFLICTS 一起用。我遇到过只写了 RPROVIDES 没写 RREPLACES,结果安装时两个包打架的情况。
| 变量 | 作用域 | 典型用途 |
|---|---|---|
| PROVIDES | 构建时 | 让其他配方能依赖这个虚拟名 |
| RPROVIDES | 运行时 | 让包管理器识别这个虚拟名 |
| RREPLACES | 运行时 | 替换掉旧包 |
| RCONFLICTS | 运行时 | 声明不能和哪些包共存 |
使用虚拟配方解决命名冲突
虚拟配方本身不产生任何文件。它就是一个「占位符」,告诉系统:某个功能由谁提供。
举个例子。我有一个项目,需要 cryptodev 驱动。但内核里有两个实现:一个是内核主线自带的,一个是第三方模块。两个都叫 cryptodev,直接冲突。
我的做法是创建一个虚拟配方:
# virtual/cryptodev.bb
SUMMARY = "Virtual package for cryptodev"
LICENSE = "MIT"
PROVIDES = "virtual/cryptodev"
然后让两个实现配方都 PROVIDES += "virtual/cryptodev"。其他配方依赖时写 DEPENDS = "virtual/cryptodev"。这样系统就知道:只要有人提供 virtual/cryptodev 就行,具体是谁不重要。
virtual/ 开头。这不是强制要求,但已经是社区约定俗成的规范。我个人建议你也这么用,方便别人一眼识别。
PREFERRED_PROVIDER:指定谁才是「正主」
当多个配方都 PROVIDES 同一个虚拟名时,系统会随机选一个。这显然不行。你需要用 PREFERRED_PROVIDER 来指定优先级。
在 local.conf 或 distro.conf 中配置:
PREFERRED_PROVIDER_virtual/cryptodev = "cryptodev-module"
PREFERRED_PROVIDER_virtual/jpeg = "libjpeg-turbo"
语法是 PREFERRED_PROVIDER_虚拟名 = "实际配方名"。注意这里的虚拟名要写全,包括 virtual/ 前缀。
实战:解决 libjpeg 与 libjpeg-turbo 的冲突
这是嵌入式 Linux 里最经典的冲突场景。很多老代码依赖 libjpeg,但新项目都用 libjpeg-turbo(性能更好)。
完整配置如下:
# libjpeg-turbo_2.1.5.bb
PROVIDES = "libjpeg"
RPROVIDES:${PN} = "libjpeg"
RREPLACES:${PN} = "libjpeg"
RCONFLICTS:${PN} = "libjpeg"
然后在 local.conf 中:
PREFERRED_PROVIDER_libjpeg = "libjpeg-turbo"
这样所有依赖 libjpeg 的配方,都会自动链接到 libjpeg-turbo。而且安装时,libjpeg-turbo 会直接替换掉 libjpeg 的包,不会出现两个包共存的情况。
虚拟配方的核心逻辑
我画了一张图,帮你理清整个流程:
你想想看,这个机制本质上就是「面向接口编程」在包管理上的体现。应用配方只关心「我需要 libjpeg」,不关心具体实现。具体用哪个,由 PREFERRED_PROVIDER 在构建时决定。
这样做的好处很明显:
- 解耦:应用配方不需要修改,就能切换底层实现
- 灵活:不同产品线可以选不同的提供者
- 可维护:新增一个实现,不需要改任何依赖方
嗯,虚拟配方这块内容其实不难,但容易忽略细节。尤其是 RPROVIDES 和 PREFERRED_PROVIDER 的配置位置,搞错了就会出各种奇怪的问题。记住一句话:PROVIDES 在配方里写,PREFERRED_PROVIDER 在配置里写,基本就不会出大错。