3、理解Yocto目录结构:Poky目录解析、meta层结构、conf目录、recipes与bb文件、layer.conf的作用

说实话,我刚接触Yocto那会儿,第一反应就是——这目录结构也太复杂了吧?

Poky、meta层、conf、recipes、bb文件……一堆名词砸过来,很容易让人懵掉。但你别怕,这些东西说白了就是一套「搭积木」的规则。你搞懂了目录结构,就等于拿到了Yocto的地图。

今天我就带你把这地图上的关键节点,一个一个捋清楚。

3.1 Poky目录解析

Poky是什么?它是Yocto项目的参考构建系统。你可以把它理解成一个「样板间」——里面啥都有,但你要自己动手改。

我习惯把Poky目录比作一个工具箱。你打开它,会看到这些核心目录:

  • bitbake/ —— 构建引擎。Yocto的灵魂,所有构建任务都靠它调度。
  • meta/ —— 核心元数据层。包含最基础的recipes、类和配置。
  • meta-poky/ —— Poky的发行版配置层。定义了默认的发行版策略。
  • meta-yocto-bsp/ —— 参考板级支持包。给几个常见硬件平台用的。
  • scripts/ —— 辅助脚本。比如检查环境、生成镜像的工具。
  • build/ —— 构建输出目录。你执行bitbake后,所有产物都在这里。

嗯,这里要注意:build/目录默认是不存在的。你需要先运行oe-init-build-env脚本,它才会被创建出来。我第一次用的时候找了半天build目录,结果发现是自己忘了source环境……

我的小习惯:每次开始新项目,我都会把Poky目录单独clone一份,而不是复用旧的。这样能避免不同项目之间的配置污染。

3.2 meta层结构

meta层,说白了就是「一摞菜谱」。每个meta层都包含一组recipes、配置文件和类文件。Yocto的层机制,让你可以像搭积木一样组合功能。

一个典型的meta层长这样:

meta-mylayer/
├── conf/
│   ├── layer.conf
│   └── machine/
│       └── mymachine.conf
├── recipes-example/
│   └── hello/
│       ├── hello_1.0.bb
│       └── files/
│           └── hello.c
├── classes/
│   └── myclass.bbclass
└── COPYING.MIT

你看,结构其实很清晰:

  • conf/ —— 层配置和机器配置
  • recipes-xxx/ —— 按功能分类的菜谱目录
  • classes/ —— 可复用的构建类
  • files/ —— 补丁、源码等附加文件

我在项目中遇到过一个问题:两个meta层都定义了同一个recipe,结果构建时老是报冲突。后来才发现,Yocto是按照BBLAYERS中层的顺序来解析的,后面的层会覆盖前面的。所以层的顺序很重要,千万别乱排。

3.3 conf目录

conf目录是Yocto的「控制面板」。你所有的构建决策,几乎都在这里配置。

核心文件就这几个:

文件 作用 我常用的配置
local.conf 本地构建配置,每个开发者自己改 MACHINE、DL_DIR、SSTATE_DIR
bblayers.conf 声明要使用的meta层列表 BBLAYERS += "path/to/mylayer"
templateconf.cfg 模板配置,很少动 默认就好

举个例子,local.conf里我经常这样配:

MACHINE = "qemuarm64"
DL_DIR = "/home/shared/downloads"
SSTATE_DIR = "/home/shared/sstate-cache"
PACKAGE_CLASSES = "package_rpm"

你想想看,DL_DIRSSTATE_DIR如果放在网络共享路径上,团队里每个人都能复用下载好的源码和编译缓存。我第一次用Yocto时没配这个,结果每次clean后都要重新下载,那叫一个慢啊……

避坑指南:我曾经把DL_DIR指向了一个NFS挂载点,结果网络一抖,下载就失败。后来我改成本地SSD + 定时同步,稳多了。

3.4 recipes与bb文件

recipes是Yocto的核心。每个.bb文件就是一个recipe,它告诉BitBake:你要从哪里下载源码、怎么编译、怎么安装。

一个简单的hello_1.0.bb长这样:

SUMMARY = "A simple hello world program"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://hello.c"

S = "${WORKDIR}"

do_compile() {
    ${CC} ${CFLAGS} ${LDFLAGS} hello.c -o hello
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 hello ${D}${bindir}
}

你看,recipe里定义了:

  • SUMMARY —— 一句话描述
  • LICENSE —— 许可证,Yocto强制要求
  • SRC_URI —— 源码来源,可以是本地文件、git仓库、tarball
  • do_compile —— 编译步骤,你可以完全自定义
  • do_install —— 安装步骤,把产物放到目标目录

我个人习惯把SRC_URI写成git://形式,这样能精确锁定commit。比如:

SRC_URI = "git://github.com/example/myapp.git;branch=main;protocol=https"
SRCREV = "a1b2c3d4e5f6..."

为什么推荐用git?因为你可以随时回退版本,而且SRCREV能保证构建的可重复性。我曾经用tarball方式,结果上游把旧版本删了,构建直接挂掉……从那以后,我尽量用git。

3.5 layer.conf的作用

每个meta层都必须有一个conf/layer.conf文件。它就像层的「身份证」,告诉BitBake这个层叫什么、包含哪些内容。

一个典型的layer.conf

BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
            ${LAYERDIR}/recipes-*/*/*.bbappend"

BBFILE_COLLECTIONS += "mylayer"
BBFILE_PATTERN_mylayer = "^${LAYERDIR}/"
BBFILE_PRIORITY_mylayer = "6"

LAYERDEPENDS_mylayer = "core"
LAYERSERIES_COMPAT_mylayer = "kirkstone"

这里的关键变量:

  • BBPATH —— 把层目录加入搜索路径
  • BBFILES —— 声明哪些文件是recipe
  • BBFILE_COLLECTIONS —— 注册层名称
  • BBFILE_PATTERN —— 匹配该层下的文件
  • BBFILE_PRIORITY —— 优先级,数字越大越优先
  • LAYERDEPENDS —— 依赖的其他层
  • LAYERSERIES_COMPAT —— 兼容的Yocto版本

嗯,这里有个坑:BBFILE_PRIORITY的默认值是5。如果你有两个层定义了同一个recipe,优先级高的会覆盖优先级低的。我遇到过一个问题:一个第三方层优先级设成了10,结果把我的自定义recipe全盖了,查了半天才发现是优先级搞的鬼。

总结一下:layer.conf就是层的「自我介绍」。你写的时候,记得把LAYERSERIES_COMPAT填对,不然BitBake会直接报错,不让你用。

好了,目录结构这块就聊到这儿。你把这些搞懂了,Yocto的骨架就搭起来了。下一章,我们聊聊怎么用BitBake跑第一个构建任务——到时候你会发现,理解了目录结构,跑起来顺手多了。