Yocto项目入门——自定义Linux发行版的构建流程
文章目录

每日一句正能量
为人处世,既需有宏大视野,也要有躬身入局的担当。
大局观让你不陷于琐碎,能看清方向;但光想不做、袖手旁观,再大的格局也是空中楼阁。真正成熟的人,既能仰望星空,也能撸起袖子解决具体问题。
一、引言:为什么需要Yocto
嵌入式Linux开发面临一个经典困境:厂商提供的SDK往往基于某个特定版本的Linux内核和根文件系统,当需要更换内核版本、添加新驱动、裁剪不需要的软件包、或者为不同硬件平台构建镜像时,开发者常常陷入"手动修改-反复编译-兼容性调试"的泥潭。
Yocto Project 提供了一套完整的解决方案:通过声明式的元数据(Metadata)描述整个Linux系统的构建过程,从源码下载、补丁应用、配置编译到镜像生成,全部自动化、可复现、可定制。
对于 HarmonyOS 生态中的设备(如 RK3568、HiSilicon 等芯片平台),Yocto 是构建底层 Linux 内核和根文件系统的标准工具。掌握 Yocto,意味着掌握从源码到镜像的完整控制权。
二、Yocto项目架构全景
2.1 核心组件

Yocto Project 的核心组件包括:
| 组件 | 职责 | 类比 |
|---|---|---|
| BitBake | 任务调度引擎,解析元数据、构建依赖树、调度执行 | Makefile 的"超级升级版" |
| OpenEmbedded (OE) | 构建系统的核心框架,提供基础类、任务、变量 | 构建系统的"操作系统" |
| Poky | Yocto 的参考发行版,包含 OE-Core + meta-poky + meta-yocto-bsp | 参考实现模板 |
| Layer | 元数据的集合,按功能/硬件/厂商分层组织 | 代码的"模块化包" |
| Recipe | 描述如何构建单个软件包的配方文件 | 软件包的"构建脚本" |
构建流程:
源码层 (Linux/U-Boot/应用源码)
↓
BitBake 解析 Recipe → 构建依赖树 → 计算任务哈希
↓
检查共享状态缓存 (sstate-cache)
↓
并行执行构建任务 (fetch → unpack → patch → configure → compile → install → package)
↓
输出产物 (内核镜像 / 根文件系统 / Bootloader / SDK / 部署镜像)
2.2 与Buildroot的对比

| 维度 | 手动构建 | Buildroot | Yocto Project |
|---|---|---|---|
| 灵活性 | 低 | 中 | 极高(Layer可叠加、可覆盖) |
| 可维护性 | 极低 | 中 | 高(元数据版本化管理) |
| 构建速度 | 中 | 快 | 慢(首次),快(增量+sstate) |
| 包管理 | 无 | 基础 | 完善(RPM/DEB/IPK) |
| 可复用性 | 无 | 低 | 高(Layer跨项目共享) |
| 学习曲线 | 低 | 中 | 高 |
选择建议:
- 快速原型验证、简单系统 → Buildroot
- 产品化、多平台、长期维护、需要包管理 → Yocto Project
三、Layer:Yocto的分层架构
3.1 Layer层次结构

Layer 是 Yocto 最核心的设计概念。它将构建元数据按功能、硬件、厂商分层组织,高优先级的 Layer 可以覆盖低优先级 Layer 中的配置。
标准Layer层次(从底到顶):
| 层级 | 典型Layer | 作用 |
|---|---|---|
| OE-Core | meta |
OpenEmbedded核心,提供基础类、通用Recipe |
| Poky参考 | meta-poky |
Yocto参考发行版配置 |
| BSP层 | meta-ti, meta-freescale, meta-rockchip |
板级支持包,特定芯片的Kernel/Bootloader配置 |
| 中间件层 | meta-qt, meta-python, meta-networking |
特定功能域的软件包集合 |
| 用户自定义 | meta-custom, meta-<product> |
产品-specific配置、自定义Recipe、镜像定义 |
优先级规则:
bblayers.conf中后出现的 Layer 优先级更高- 高优先级 Layer 的 Recipe 可以覆盖低优先级同名 Recipe
BBFILE_PRIORITY变量显式控制优先级
# bblayers.conf - Layer注册与优先级配置
BBLAYERS ?= " \
/home/yocto/poky/meta \
/home/yocto/poky/meta-poky \
/home/yocto/poky/meta-yocto-bsp \
/home/yocto/meta-openembedded/meta-oe \
/home/yocto/meta-openembedded/meta-python \
/home/yocto/meta-rockchip \
/home/yocto/meta-harmonyos \
/home/yocto/meta-custom \
"
3.2 Layer目录结构
meta-custom/ # Layer根目录
├── conf/ # 配置目录
│ ├── layer.conf # Layer配置文件(必需)
│ ├── distro/ # 发行版配置
│ │ └── custom-distro.conf
│ └── machine/ # 机器配置(BSP)
│ └── custom-machine.conf
├── recipes-core/ # 核心软件包
│ ├── base-files/
│ │ └── base-files_%.bbappend
│ └── images/
│ └── custom-image.bb
├── recipes-example/ # 示例Recipe
│ └── hello/
│ ├── hello_1.0.bb
│ └── files/
│ ├── main.c
│ └── Makefile
├── recipes-kernel/ # 内核相关
│ └── linux/
│ └── linux-yocto_%.bbappend
├── recipes-bsp/ # BSP相关
│ └── u-boot/
│ └── u-boot_%.bbappend
├── classes/ # 自定义BitBake类
│ └── custom-image.bbclass
└── README # 说明文档
四、Recipe:软件包的构建配方
4.1 Recipe结构详解

Recipe 是 Yocto 中最基础的构建单元,以 .bb(BitBake)为后缀。一个完整的 Recipe 描述了如何从源码构建一个软件包。
# hello_1.0.bb - 示例Recipe
# ============================================================
# 元数据头
# ============================================================
SUMMARY = "Hello World Example Application"
DESCRIPTION = "A simple hello world program demonstrating Yocto recipe creation"
HOMEPAGE = "https://example.com/hello"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://COPYING;md5=bb5c5b6b7c5b5c5b5c5b5c5b5c5b5c5b"
# ============================================================
# 源码获取
# ============================================================
SRC_URI = "git://github.com/example/hello.git;protocol=https;branch=main"
SRCREV = "v1.0.0"
# 或从本地文件
# SRC_URI = "file://${TOPDIR}/../sources/hello-1.0.tar.gz"
# 源码解压后的工作目录
S = "${WORKDIR}/git"
# ============================================================
# 依赖声明
# ============================================================
# 构建时依赖(编译时需要)
DEPENDS = "zlib openssl"
# 运行时依赖(安装时需要)
RDEPENDS:${PN} = "libc6"
# 可选功能配置
PACKAGECONFIG[ssl] = "--with-ssl,--without-ssl,openssl"
PACKAGECONFIG[zlib] = "--with-zlib,--without-zlib,zlib"
# ============================================================
# 构建任务
# ============================================================
# 配置任务(继承自autotools类时通常不需要重写)
do_configure() {
# 自定义配置
./configure --prefix=${prefix} \
--sysconfdir=${sysconfdir} \
--localstatedir=${localstatedir} \
${@bb.utils.contains('PACKAGECONFIG', 'ssl', '--with-ssl', '--without-ssl', d)}
}
# 编译任务
do_compile() {
oe_runmake -j${BB_NUMBER_THREADS}
}
# 安装任务
do_install() {
# 安装到临时目录${D},后续打包
install -d ${D}${bindir}
install -m 0755 ${B}/hello ${D}${bindir}/
# 安装配置文件
install -d ${D}${sysconfdir}
install -m 0644 ${S}/hello.conf ${D}${sysconfdir}/
# 安装systemd服务
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${S}/hello.service ${D}${systemd_system_unitdir}/
}
# ============================================================
# 包分割
# ============================================================
# 将构建产物分割为多个二进制包
PACKAGES =+ "${PN}-config ${PN}-systemd"
FILES:${PN}-config = "${sysconfdir}/hello.conf"
FILES:${PN}-systemd = "${systemd_system_unitdir}/hello.service"
# ============================================================
# 系统服务配置
# ============================================================
inherit systemd
SYSTEMD_SERVICE:${PN} = "hello.service"
SYSTEMD_AUTO_ENABLE = "enable" # 开机自启
4.2 任务执行顺序
BitBake 将 Recipe 中的指令分解为一系列任务,按依赖顺序执行:
do_fetch → 下载源码(支持git/http/ftp/local)
do_unpack → 解压源码到WORKDIR
do_patch → 应用补丁(.patch/.diff文件)
do_configure → 运行配置脚本(./configure/cmake/meson)
do_compile → 编译源码
do_install → 安装到临时目录${D}
do_package → 将安装内容分割为二进制包
do_package_write → 生成RPM/DEB/IPK包
do_populate_sysroot → 将头文件/库导出到sysroot供其他Recipe使用
do_build → 默认任务,触发上述所有任务
关键变量:
${WORKDIR}:Recipe的工作目录(如/home/yocto/build/tmp/work/cortexa53-poky-linux/hello/1.0-r0)${S}:源码目录(解压后的位置)${B}:构建目录(可能与${S}分离,支持out-of-tree构建)${D}:安装目标目录(临时根文件系统)${PN}:包名(Recipe文件名去掉版本号)${PV}:包版本号${PR}:包修订号
五、BitBake:任务调度引擎
5.1 依赖解析与调度

BitBake 的核心能力在于依赖解析和并行调度:
1. 解析阶段:
- 读取
bblayers.conf获取所有 Layer - 遍历所有
.bb和.bbappend文件 - 解析变量、继承类、任务定义
- 构建全局依赖图
2. 依赖树构建:
# 查看包的依赖树
$ bitbake -g core-image-minimal
# 生成pn-buildlist和task-depends.dot
# 查看特定Recipe的依赖
$ bitbake -e hello | grep ^DEPENDS=
$ bitbake -e hello | grep ^RDEPENDS=
3. 任务哈希与共享缓存:
BitBake 使用 sstate-cache(Shared State Cache)避免重复构建:
- 每个任务计算输入哈希(源码、配置、依赖)
- 如果哈希匹配缓存,直接复用上次构建结果
- 首次构建慢,增量构建极快
# 配置共享缓存(团队共享)
SSTATE_DIR ?= "/mnt/sstate-cache"
DL_DIR ?= "/mnt/downloads"
# 使用本地镜像加速下载
SOURCE_MIRROR_URL ?= "http://internal-mirror.company.com/yocto/"
INHERIT += "own-mirrors"
4. 并行构建:
# local.conf - 并行配置
BB_NUMBER_THREADS = "16" # BitBake任务并行数(建议CPU核心数)
PARALLEL_MAKE = "-j 16" # make编译并行数
BB_NUMBER_PARSE_THREADS = "8" # 解析阶段并行数
5.2 常用BitBake命令
# 初始化构建环境(每次新终端执行)
$ source poky/oe-init-build-env build-rk3568
# 构建完整镜像
$ bitbake core-image-minimal
$ bitbake core-image-sato # 带图形界面
$ bitbake custom-image
# 构建单个包
$ bitbake hello
$ bitbake -c clean hello # 清理构建
$ bitbake -c rebuild hello # 强制重新构建
# 查看可用镜像
$ bitbake-layers show-images
# 查看Layer信息
$ bitbake-layers show-layers
$ bitbake-layers show-recipes
$ bitbake-layers show-appends
# 查看包依赖
$ bitbake -g hello
$ dot -Tpng task-depends.dot -o depends.png
# 生成SDK
$ bitbake custom-image -c populate_sdk
# 进入开发shell(用于调试)
$ bitbake -c devshell hello
六、自定义Layer创建实战
6.1 从零创建自定义Layer

步骤1:创建Layer目录结构
# 进入Yocto项目目录
cd ~/yocto/poky
# 使用脚本创建Layer骨架
bitbake-layers create-layer meta-harmonyos-rk3568
# 或手动创建
mkdir -p meta-harmonyos-rk3568/{conf,recipes-example,recipes-core,recipes-kernel,recipes-bsp}
步骤2:编写Layer配置文件
# meta-harmonyos-rk3568/conf/layer.conf
# 必须包含的基本配置
# Layer路径加入BBPATH
BBPATH .= ":${LAYERDIR}"
# Recipe文件搜索路径
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
# Layer集合名称
BBFILE_COLLECTIONS += "harmonyos-rk3568"
# 文件匹配模式(用于确定哪些Recipe属于本Layer)
BBFILE_PATTERN_harmonyos-rk3568 = "^${LAYERDIR}/"
# Layer优先级(数值越大优先级越高)
BBFILE_PRIORITY_harmonyos-rk3568 = "10"
# Layer依赖(本Layer依赖的其他Layer)
LAYERDEPENDS_harmonyos-rk3568 = "core rockchip"
# 兼容的Yocto版本
LAYERSERIES_COMPAT_harmonyos-rk3568 = "scarthgap"
# Layer版本
LAYER_VERSION_harmonyos-rk3568 = "1"
步骤3:编写自定义Recipe
# meta-harmonyos-rk3568/recipes-core/images/harmonyos-image.bb
# 自定义镜像Recipe
SUMMARY = "HarmonyOS Device Image for RK3568"
DESCRIPTION = "Custom Linux image for HarmonyOS ecosystem devices based on RK3568"
LICENSE = "MIT"
# 继承core-image类
inherit core-image
# 镜像特性
IMAGE_FEATURES += " \
ssh-server-openssh \
package-management \
hwclock \
splash \
"
# 安装到镜像的软件包
IMAGE_INSTALL += " \
kernel-modules \
linux-firmware \
usbutils \
pciutils \
i2c-tools \
spi-tools \
ethtool \
iproute2 \
iw \
wpa-supplicant \
bluez5 \
alsa-utils \
v4l-utils \
libdrm \
mesa \
harmonyos-distributed-service \
harmonyos-softbus \
"
# 根文件系统大小(MB)
IMAGE_ROOTFS_SIZE ?= "8192"
# 额外分区配置
IMAGE_FSTYPES += " wic wic.bmap ext4"
# 预装配置文件
ROOTFS_POSTPROCESS_COMMAND += "harmonyos_postprocess;"
harmonyos_postprocess() {
# 设置主机名
echo "harmonyos-device" > ${IMAGE_ROOTFS}/etc/hostname
# 配置网络
install -d ${IMAGE_ROOTFS}/etc/systemd/network
cat > ${IMAGE_ROOTFS}/etc/systemd/network/20-wired.network <<EOF
[Match]
Name=eth0
[Network]
DHCP=yes
EOF
# 创建HarmonyOS数据目录
install -d ${IMAGE_ROOTFS}/data/harmonyos
install -d ${IMAGE_ROOTFS}/data/harmonyos/logs
install -d ${IMAGE_ROOTFS}/data/harmonyos/config
}
步骤4:注册Layer
# 编辑build/conf/bblayers.conf,添加新Layer
BBLAYERS ?= " \
${TOPDIR}/../poky/meta \
${TOPDIR}/../poky/meta-poky \
${TOPDIR}/../poky/meta-yocto-bsp \
${TOPDIR}/../meta-openembedded/meta-oe \
${TOPDIR}/../meta-openembedded/meta-python \
${TOPDIR}/../meta-openembedded/meta-networking \
${TOPDIR}/../meta-rockchip \
${TOPDIR}/../meta-harmonyos-rk3568 \
"
# 或使用命令行添加
$ bitbake-layers add-layer ../meta-harmonyos-rk3568
步骤5:构建验证
# 构建自定义镜像
$ bitbake harmonyos-image
# 构建输出目录
# build/tmp/deploy/images/rk3568-evb/
# ├── harmonyos-image-rk3568-evb.wic # 可直接烧录的镜像
# ├── harmonyos-image-rk3568-evb.ext4 # 根文件系统
# ├── zImage # 内核镜像
# ├── rk3568-evb.dtb # 设备树
# └── u-boot.itb # Bootloader
# 烧录到SD卡
$ sudo dd if=harmonyos-image-rk3568-evb.wic of=/dev/sdX bs=4M status=progress
七、BSP适配:为RK3568添加板级支持
7.1 机器配置
# meta-harmonyos-rk3568/conf/machine/harmonyos-rk3568.conf
# RK3568机器配置
# 继承Rockchip基础配置
require conf/machine/include/rk3568.inc
# 机器名称
MACHINE = "harmonyos-rk3568"
# 内核配置
PREFERRED_PROVIDER_virtual/kernel = "linux-rockchip"
KCONFIG_MODE = "--alldefconfig"
KBUILD_DEFCONFIG = "rockchip_defconfig"
# 设备树
KERNEL_DEVICETREE = "rockchip/rk3568-evb.dtb"
# Bootloader
PREFERRED_PROVIDER_virtual/bootloader = "u-boot-rockchip"
UBOOT_MACHINE = "rk3568_defconfig"
# 串口配置
SERIAL_CONSOLES = "1500000;ttyS2"
# 分区配置
IMAGE_BOOT_FILES = "zImage rk3568-evb.dtb"
WKS_FILE = "harmonyos-rk3568.wks"
# GPU/显示配置
MACHINE_FEATURES += " gpu mali-g52"
MACHINE_FEATURES += " display hdmi lvds"
7.2 内核Recipe扩展
# meta-harmonyos-rk3568/recipes-kernel/linux/linux-rockchip_%.bbappend
# 扩展Rockchip内核,添加HarmonyOS分布式支持
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
# 添加HarmonyOS分布式补丁
SRC_URI += " \
file://0001-harmonyos-add-distributed-softbus.patch \
file://0002-harmonyos-enable-binder-ipc.patch \
file://0003-harmonyos-optimize-scheduler-for-iot.patch \
file://harmonyos.config \
"
# 合并内核配置
do_configure:append() {
${S}/scripts/kconfig/merge_config.sh \
${B}/.config \
${WORKDIR}/harmonyos.config
}
# 添加内核模块
KERNEL_MODULE_AUTOLOAD += " \
harmonyos_softbus \
harmonyos_distributed_sched \
"
八、高级技巧与最佳实践
8.1 镜像定制技巧
# 通过bbappend修改现有Recipe
# meta-harmonyos-rk3568/recipes-core/busybox/busybox_%.bbappend
# 添加自定义busybox配置
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
SRC_URI += "file://harmonyos-busybox.cfg"
# 启用特定功能
CONFIG_FEATURES:append = " \
CONFIG_FEATURE_FANCY_ECHO=y \
CONFIG_FEATURE_EDITING=y \
CONFIG_FEATURE_TAB_COMPLETION=y \
"
8.2 包管理配置
# local.conf - 包管理配置
# 包格式选择(RPM/DEB/IPK)
PACKAGE_CLASSES ?= "package_rpm"
# 生成包feed(用于OTA更新)
PACKAGE_FEED_URIS = "http://update.harmonyos.com/rpm"
PACKAGE_FEED_BASE_PATHS = "rpm"
PACKAGE_FEED_ARCHS = "cortexa53"
# 生成SBOM(软件物料清单)
INHERIT += "create-spdx"
SPDX_INCLUDE_SOURCES = "1"
8.3 SDK生成与使用
# 生成SDK(包含交叉编译器、sysroot、环境脚本)
$ bitbake harmonyos-image -c populate_sdk
# 输出
# build/tmp/deploy/sdk/poky-glibc-x86_64-harmonyos-image-cortexa53-rk3568-toolchain-5.0.sh
# 安装SDK
$ ./poky-glibc-x86_64-harmonyos-image-cortexa53-rk3568-toolchain-5.0.sh -d /opt/harmonyos-sdk
# 使用SDK
$ source /opt/harmonyos-sdk/environment-setup-cortexa53-poky-linux
$ $CC hello.c -o hello # 使用交叉编译器
$ $STRIP hello # 使用strip工具
九、常见问题与排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 构建失败:checksum mismatch | 源码下载不完整或被修改 | 删除DL_DIR中的文件,重新下载 |
| 任务被跳过(setscene) | sstate-cache命中 | 使用 bitbake -c cleansstate <recipe> 清除 |
| 找不到Recipe | Layer未注册或路径错误 | 检查 bblayers.conf 和 BBFILES |
| 依赖循环 | Recipe间循环依赖 | 使用 bitbake -g 查看依赖图,打破循环 |
| 磁盘空间不足 | tmp目录过大 | 清理 tmp/ 或配置 TMPDIR 到大容量分区 |
| 编译器错误 | 交叉编译环境配置错误 | 检查 CC/LD/CFLAGS 等环境变量 |
十、总结
Yocto Project 是构建自定义嵌入式Linux发行版的工业标准工具。通过本文的学习,我们掌握了:
- Layer分层架构:从OE-Core到用户自定义Layer的优先级叠加机制
- Recipe编写:从元数据头到构建任务的完整配方
- BitBake调度:依赖解析、任务哈希、共享缓存、并行构建
- BSP适配:为RK3568等芯片平台添加板级支持
- 镜像定制:从core-image继承,添加HarmonyOS分布式服务
- SDK生成:为应用开发提供标准化交叉编译环境
在 HarmonyOS 生态中,Yocto 是构建底层 Linux 基础设施的基石。掌握 Yocto,意味着从"使用厂商SDK"跃升到"掌控整个系统栈",这是每一位嵌入式Linux工程师的必修课。
转载自:https://blog.csdn.net/u014727709/article/details/162639719
欢迎 👍点赞✍评论⭐收藏,欢迎指正
更多推荐



所有评论(0)