4. Recipe 编写入门:从零开始构建你的第一个配方

Recipe,说白了就是 Yocto 的“菜谱”。它告诉 BitBake 从哪里下载源码、怎么打补丁、怎么编译、最后装到哪。我刚开始接触 Yocto 时,觉得这东西很神秘。后来写了几十个 recipe 才发现,核心套路就那么几个。

今天咱们就把 recipe 的骨架拆开,一个一个看明白。

4.1 Recipe 文件结构:一个 recipe 长什么样

一个典型的 recipe 文件,后缀是 .bb。它本质上是一个 元数据文件,用 BitBake 的语法写成。我习惯把它分成三个部分:

  • 头部:描述信息,比如名字、版本、许可证
  • 源码区:告诉系统去哪拿代码
  • 任务区:定义构建和安装的步骤

来看一个最简单的例子:

# 头部
SUMMARY = "Hello World 示例程序"
DESCRIPTION = "一个简单的 C 程序,用于演示 recipe 结构"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=xxxx"

# 源码区
SRC_URI = "file://hello.c"

# 任务区
do_compile() {
    ${CC} ${CFLAGS} ${LDFLAGS} hello.c -o hello
}

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

嗯,这里要注意:${CC}、${CFLAGS} 这些变量是 Yocto 自动帮你设好的,你不用自己写死编译器路径。

4.2 LICENSE 与 LIC_FILES_CHKSUM:许可证不是小事

Yocto 对许可证检查非常严格。如果你不写 LICENSE,BitBake 会直接报错。我见过不少新手在这栽跟头。

LICENSE 变量声明软件使用什么许可证。常见的有:

许可证 说明 典型场景
MIT 宽松许可证 自己写的小工具
GPL-2.0-only GNU 通用公共许可证 v2 Linux 内核、BusyBox
BSD-2-Clause BSD 2 条款许可证 一些网络库
Proprietary 专有许可证 商业软件、闭源驱动

LIC_FILES_CHKSUM 是校验文件。Yocto 会去检查你指定的许可证文件,计算它的 MD5 值。如果文件内容变了,构建就会失败。为什么要这样?

我曾经在一个项目里,上游悄悄改了许可证条款,从 MIT 改成了 GPL。如果没有这个校验,我们可能就把 GPL 代码当成 MIT 代码发布出去了——那可就惹上法律麻烦了。

注意: 如果你用的是 file:// 指向本地文件,路径是相对于 recipe 所在目录的。别写成绝对路径。

4.3 SRC_URI 详解:源码从哪里来

SRC_URI 是 recipe 里最灵活的变量之一。它告诉 BitBake 去哪里拿源码。支持的协议非常多:

  • file:// — 本地文件,放在 recipe 同目录或 files/ 子目录
  • http:// / https:// — 从网络下载
  • git:// — 从 Git 仓库拉取
  • svn:// — 从 SVN 仓库拉取
  • ftp:// — 从 FTP 服务器下载

来看几个实际例子:

# 从 GitHub 下载特定版本的 tar 包
SRC_URI = "https://github.com/example/libfoo/archive/v1.2.3.tar.gz"

# 从 Git 仓库拉取,指定分支和提交
SRC_URI = "git://github.com/example/libfoo.git;branch=master;protocol=https"

# 混合使用:从网络下载主程序,本地补丁
SRC_URI = "https://example.com/app-1.0.tar.gz \
           file://fix-build.patch \
           file://config.cfg"

这里有个坑:用 git:// 协议时,记得加上 protocol=https。很多公司防火墙会拦截 git 协议,但放行 HTTPS。我踩过这个坑,排查了半天才发现是网络策略问题。

4.4 do_fetch / do_unpack / do_patch:源码准备三步走

这三个任务是 Yocto 构建流程的前三步。它们按顺序执行:

  1. do_fetch — 下载源码
  2. do_unpack — 解压源码
  3. do_patch — 打补丁

咱们一个一个看。

do_fetch:把源码拿回来

BitBake 会根据 SRC_URI 里的协议,自动选择对应的 fetcher 模块。下载的文件会放在 ${DL_DIR} 目录下(通常是 downloads/)。

我个人习惯在 local.conf 里设置 DL_DIR 指向一个共享目录。这样多个项目可以共用下载好的源码,省带宽也省时间。

do_unpack:解压到工作目录

解压后的文件会放到 ${WORKDIR} 下。对于 tar 包,BitBake 会自动识别格式并解压。对于 Git 仓库,它会执行 git checkout。

如果你需要自定义解压方式,可以重写 do_unpack:

do_unpack_append() {
    # 解压后执行一些额外操作
    chmod +x ${S}/scripts/*.sh
}

do_patch:给源码打补丁

Yocto 会自动应用 SRC_URI 里所有 .patch 或 .diff 文件。补丁文件通常放在 recipe 目录的 files/ 子目录下。

补丁的命名我建议遵循这个格式:

数字-描述.patch

比如:

0001-fix-cross-compile.patch
0002-add-missing-header.patch

数字越小,越先应用。这样可以避免补丁之间的依赖问题。

小技巧: 用 devtool modify 可以自动帮你生成补丁。你修改源码后,devtool finish 会把改动导出为 patch 文件。省去手动 diff 的麻烦。

4.5 do_configure / do_compile / do_install:构建三剑客

这三个任务是 recipe 的核心。它们定义了软件怎么配置、怎么编译、怎么安装。

do_configure:配置构建环境

对于 autotools 项目,默认会执行 ./configure。对于 CMake 项目,会执行 cmake。如果你用的是 Makefile 项目,默认什么都不做。

我经常需要给 configure 传一些自定义参数:

EXTRA_OECONF = "--enable-feature-x --disable-debug"

对于 CMake 项目,用 EXTRA_OECMAKE:

EXTRA_OECMAKE = "-DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release"

do_compile:真正开始编译

默认行为是执行 make。如果你的项目需要特殊参数:

EXTRA_OEMAKE = "V=1"

如果你需要完全自定义编译过程,直接重写 do_compile:

do_compile() {
    oe_runmake -C ${S}/src
}

oe_runmake 是 Yocto 封装的 make 命令,它会自动带上交叉编译的工具链参数。

do_install:把文件装到目标目录

这是最容易出错的地方。记住:do_install 是把文件安装到 ${D} 目录(即 ${WORKDIR}/image),而不是你的开发机根目录。

do_install() {
    # 创建目标目录
    install -d ${D}${bindir}
    install -d ${D}${sysconfdir}

    # 安装二进制文件
    install -m 0755 ${S}/src/myapp ${D}${bindir}/

    # 安装配置文件
    install -m 0644 ${S}/config/myapp.conf ${D}${sysconfdir}/
}
常见错误: 有人直接写 make install DESTDIR=/usr,结果把文件装到了开发机的 /usr 下。一定要用 ${D} 前缀。

4.6 知识体系总览

下面这张图把 recipe 的完整流程串起来了。我建议你保存下来,写 recipe 时对照着看:

Recipe 构建流程总览 Recipe 元数据 源码准备阶段:do_fetch → do_unpack → do_patch 构建阶段:do_configure → do_compile 安装阶段:do_install 打包阶段:do_package / do_populate_sysroot 核心变量:LICENSE / LIC_FILES_CHKSUM / SRC_URI / S / WORKDIR / D 每个阶段都可以通过 _append / _prepend 或重写函数来自定义

4.7 实战建议:从零开始写一个 recipe

如果你现在要写第一个 recipe,我建议按这个步骤来:

  1. 先抄一个现成的 — 在 meta/recipes-* 目录里找一个类似的 recipe,复制过来改
  2. 只改必要字段 — SUMMARY、LICENSE、SRC_URI 这三个必须改
  3. 用 bitbake -c fetch 测试下载 — 先确认源码能拿到
  4. 用 bitbake -c compile 测试编译 — 确认能编译通过
  5. 用 bitbake -c install 测试安装 — 确认文件装到了正确位置

我刚开始写 recipe 时,经常一次性跑完整构建,结果报错了也不知道是哪一步的问题。后来学乖了,一步一测,效率反而更高。

核心要点回顾:
  • Recipe 是 .bb 文件,包含头部、源码区、任务区
  • LICENSE 和 LIC_FILES_CHKSUM 必须配对使用
  • SRC_URI 支持多种协议,Git 仓库记得加 protocol=https
  • do_fetch / do_unpack / do_patch 是源码准备三步
  • do_configure / do_compile / do_install 是构建三剑客
  • 安装时一定要用 ${D} 前缀,别装到开发机根目录