第6课:Package / Dependency / RDEPENDS
摘要:本课深入讲解 Yocto 的 Package 机制,包括 Recipe 如何拆分为多个 Package、Package 的生成流程、FILES 变量分配文件到不同 Package,以及编译时依赖(DEPENDS)和运行时依赖(RDEPENDS)的区别与作用。通过 hello-script 实验,你将亲身体验 RDEPENDS 如何自动安装运行时依赖到最终镜像,并掌握 packages-split 目录分析和 oe-pkgdata-util 工具的使用。
这是 Yocto 学习路线里非常重要的一课。到目前为止,你已经掌握了:
1.Layer (meta-myproduct)
2.Recipe (.bb)
3.do_install
4.systemd service
5.image recipe (myproduct-image)
6.将文件安装到 rootfs
接下来学习 Package / Dependency / RDEPENDS,就是理解 Yocto 如何把一个 recipe 拆成
package,以及 package 之间如何自动安装依赖。
一、为什么需要 Package?
假设你写了一个 recipe: hello.bb
里面安装了: /usr/bin/hello
编译结束以后,并不是直接进入 image。
Yocto 会先产生 package。
例如: hello
然后 image 再决定是否安装这个 package。
整个流程
Recipe ▼ do_install ▼${D}▼ do_package ▼ ipk/rpm/deb package ▼ Image所以: .bb ↓ Package ↓ Image这一层就是今天要学的。
二、Package 长什么样?
例如: hello.bb
编译后: tmp/deploy/ipk/
会看到: hello_1.0-r0.ipk
或者
tmp/deploy/rpm/ hello-1.0.rpm或者
tmp/deploy/deb/ hello_1.0.debYocto 默认由: PACKAGE_CLASSES
决定。
例如: PACKAGE_CLASSES = “package_ipk”
三、一个 recipe 可以产生多个 package
这是 Yocto 最大特色之一。
例如: nano.bb
最后可能产生
nanonano-doc nano-dev nano-dbg nano-staticdev为什么?
因为:
程序 nano 放一起。
文档 /usr/share/doc 放一起。
头文件: /usr/include 放一起。
调试信息: .debug 放一起。
所以:
一个 Recipe ≠ 一个 Package
而是
Recipe ↓ 多个 Package四、PACKAGES
例如
PACKAGES="\${PN}\${PN}-dev \${PN}-dbg \ "其中: PN 就是 package name。
例如
hello.bbPN=hello所以: ${PN} 就是 hello
五、FILES
FILES 决定: 哪些文件属于哪个 package。
例如: FILES:${PN} += “/usr/bin/hello”
表示: hello package 包含 /usr/bin/hello
例如: FILES:${PN}-dev += “/usr/include” 表示 hello-dev 包含 /usr/include
六、什么是 Dependency?
有两种:
Build Dependency 和 Runtime Dependency
这是初学者最容易混淆的地方。
Build Dependency
使用 DEPENDS
例如: DEPENDS += “openssl”
意思:
编译 hello 之前 先编译 openssl注意:
这是编译机上的依赖。
不是 rootfs。
Runtime Dependency
使用 RDEPENDS
例如: RDEPENDS:${PN} += “bash”
表示运行 hello
需要 bash。
因此 image 会自动安装 bash。
所以:
DEPENDS 影响 bitbake 编译顺序而
RDEPENDS
影响 image 安装哪些 package
七、举例理解
例如 : myscript.sh
内容:
#!/bin/bashechohellorecipe
SRC_URI+="file://myscript.sh"do_install(){install-d ${D}${bindir}install-m755${WORKDIR}/myscript.sh \ ${D}${bindir}/myscript}如果 image 没有 bash
运行: myscript
会看到
/bin/bash
No such file
因此需要: RDEPENDS:${PN} += “bash”
这样 image 自动安装 bash
八、查看 package 包含哪些文件
编译结束以后:tmp/work/…/packages-split/
例如
packages-split/ hello/ usr/bin/hello hello-dev/ usr/include hello-dbg/ usr/lib/.debug这是理解 package 最好的目录。
建议以后经常看。
九、查看 package 依赖
可以查看 pkgdata:oe-pkgdata-util list-pkg-files hello
查看 package 文件:oe-pkgdata-util find-path /usr/bin/hello
查看 package 运行依赖:oe-pkgdata-util read-value RDEPENDS hello
这些命令对分析依赖非常有帮助。
十、与你当前学习最相关的实践
建议不要直接学习复杂的软件包,而是继续使用你已经建立的 meta-myproduct 学习环境,做两个最小实验:
实验 1:体验 RDEPENDS
- 新建一个 hello-script.bb,安装一个#!/bin/bash 的脚本。- 先不写 RDEPENDS:${PN}+="bash",观察运行失败。 - 再加入 RDEPENDS,重新构建 image,验证bash会自动进入 rootfs。实验 2:体验多个 Package
在同一个 recipe 中安装一个程序和一个 README。
将 README 放入 ${PN}-doc,程序放入 ${PN}。
查看 tmp/work/…/packages-split/,确认生成了两个 package,并观察各自包含的文件。
推荐掌握顺序
按照你目前的 Yocto 学习路线,我建议按下面的顺序继续:
1.Package 的生成流程(Recipe → Package → Image)
2.FILES:PN的作用3.DEPENDS(编译时依赖)4.RDEPENDS:{PN} 的作用 3.DEPENDS(编译时依赖) 4.RDEPENDS:PN的作用3.DEPENDS(编译时依赖)4.RDEPENDS:{PN}(运行时依赖)
5.packages-split 目录分析
6.oe-pkgdata-util 分析 package 和依赖
学完这一课后,你就具备了阅读大多数官方 .bb 文件的基础,下一步可以学习 Packagegroup(软件包集合),以及
IMAGE_INSTALL 与 packagegroup 的关系,这也是构建完整产品镜像时最常用的机制。
实验1
最终希望验证:
hello-script.bb │ ├── 安装 /usr/bin/hello-script │ ├── 不写 RDEPENDS │ │ │ └── 运行失败(找不到 bash) │ └── 加入 RDEPENDS:${PN}+="bash"│ └── image 自动安装bashStep 1 建立 recipe 目录
进入你的 layer: cd ~/yocto/meta-myproduct
创建 recipe: mkdir -p recipes-test/hello-script/files
确认目录: find recipes-test
应该看到
recipes-test/ └── hello-script └── filesStep 2 编写脚本
进入: cd recipes-test/hello-script/files
建立: nano hello-script
内容:
#!/bin/bashecho"Hello from Bash!"echo"Shell =$0"保存。
然后赋予可执行权限(方便本地测试,Yocto 安装时也会设置权限):chmod +x hello-script
验证:cat hello-script
应该看到
#!/bin/bashecho"Hello from Bash!"echo"Shell =$0"Step 3 编写 hello-script.bb
回到 recipe : cd …
建立recipe: nano hello-script.bb
内容:
SUMMARY="Simple bash script for RDEPENDS demo"LICENSE="MIT"SRC_URI="file://hello-script"S="${WORKDIR}"do_install(){install-d ${D}${bindir}install-m0755${WORKDIR}/hello-script ${D}${bindir}/hello-script}#RDEPENDS:${PN}+="bash"注意:
这里故意不写
RDEPENDS:${PN}+="bash"因为我们要先观察失败。
Step 4 把 package 加进 image
打开: meta-myproduct/recipes-core/images/myproduct-image.bb
找到: IMAGE_INSTALL += “…”
加入: IMAGE_INSTALL += “hello-script”
例如
IMAGE_INSTALL+="\ hello-script \ "到这里暂停
现在你的 layer 应该像这样:
meta-myproduct/ recipes-test/ └── hello-script ├── hello-script.bb └── files └── hello-script以及
recipes-core/images/ └── myproduct-image.bb里面已经有: IMAGE_INSTALL += “hello-script”
Step 5 编译 hello-script
先不要编整个 image。
先验证 recipe 是否正确。
执行:
cd ~/yocto/build-rpi2
bitbake hello-script
如果成功,会看到类似:
NOTE: Tasks Summary:
Attempted xxx tasks
没有 ERROR 即表示 recipe 正常。
Step 6 检查 package 是否产生
执行:find tmp/deploy -name “hello-script”
应该能看到类似:
tmp/deploy/ipk/... hello-script_1.0-r0_cortexa7t2hf-neon-vfpv4.ipk(如果你的 PACKAGE_CLASSES 是 rpm 或 deb,则扩展名会不同。)
Step 7 查看 packages-split
这是今天最重要的一步。
执行:find tmp/work -path “hello-script” -path “packages-split” -type d
应该会得到类似:…/hello-script/1.0/packages-split
进入:
cd<刚才输出的packages-split目录>tree你应该看到类似:
packages-split └── hello-script └── usr └── bin └── hello-script这一步非常关键,因为它说明:
Recipe ↓ do_install ↓${D}↓ do_package ↓ packages-split ↓ ipk你已经亲眼看到 Recipe 被拆成 Package 了。
Step 8 查看 Package 内容
再执行:oe-pkgdata-util list-pkg-files hello-script
如果成功,会列出类似:
hello-script:
/usr/bin/hello-script
这说明 package 中确实只有这个脚本。
暂时不要编 image
我们现在的目标是先确认:
✅ recipe 能编译
✅ package 已生成
✅ package 里面包含 /usr/bin/hello-script
确认这些以后,再进入第二阶段:
验证没有 RDEPENDS 时,为什么运行会失败。
我们已经验证了什么?
① Recipe 编译成功 ✅
bitbake hello-script没有任何 ERROR,说明:
- hello-script.bb 正确 - do_install()正确 - package 成功生成② RPM package 已生成 ✅
你这里看到:tmp/deploy/rpm/…/hello-script-1.0-r0…rpm
说明你的系统使用的是: PACKAGE_CLASSES = “package_rpm”
所以生成的是 RPM,而不是 IPK。
③ package 已拆分成功 ✅
你看到:
packages-split/ hello-script hello-script-dev hello-script-doc hello-script-dbg…
这正是 Yocto 的标准行为。
虽然你的 recipe 很简单,-dev、-doc 等 package 基本是空的,但 Yocto 仍然会创建这些 package。
这说明:
Recipe ↓ do_install ↓${D}↓ do_package ↓ packages-split你已经亲眼看到了。
④ package 中确实有脚本 ✅
你执行:oe-pkgdata-util list-pkg-files hello-script
得到: /usr/bin/hello.sh
这里我要指出一个小细节。
我们确认 package 没问题后,就开始真正的 RDEPENDS 实验。
现在进入真正的 RDEPENDS 实验
终于到了今天最重要的部分。
Step 9 构建 image
重新编译 image:
cd~/yocto/build-rpi2 bitbake myproduct-image成功以后,烧录 SD 卡。
Step 10 在 Raspberry Pi 上验证
启动 Pi。
先确认脚本存在:which hello.sh
或者: ls -l /usr/bin/hello.sh
应该能看到:/usr/bin/hello.sh
然后运行:hello.sh
或者: /usr/bin/hello.sh
Step 11 观察结果
我们这次故意没有写
RDEPENDS:${PN} += “bash”
因此,要验证一个问题:
Image 里面有没有 bash?
先执行:which bash
再执行:ls -l /bin/bash
然后运行:hello.sh
有两种可能
情况 A(我们希望看到)
/bin/bash: No suchfileor directory这说明:
Image 里没有 bash。
脚本因为 shebang
#!/bin/bash无法启动。
这就是为什么需要
RDEPENDS:${PN}+="bash"情况 B
如果运行成功,例如:
Hello from Bash!说明:
你的 image 已经因为其它 package 安装了 bash。
这种情况也很常见,例如:
openssh或者某些 packagegroup 已经依赖 bash。
那我们就不能证明 RDEPENDS 的作用。
不过不用担心,我们仍然可以验证:
hello-script │ └── 是否声明bash为运行依赖并通过修改 recipe 观察依赖变化。
下一步
请先完成:bitbake myproduct-image
烧录并启动 Raspberry Pi,然后把下面四个命令的输出贴给我:
whichbashls-l/bin/bashwhichhello.sh hello.sh根据结果,我们再进行 添加 RDEPENDS:${PN} += “bash” 前后的对比实验。这是理解 RDEPENDS 的关键一步。