3. 理解Poky与BitBake:Poky参考发行版结构、BitBake任务执行引擎、元数据(Metadata)与配方(Recipe)概念
好,咱们进入第三章。这一章是Yocto的“内功心法”。
很多新手刚开始接触Yocto,第一反应就是:“这玩意儿怎么这么复杂?目录一堆,文件一堆,到底谁管谁?”
我当年也一样。第一次运行 bitbake core-image-minimal 的时候,看着屏幕上刷刷刷滚过的日志,心里完全没底。它到底在干什么?那些 .bb 文件和 .conf 文件是什么关系?
说白了,Yocto的核心就三样东西:Poky(参考发行版)、BitBake(执行引擎)、元数据(配方和配置)。搞懂这三样,你就拿到了Yocto的钥匙。
3.1 Poky:Yocto项目的“样板间”
Poky是什么?它不是Yocto本身,而是Yocto项目官方维护的一个参考发行版。你可以把它理解成装修公司的“样板间”——展示了Yocto能怎么用,也提供了最基础的构建环境。
我习惯把Poky看作一个“大杂烩”仓库。它里面包含了:
- BitBake:任务执行引擎,后面细说。
- OpenEmbedded-Core (OE-Core):核心元数据层,提供最基础的配方和类。
- meta-yocto-bsp:针对特定硬件(比如QEMU模拟器)的板级支持包。
- meta-poky:Poky发行版本身的配置和策略。
你从Yocto官网下载的 poky-*.tar.bz2,解压后就是这个结构。我刚开始做项目时,直接拿Poky当根目录,在里面加自己的层。后来项目多了,才发现这样很乱。现在我的习惯是:Poky只作为上游,不动它。自己的东西全部放在独立的层里。
看一下Poky的顶层目录结构,你就明白了:
poky/
├── bitbake/ # BitBake引擎源码
├── meta/ # OE-Core核心层
├── meta-poky/ # Poky发行版配置
├── meta-yocto-bsp/ # 参考BSP层
├── oe-init-build-env # 环境初始化脚本
└── README.hardware # 硬件支持说明
嗯,这里要注意:meta/ 目录就是OE-Core,它是所有层的“地基”。你写的任何配方,几乎都会继承 meta/classes/ 下的 .bbclass 文件。
3.2 BitBake:Yocto的“心脏”
BitBake是一个任务执行引擎。你可以把它想象成一个“超级Makefile解释器”。但它比Make强大得多——它懂得依赖关系、懂得缓存、懂得并行执行。
我举个例子。你执行 bitbake core-image-minimal,BitBake会做以下几件事:
- 解析元数据:读取所有 .bb 和 .conf 文件,构建依赖树。
- 计算任务:每个配方都有多个任务(fetch、extract、configure、compile、install、package等)。BitBake会算出哪些任务需要执行,哪些可以跳过(因为缓存命中)。
- 调度执行:根据依赖关系,并行执行任务。默认是CPU核心数+2个线程。
- 输出结果:生成IPK/DEB/RPM包、根文件系统、内核镜像等。
BitBake的核心概念是任务(Task)。每个任务都有一个名字,比如 do_fetch、do_compile。任务之间可以设置依赖:
# 在配方中,让do_configure依赖于do_patch
do_configure[depends] += "virtual/kernel:do_populate_sysroot"
我曾经踩过一个坑:自己写了一个配方,编译时总报头文件找不到。查了半天,发现是 do_configure 没有正确依赖 do_populate_sysroot。BitBake不会自动帮你推断所有依赖,你得显式告诉它。
bitbake -c listtasks <recipe> 查看一个配方有哪些任务。用 bitbake -g <recipe> 可以生成依赖图(dot格式),用Graphviz打开一目了然。
3.3 元数据(Metadata)与配方(Recipe)
元数据是Yocto的灵魂。它是一堆描述“怎么构建软件”的文件。元数据主要分三类:
| 类型 | 文件后缀 | 作用 | 例子 |
|---|---|---|---|
| 配方(Recipe) | .bb | 描述一个软件的构建方法 | zlib_1.2.11.bb |
| 附加配方(Append) | .bbappend | 扩展或覆盖已有配方 | zlib_%.bbappend |
| 配置文件 | .conf | 设置全局变量和策略 | local.conf, bblayers.conf |
| 类文件 | .bbclass | 可复用的构建逻辑 | autotools.bbclass |
| 包含文件 | .inc | 配方间共享的代码片段 | glibc.inc |
一个典型的配方长什么样?我拿 zlib 举例:
# zlib_1.2.11.bb
SUMMARY = "Zlib Compression Library"
HOMEPAGE = "http://zlib.net/"
SECTION = "libs"
LICENSE = "Zlib"
LIC_FILES_CHKSUM = "file://zlib.h;beginline=4;endline=23;md5=..."
SRC_URI = "http://zlib.net/${BP}.tar.gz"
SRC_URI[md5sum] = "1c9f62f0778697a09d36121ead88e08e"
inherit autotools
do_install_append() {
# 我自己加的:删除静态库以节省空间
rm -f ${D}${libdir}/libz.a
}
你看,配方就是一组变量的赋值和函数的定义。inherit autotools 这行很关键——它把 autotools.bbclass 里的几十个函数全部拉进来了,包括 do_configure、do_compile、do_install 的默认实现。
我个人的经验是:写配方时,尽量继承已有的类。除非你要构建的东西非常特殊,否则 autotools、cmake、makefile 这些类已经覆盖了90%的场景。自己手写 do_compile 很容易出问题。
zlib_1.2.11.bb 中的 1.2.11 是版本号。BitBake会根据版本号自动选择最新版本。如果你有多个版本,可以用 PREFERRED_VERSION 指定。我曾经因为文件名写错版本号(比如 zlib_1.2.11.bb 写成 zlib_1.2.11.bb 但内部PV设成1.2.10),导致构建用了旧版本,排查了半天。
3.4 知识结构图:Poky、BitBake、元数据的关系
下面这张图,我画出了这三者的关系。你看完应该能理解Yocto的整体架构。
这张图你看懂了吗?从上到下:
- Poky 是一个大容器,把 BitBake、OE-Core、BSP 层打包在一起。
- BitBake 是引擎,负责读取元数据并执行任务。
- 元数据 分散在 OE-Core、BSP 层和你自己的层里。BitBake 把它们全部解析,构建出完整的依赖图。
- 最终输出你需要的镜像、包、内核等。
你想想看,如果没有Poky这个“样板间”,你从零开始搭环境得多累?Poky帮你把最基础的东西都配好了。你只需要在 bblayers.conf 里加上自己的层,在 local.conf 里改改目标机器和特性,就能开始构建。
- Poky = BitBake + OE-Core + 参考BSP + 发行版配置
- BitBake 是任务调度器,核心是“任务”和“依赖”
- 元数据是“怎么做”的描述,配方是元数据的主体
- 写配方时优先继承 .bbclass,不要重复造轮子