在这里插入图片描述

每日一句正能量

为人处世,既需有宏大视野,也要有躬身入局的担当。
大局观让你不陷于琐碎,能看清方向;但光想不做、袖手旁观,再大的格局也是空中楼阁。真正成熟的人,既能仰望星空,也能撸起袖子解决具体问题。


一、引言:为什么需要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.confBBFILES
依赖循环 Recipe间循环依赖 使用 bitbake -g 查看依赖图,打破循环
磁盘空间不足 tmp目录过大 清理 tmp/ 或配置 TMPDIR 到大容量分区
编译器错误 交叉编译环境配置错误 检查 CC/LD/CFLAGS 等环境变量

十、总结

Yocto Project 是构建自定义嵌入式Linux发行版的工业标准工具。通过本文的学习,我们掌握了:

  1. Layer分层架构:从OE-Core到用户自定义Layer的优先级叠加机制
  2. Recipe编写:从元数据头到构建任务的完整配方
  3. BitBake调度:依赖解析、任务哈希、共享缓存、并行构建
  4. BSP适配:为RK3568等芯片平台添加板级支持
  5. 镜像定制:从core-image继承,添加HarmonyOS分布式服务
  6. SDK生成:为应用开发提供标准化交叉编译环境

在 HarmonyOS 生态中,Yocto 是构建底层 Linux 基础设施的基石。掌握 Yocto,意味着从"使用厂商SDK"跃升到"掌控整个系统栈",这是每一位嵌入式Linux工程师的必修课。


转载自:https://blog.csdn.net/u014727709/article/details/162639719
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐