3 Yocto Project 概念
本章解释操作指南和参考资料背后的 Yocto Project 概念,包括组件、 OpenEmbedded Build System 流程、交叉开发工具链、共享状态缓存等。
备注
本章介绍许多变量及其含义。要在 Build Directory 上下文中查看 任意变量值,可用 bitbake-getvar,见 “查看变量值”。
3 Yocto Project 组件
BitBake 任务执行器和各类配置文件构成 OpenEmbedded-Core (OE-Core)。本节概述这些组件的用途及交互。
BitBake 解析并执行数据文件,数据包括:
配方: 描述具体软件。
类数据: 抽象公共构建信息,例如如何构建 Linux 内核。
配置数据: 定义机器设置、策略等,将各部分连接起来。
BitBake 能组合多个数据源,每个数据源称为层。层介绍见 “理解和创建层”。
下面简述核心组件。构建时的交互详见 “OpenEmbedded 构建系统概念”。
3.1.1 BitBake
BitBake 是构建系统核心,负责解析 Metadata、生成任务列表并执行。 这里仅作简介,更多信息见 BitBake 用户手册。
查看支持的选项:
$ bitbake -h
$ bitbake --help
最常用形式为 bitbake recipename,recipename 是要构建的配方,即目标。
通常对应文件名第一部分,例如 foo_1.3.0.bb 的 foo。处理
matchbox-desktop_1.2.3.bb 可运行:
$ bitbake matchbox-desktop
可能存在多个 matchbox-desktop 版本,BitBake 按发行版配置选择。不同版本 和提供者的选择规则见 “Preferences”。
BitBake 也会先执行依赖任务。例如交叉编译器和 glibc 尚未构建时,会先构建 它们,再构建 matchbox-desktop。
-k 或 --continue 可让构建在出错后尽可能继续。失败目标及依赖它的
目标不能重建,但其他依赖仍可处理。
3 配方
.bb 文件称为配方,通常描述一个软件,包括原始源码下载位置、需要的补丁、 特殊配置选项、编译方式及输出打包方式。
有时也用 package 指代配方,但本手册将“软件包”专用于 .ipk、.deb、.rpm 等打包输出,避免混淆。
3 类
.bbclass 保存配方间可共享的信息。例如 autotools* 包含使用 GNU Autotools 构建应用的通用设置。 详情见参考手册的 “类”。
3 配置
配置文件 (.conf)定义控制构建过程的变量, 涵盖机器、发行版、编译器调优、通用配置,以及构建目录中 build/conf/local.conf 的用户设置。
3 层
层是包含相关元数据指令的仓库,指导系统构建目标。 Yocto Project 层模型 促进协作、 共享、定制和复用。层按逻辑隔离信息,例如将特定硬件配置放在独立层,使其他 可跨硬件共用的元数据放在另一层中共享。
现有层很多,可以从 Yocto Project 兼容层索引 和 OpenEmbedded 层索引 中选取或借鉴。
层按惯例遵循特定结构,使 BitBake 知道到哪里查找各类元数据。创建流程和 bitbake-layers 等工具见 “理解和创建层”。
3 OpenEmbedded 构建系统概念
本节深入介绍项目所用构建系统内部过程,其核心是任务执行器 BitBake。
下图展示高级流程,后文分别展开输入、输出、处理过程和元数据逻辑模块。
流程包括:
用户配置: 用户用来控制构建的元数据。
元数据层: 提供软件、机器和发行版元数据的各层。
源文件: 上游发行包、本地项目和 SCM。
构建系统: BitBake 控制的过程,包括获取、打补丁、编译、分析输出、 创建并测试软件包、生成镜像和交叉开发工具。
软件包源: 保存 RPM、DEB 或 IPK 输出包的目录,供构建镜像或 SDK。 若目标启用运行时包管理,也可通过 Web 等方式复制共享这些目录,扩展或 更新已部署镜像。
镜像: 流程生成的镜像。
应用开发 SDK: 随镜像一起或由 BitBake 单独生成的交叉开发工具。
3 用户配置
用户配置定义目标架构、源码下载位置及其他构建属性。
下图展开 总体流程 中的 User Configuration:
BitBake 需要基础 .conf 文件完成构建。最少所需文件的示例位于构建目录 conf 子目录。
初始化环境 时, 可以指定 Source Directory。
若构建目录不存在,初始化会创建它。BitBake 的全部构建工作在其中进行。 conf 子目录包含默认 build/conf/local.conf 和 build/conf/bblayers.conf。仅当加载设置脚本时这些文件尚不 存在,才会生成默认版本。
配置文件提供定义环境的基础变量。可配置项示例见 OE-Core 的 local.conf.sample。
以下列表并不穷尽:
目标机器: MACHINE。
下载目录: DL_DIR。
共享状态目录: SSTATE_DIR。
持久数据目录: PERSISTENT_DIR。
构建输出: TMPDIR。
发行版策略: DISTRO。
打包格式: PACKAGE_CLASSES。
SDK 目标架构: SDKMACHINE。
额外镜像软件包: EXTRA_IMAGE_FEATURES。
备注
local.conf 中的设置也可以放在 build/conf/site.conf 和 build/conf/auto.conf。
bblayers.conf 告诉 BitBake 使用哪些层,默认包含构建系统的最小必需层。 自定义层需要手动加入,见 “启用层”。
初始化脚本不创建 site.conf 和 auto.conf。需要时自行创建 auto.conf, 它通常由自动构建器生成:
site.conf: 可为多个构建目录设置公共默认值,例如用 PACKAGE_CLASSES 统一软件包格式。
auto.conf: 通常由自动构建器写入,内容类似 local.conf 或 site.conf。
可以编辑所有配置文件,进一步定义具体环境,图中对应 User Configuration Edits。
运行 bitbake target 时,BitBake 汇总配置。读取顺序固定为 site.conf、
auto.conf、local.conf,并应用 BitBake 用户手册
“Syntax and Operators” 的普通赋值规则。
因此顺序会影响同一变量。例如 auto.conf 和 local.conf 分别赋不同值,后读
的 local.conf 最终生效。
3 元数据、机器配置和策略配置
上一节介绍全局用户配置,本节进一步看提供软件、机器和策略元数据的层。
总体流程图 中,用户配置下面有三类输入:
元数据(.bb 和补丁): 软件层包含用户配方、补丁和追加文件。例如 层索引 中的 meta-qt5,支持桌面、嵌入式和移动跨平台框架 Qt 5.0。
机器 BSP 配置: 提供特定硬件和目标架构配置。Poky 的示例是 meta-yocto-bsp,参阅 参考嵌入式发行版(Poky)。
策略配置: 发行版层为该发行版镜像和 SDK 提供高层通用策略。Poky 使用 meta-poky,conf/distro 中的 poky.conf 包含许多策略。
下面展开这三类层:
层结构一般相似:分发时需要许可证文件,例如 COPYING.MIT;推荐提供 README, 尤其是要分发时;还包含配置目录和配方目录。结构见 “创建自己的层”,层及可用资源见 “层” 和 “Yocto Project 层模型”。
上述链接列出许多可用层,源码仓库 的 Yocto Metadata Layers 分组也提供这些内容。
备注
源码仓库中部分层不在 OpenEmbedded 层索引,通常因为已弃用或仍属实验性质。
BitBake 通过用户配置 bblayers.conf 确定要使用的层。
3 发行版层
发行版层提供策略配置,推荐独立隔离。conf/distro/distro.conf 中的设置 会覆盖构建目录 local.conf 中同类设置。
典型内容如下,meta-poky 就是此类层:
classes、classes-global、classes-recipe: .bbclass 保存配方共享功能。 配方继承后获得其设置和函数,见 “类”。
conf: 保存层配置 conf/layer.conf、发行版配置 conf/distro/distro.conf 及发行版范围的包含文件。
recipes-*: 影响整个发行版公共功能的配方和追加文件,例如专用配置、 初始化脚本、定制镜像。目录例子有 recipes-core、recipes-extra,内部 层级可变化,通常含 .bb、.bbappend 和发行版专用配置目录等。
3 BSP 层
BSP 层提供面向具体硬件的机器配置,所有内容都与目标机器有关。通用结构见 Yocto Project 板级支持包(BSP)手册。
备注
要符合 Yocto Project 兼容要求,BSP 层必须满足一些结构要求。
conf 包含机器配置 conf/machine/machine.conf 以及层配置 conf/layer.conf。
其他部分按功能分为 recipes-bsp、recipes-core、recipes-graphics、 recipes-kernel 等,可包含多种外形规格、图形支持系统的元数据。
备注
图中虽有多个 recipes-*,并非每个 BSP 都有全部目录。
3 软件层
软件层提供额外软件包的元数据,不包含应放在各自层中的发行版或机器专用信息。 它包含项目需要的配方、追加文件和补丁。
3 源码
生成镜像或其他目标需要访问源码。总体流程图的 Upstream Project Releases、 Local Projects、SCMs (optional) 表示来源,Source Materials 表示也参与 定位源码的镜像源。
源码如何组织由项目决定。已发布软件通常用归档文件固定发行状态;动态或实验 项目可能放在 Git 等 SCM 仓库,允许选择具体修订。也可结合两种方式。
无论来源如何,每个配方都通过 SRC_URI 指向源码。
DL_DIR 是已下载源码缓存。还可通过 BB_GENERATE_MIRROR_TARBALLS 让系统为 Git 仓库生成归档并保存在这里, 这不是默认行为。
合理使用下载缓存可避免重复联网。建议将 DL_DIR 放在构建目录之外,这样 删除构建目录也不会误删下载内容。
下面进一步展示来源和镜像源:
3 上游项目发行包
发行包以 tarball、zip 等归档存在于任意位置,与各配方对应。图中展示 BusyBox、 Qt 和 Dbus 的特定版本,实际上任何可通过配方构建的发行产品都可采用这种形式。
3 本地项目
本地项目是用户提供的自定义软件,位于项目本地某处,例如团队检入开发源码树 的目录。
标准方式是使用 externalsrc,在 local.conf 或追加文件 中设置或覆盖配方,使其从指定本地目录获取源码。
3 源码管理系统(可选)
构建系统也可通过支持 Git、Subversion 等 SCM 的 Fetchers 克隆或检出 仓库。do_fetch 根据 SRC_URI 及前缀选择正确模块。
备注
Git 仓库归档并保存在 DL_DIR 的方法见 BB_GENERATE_MIRROR_TARBALLS。
获取仓库时,BitBake 用 SRCREV 确定具体构建修订。
3 源码镜像源
有前置镜像和普通镜像两类,分别由 PREMIRRORS、MIRRORS 指定。 BitBake 在访问上游前检查前置镜像,适用于 DL_DIR 之外的共享目录,通常是 组织内部的位置。
普通镜像可以是互联网任意站点,在主站不可用时作为替代源码位置。
3 软件包源
生成镜像或 SDK 时,构建系统从构建目录中的软件包源区域取包,在总体流程图 右上角表示。
下面展开该区域:
软件包源是构建中间阶段。系统提供生成不同包类型的类,通过 PACKAGE_CLASSES 选择。在放入软件包源前,构建系统会验证包生成过程 中的输出质量。
这些质量检查由 insane 类完成。
软件包源位于构建目录,具体暂存位置由变量和包管理器共同决定。图中右侧所列 关键变量为:
DEPLOY_DIR:构建目录中的 tmp/deploy。
DEPLOY_DIR_*:按包管理器选择类型子目录,RPM、IPK、DEB 分别使用 DEPLOY_DIR_RPM、DEPLOY_DIR_IPK、DEPLOY_DIR_DEB。PACKAGE_ARCH:架构子目录,例如 i586 或 qemux86。
do_package_write_* 生成并放置软件包, 例如 IPK 使用 do_package_write_ipk。详情见 do_package_write_deb、 do_package_write_ipk、do_package_write_rpm。 若使用 IPK 且支持 i586 与 qemux86,则分别放到 build/tmp/deploy/ipk/i586 和 build/tmp/deploy/ipk/qemux86。
3 BitBake 工具
构建系统用 BitBake 生成镜像和 SDK。总体流程图的 BitBake 区域包含多个功能 部分,下面逐一展开。
备注
BitBake 有独立文档,参阅 BitBake 用户手册。
3 获取源码
配方构建首先获取并解包源码:
do_fetch 和 do_unpack 将文件获取并解包到构建目录。
备注
对 SRC_URI 中每个 file:// 本地文件,系统计算校验和并加入 fetch 任务 签名。任一本地文件变化,fetch 及全部依赖它的任务都会重新执行。
默认全部工作在具有固定结构的构建目录进行,详见 build/。
每个配方都有解包源码区域,由 UNPACKDIR 指向,默认名称 sources。 图示层级中的变量为:
TMPDIR:构建全部工作的基础目录,默认为 tmp。
PACKAGE_ARCH:输出包架构,随目的地而异,可能是目标架构、构建 主机、SDK 或具体机器,详见变量说明。
TARGET_OS:目标操作系统,通常为 linux,例如 qemux86-poky-linux。
PN:生成包的配方名称。它在其他上下文可能有不同含义,但在输入 文件上下文中指配方名。
WORKDIR:构建配方、创建软件包的工作位置。
PV:配方版本。
UNPACKDIR:该配方解包后的源文件。
S:源码最终位置。
BP 默认为
${BPN}-${PV},其中:
备注
图中分别展示基于 PACKAGE_ARCH 和 MACHINE 的两种层级,底层结构相同, 区别在于构建目标是通用架构、主机、SDK 还是具体机器。
3 打补丁
获取并解包后,BitBake 查找补丁并应用到源码:
do_patch 结合 SRC_URI 和 FILESPATH 定位适用补丁。
默认识别 *.patch、*.diff 及其压缩形式,也可通过 SRC_URI 参数
改变识别方式,详见该任务文档。
同一配方多个补丁按找到的顺序应用。FILESPATH 定义默认搜索目录,找到后 应用到 S 中的源码。
源码目录创建见 “获取源码”。补丁 创建和处理见 “修补代码”,也可参阅 “使用 devtool modify 修改已有组件源码” 及 “使用传统内核开发流程修补内核”。
3 配置、编译和暂存
打补丁后,执行配置和编译,再将文件复制到暂存区域,准备打包:
涉及任务如下:
do_prepare_recipe_sysroot:在 WORKDIR 设置两个系统根目录, 使后续 configure、compile 等任务能访问依赖配方生成的库、头文件等。
recipe-sysroot:目标库、头文件及交叉构建需要的其他数据。
recipe-sysroot-native:主机原生可执行程序、库和数据,供构建时直接运行。
do_configure:启用或禁用软件构建配置选项,来源可为配方、 继承类,软件本身也可能按目标自动配置。
此任务处理的是配方所构建源码的配置。
使用 autotools* 时,可通过 EXTRA_OECONF 或 PACKAGECONFIG_CONFARGS 增加选项,类实现见 此处。
do_compile:配置完成后编译,在 B 指定目录中进行。 默认 B 与 S 相同。
do_install:编译完成后,从 B 复制文件到 D 暂存区, 后续打包使用这些文件。
3 拆分软件包
源码配置、编译和暂存后,分析结果并拆包:
do_package 和 do_packagedata 分析 D 中的文件, 按可用软件包和文件规则拆分。分析包括拆出调试符号、检查共享库依赖和包关系等。
packagedata 根据分析生成元数据,供创建最终软件包。 do_populate_sysroot 则把安装文件的子集复制到相应系统根目录。 工作、暂存及中间结果使用以下区域:
PKGD:拆包前的目的目录,即 package。
PKGDESTWORK:package 任务保存包元数据的临时区域,即 pkgdata。
PKGDEST:拆包后各包的父目录,即 packages-split。
PKGDATA_DIR:保存打包元数据的共享全局目录。内容从 PKGDESTWORK 复制到这里,供全局使用。
STAGING_DIR_HOST:组件预期运行系统的系统根目录,即 recipe-sysroot。
STAGING_DIR_NATIVE:构建主机组件所用系统根目录,即 recipe-sysroot-native。
STAGING_DIR_TARGET:组件在一个系统运行、为另一个机器生成代码时 使用的系统根目录,例如 cross-canadian 配方。
配方输出包列在 PACKAGES, bitbake.conf 默认定义:
PACKAGES = "${PN}-src ${PN}-dbg ${PN}-staticdev ${PN}-dev ${PN}-doc ${PN}-locale ${PACKAGE_BEFORE_PN} ${PN}"
各包有 FILES 定义的默认文件列表。例如 ${PN}-dev 包含开发依赖该 软件的应用所需文件,其默认列表也定义在 bitbake.conf:
FILES:${PN}-dev = "${includedir} ${FILES_SOLIBSDEV} ${libdir}/*.la \
${libdir}/*.o ${libdir}/pkgconfig ${datadir}/pkgconfig \
${datadir}/aclocal ${base_libdir}/*.o \
${libdir}/${BPN}/*.la ${base_libdir}/*.la \
${libdir}/cmake ${datadir}/cmake"
这些路径必须是从目标根文件系统看待的 绝对路径,不能引用 D 或 WORKDIR 相关变量。正确示例:
${sysconfdir}/foo.conf
备注
包文件列表使用覆盖语法,以冒号分隔 FILES 和包名。
每个文件只能进入一个包。系统从左到右遍历 PACKAGES,文件进入第一个 FILES 模式匹配的包。
备注
可用 oe-pkgdata-util 查找文件属于哪个包:
$ oe-pkgdata-util find-path '/etc/fstab'
base-files: /etc/fstab
更多信息见 使用 oe-pkgdata-util 查看软件包信息。
要增加 ${PN}-extra 这类任意命名的变体,可加入 PACKAGE_BEFORE_PN:
PACKAGE_BEFORE_PN += "${PN}-extra"
也可用 =+ 前置到 PACKAGES:
PACKAGES =+ "${PN}-extra"
根据 RPM、DEB、IPK 类型,do_package_write_* 创建实际包并放到 ${TMPDIR}/deploy,详见 “软件包源”。
备注
不直接支持从 deploy/* 目录生成正式软件包源。通常需要发行版专用的源 维护机制,把新包上传到正式源,例如 Ångström。因高度依赖发行版,默认 不提供这部分功能。
3 生成镜像
软件包拆分并保存后,BitBake 生成根文件系统镜像:
流程包含多个阶段,依赖多项任务和变量。do_rootfs 创建文件和 目录结构,以下变量帮助确定实际安装列表:
IMAGE_INSTALL:从软件包源安装的基础集合。
PACKAGE_EXCLUDE:禁止进入镜像的软件包。
IMAGE_FEATURES:镜像功能,多数映射为额外软件包。
PACKAGE_CLASSES:RPM、DEB、IPK 后端,也决定在软件包源中查找的位置。
IMAGE_LINGUAS:需要安装语言支持包的语言。
PACKAGE_INSTALL:最终传给包管理器的安装列表。
IMAGE_ROOTFS 指向正在创建的文件系统,PACKAGE_INSTALL 提供最终 软件包集合,二者驱动根文件系统生成。
无论目标是否启用包管理,安装都由 dnf/rpm、opkg 或 apt/dpkg 控制。如果 目标不启用,流程末尾会删除包管理数据库。安装最后运行各包的安装后脚本, 无法在主机运行的脚本留到目标首次启动。使用 只读根文件系统 时,全部安装后脚本都必须在主机成功执行。
rootfs 最后执行后处理,包括创建清单和优化。
.manifest 与根文件系统镜像同目录,逐行列出安装包。例如 testimage 类用它 决定是否运行某些测试,详见 IMAGE_MANIFEST。
优化包括 mklibs 及 ROOTFS_POSTPROCESS_COMMAND 指定的其他命令。 mklibs 用于缩小库体积。
根文件系统完成后,do_image 开始镜像处理,先运行 IMAGE_PREPROCESS_COMMAND 列出的函数,然后创建最终输出文件。
系统根据 IMAGE_FSTYPES 动态生成所需 do_image_* 任务,形成一个或 多个镜像文件,并可压缩根文件系统以减小体积。格式由 IMAGE_FSTYPES 决定, 能否压缩取决于格式支持。
动态任务形式为:
do_image_type
例如 ext4 对应:
do_image_ext4
最后执行 do_image_complete,调用 IMAGE_POSTPROCESS_COMMAND 中的函数,对最终输出文件作后处理。
备注
整个镜像生成过程在 Pseudo 下运行,确保根文件系统的文件所有权正确。
3 生成 SDK
构建系统用 BitBake 生成标准 SDK 和 eSDK 的安装脚本:
备注
交叉工具链生成见 “交叉开发工具链生成”; 使用 populate_sdk 的优势见 “构建 SDK 安装程序”。
与镜像一样,SDK 生成包含多阶段和变量。do_populate_sdk 和 do_populate_sdk_ext 用关键变量决定实际安装的包,图中变量见 “应用开发 SDK”。
populate_sdk 构建标准 SDK,分目标与主机两部分。目标部分包含面向硬件的库 和头文件,主机部分运行于 SDKMACHINE。
populate_sdk_ext 处理方式不同,它封装完整构建系统,包含主机和目标所需 全部内容。
无论类型,任务在清理后生成交叉环境设置脚本和必要配置。最终输出为包含 环境脚本的交叉工具链安装 .sh 文件。
3 戳记文件与任务重跑
每个任务成功后,BitBake 在 STAMPS_DIR 写入戳记文件。名称开头由 STAMP 决定,末尾包括任务名称和当前 输入校验和。
备注
此命名假定 BB_SIGNATURE_HANDLER 为 OEBasicHash,当前通常如此。
判断是否重跑时,检查是否有输入校验和匹配的戳记。有则假定输出仍存在且有效, 否则重新执行。
备注
戳记机制比 “Setscene 任务与共享状态” 的共享状态缓存更通用:任何有有效戳记的任务都不重跑,不限于可缓存任务。
但戳记只表示工作已完成,不保存输出。实际输出通常位于 TMPDIR,例如配方 WORKDIR。共享状态则增加了缓存输出并跨构建机器共享的能力。
STAMPS_DIR 通常是 TMPDIR 子目录,因此删除 TMPDIR 也会删除戳记,任务便会 正确重跑以重新填充。
若要任务始终视为过期,可设置 nostamp。 依赖它的任务也将始终过期,这未必符合预期。
查看任务签名信息见 “查看任务的变量依赖”。
3 镜像
镜像是可在目标启动的压缩根文件系统,构建输出部分即为镜像。下面展开:
备注
项目提供的示例镜像列表见 “镜像”。
镜像写入构建目录的 tmp/deploy/images/machine/,其中包含预期加载到目标的 文件。DEPLOY_DIR 指向 deploy,DEPLOY_DIR_IMAGE 指向当前配置对应 的镜像目录。
kernel-image:内核二进制。KERNEL_IMAGETYPE 决定文件命名,可能 使用不同前缀。机器镜像目录可包含多个内核镜像。
root-filesystem-image:目标根文件系统,例如
*.ext3、*.bz2。 IMAGE_FSTYPES 决定格式,同一机器目录可包含多个根文件系统。kernel-modules:包含内核全部已构建模块的归档。此形式用于历史兼容, 可将 MODULE_TARBALL_DEPLOY 设为 0 禁用。机器目录可包含多个归档。
bootloaders:若目标适用,包含镜像所需引导加载程序,同一机器可有多个。
symlinks:每类文件都有指向最近构建版本的符号链接,方便外部脚本获取最新文件。
3 应用开发 SDK
总体流程图的 Application Development SDK 表示 SDK 输出。eSDK 使用
bitbake -c populate_sdk_ext imagename,标准 SDK 使用
bitbake -c populate_sdk imagename,生成方式有所不同:
输出包括自解压安装器 .sh、主机与目标清单及 SDK 测试文件。运行安装器会安装 交叉工具链、库和头文件以及环境设置脚本,建立交叉开发环境。工具链运行在 SDK 机器上,属于主机部分;库和头文件面向目标硬件,属于目标部分。使用工具 前通过环境脚本初始化。
备注
可以下载预构建安装器,也可以自行构建并安装,项目支持多种设置方式。
工具链背景见 “交叉开发工具链生成”。
环境设置详见 Yocto 项目应用开发与软件开发套件(SDK/eSDK)手册。
所有输出写入构建目录的 deploy/sdk。配置变量随 SDK 类型不同而不同。 eSDK 相关变量包括:
DEPLOY_DIR:指向 deploy。
SDK_EXT_TYPE:是否复制共享状态产物,默认复制全部必需产物。
SDK_INCLUDE_PKGDATA:是否包含 world 中全部配方的 packagedata。
SDK_INCLUDE_TOOLCHAIN:是否在 eSDK 中包含工具链。
ESDK_LOCALCONF_ALLOW:允许从构建配置传入 eSDK 的变量列表。
ESDK_LOCALCONF_REMOVE:不允许传入的变量列表。
ESDK_CLASS_INHERIT_DISABLE:从 eSDK 全局 INHERIT 移除的类。
标准 SDK 相关变量包括:
DEPLOY_DIR:指向 deploy。
SDKMACHINE:运行交叉工具、为目标硬件创建软件包的机器架构。
SDKIMAGE_FEATURES:目标部分包含的功能。
TOOLCHAIN_HOST_TASK:主机部分的软件包,运行于 SDKMACHINE。 populate_sdk 有默认包集合,此变量可追加内容。
TOOLCHAIN_TARGET_TASK:为目标硬件构建的 SDK 目标部分软件包。
SDKPATHINSTALL:安装脚本提供的默认安装路径。
SDK_HOST_MANIFEST:主机部分全部已安装包列表,主要用于标准 SDK, 在 eSDK 中也有少量作用。
SDK_TARGET_MANIFEST:目标部分全部已安装包列表,同样主要用于标准 SDK,在 eSDK 中也有少量作用。
3 交叉开发工具链生成
创建 交叉开发工具链 时,项目替你 完成大多数工作。本节提供生成和使用的技术背景,另见 Yocto 项目应用开发与软件开发套件(SDK/eSDK)手册。
工具链用于构建运行在目标硬件上的镜像和应用,只需少量命令,构建系统便会 创建所需工具链。
下图展示构建环境中的工具链生成和使用:
多数工作在构建主机完成。BitBake 创建镜像时,用主机 gcc 引导生成 gcc-cross, 使用 Clang 时则为 clang-cross。随后用它编译目标源码,可以将其看作仅供 BitBake 内部使用的自动生成交叉编译器。
标准工具链引导过程如下:
gcc -> virtual/cross-binutils -> linux-libc-headers -> virtual/cross-cc -> libgcc-initial -> virtual/libc -> libgcc -> virtual/compilerlibs
gcc:由构建主机或 buildtools 归档提供的 GNU 编译器。
virtual/cross-binutils:引导 cross-cc 阶段及构建 C 库头文件所需二进制工具。
linux-libc-headers:交叉编译器和 C 库构建需要的头文件。
virtual/cross-cc:交叉编译器引导的最后阶段,生成 BitBake 为目标设备 构建镜像时实际使用的编译器。
它是运行在构建主机的本机工具。
libgcc-initial:用于引导 virtual/libc 的初始 GCC 支持库。
virtual/libc:C 标准库提供者,例如 GNU C Library。
libgcc:最终 GCC 支持库,只有 C 库可供链接后才能构建。
virtual/compilerlibs:引导过程产生的运行时库,输出包含目标设备所需库 的二进制内容。
还可以为应用开发生成可重定位 SDK 安装器。运行后安装 gcc-cross-canadian、 clang-cross-canadian、binutils-cross-canadian 和其他 nativesdk-* 工具, 它们原生运行于 SDK_ARCH,用于交叉编译和测试软件。图中展示生成 命令。该工具链运行于 SDKMACHINE,它可能与构建主机相同,也可能不同。
备注
如果目标架构受支持,可以使用项目附带的预构建镜像,其中已有交叉开发 工具链安装器。
可重定位工具链的引导过程为:
gcc -> virtual/cross-binutils -> linux-libc-headers -> virtual/cross-cc -> libgcc-initial -> virtual/libc -> gcc-cross-canadian/clang-cross-canadian
除最后一项外,与标准工具链相同。gcc-cross-canadian 或 clang-cross-canadian 是最终可重定位交叉编译器,在 SDKMACHINE 运行并生成目标可执行代码。每种 架构只生成一个,通过编译命令参数选择不同处理器优化,从而避免多个编译器, 减小工具链体积。
备注
eSDK 不使用 cross-canadian,因为它包含构建系统副本,内部系统根目录 已有 gcc-cross。
备注
SDK 使用 Clang 的方法见 PREFERRED_TOOLCHAIN_SDK。
备注
构建工具链安装器的优势见 “构建 SDK 安装程序”。
3 自动添加运行时依赖
系统自动添加常见运行时依赖,无需手写 RDEPENDS。三个机制 shlibdeps、pcdeps、 depchains 分别处理共享库、pkg-config 模块和 -dev/-dbg 包。其他依赖仍需 手动声明。
shlibdeps:每个配方的 package 任务查找所安装共享库,将所在包登记为该 库的 soname 提供者。packagedata 将映射保存到 全局 PKGDATA_DIR。
同时检查所有可执行文件和共享库链接了哪些库。对每项依赖,查询映射, 找到包含该库的包,通常来自其他配方,然后添加到运行时依赖。
自动依赖还限制版本,至少使用当前库提供包版本,相当于 RDEPENDS 添加 "package (>= version)",必要时强制同步升级库包。
如果库仅内部使用、不应登记为提供者,在配方的 PRIVATE_LIBS 中列出。
pcdeps:package 查找所有 .pc 模块,将所在包登记为提供者,由 packagedata 保存全局映射。
同时检查模块的 Requires:,识别对其他模块的依赖,再查询 PKGDATA_DIR, 找到提供包并添加运行时依赖。
备注
该机制最常推断 -dev 包之间的依赖。
depchains:若 foo 依赖 bar,则 foo-dev 依赖 bar-dev,foo-dbg 依赖 bar-dbg。 例如 bar-dev 可能提供 foo-dev 所需头文件和共享库符号链接。
此机制添加的是 RRECOMMENDS。
备注
foo-dev 默认还以 RDEPENDS 依赖 foo,因为 bitbake.conf 的 RDEPENDS:${PN}-dev 包含 ${PN}。
为避免依赖链断裂,默认即使为空也生成 -dev 和 -dbg,见 ALLOW_EMPTY。
package 通过
[deptask]
依赖 DEPENDS 各配方的 packagedata。因此只要 DEPENDS 正确,所需库和模块
映射就会及时可用。
3 Fakeroot 与 Pseudo
install、package_write*、rootfs 和 image_* 等任务,若能执行通常需要 root 的操作,会更容易实现。例如 install 需要将文件 UID、GID 设为任意值。
可以要求 BitBake 以 root 运行,但这既麻烦又不安全。实际采用“虚假 root” 环境,让任务及子进程认为自己是 root,并看到内部一致的文件系统视图。 只要生成最终包或镜像本身不需要真实 root,前面使用这种环境就不会有问题。
此能力称为 fakeroot,名称来自请求该环境的 BitBake 关键字或变量标志。
构建系统用 Pseudo 实现。它通过
LD_PRELOAD 覆盖系统调用,模拟 root,并用 SQLite 3 数据库记录虚假所有者
和权限。各配方数据库位于 ${WORKDIR}/pseudo/files.db。
采用文件而非内存,使状态跨任务和构建持久保存,这是普通 fakeroot 无法做到的。
备注
自定义任务若操作与 fakeroot 任务相同的文件,也必须在 fakeroot 下运行, 否则既不能执行 root 专用操作,也看不到模拟权限。同时应依赖 virtual/fakeroot-native:do_populate_sysroot,例如:
fakeroot do_mytask () {
...
}
do_mytask[depends] += "virtual/fakeroot-native:do_populate_sysroot"
更多信息见 BitBake 的 FAKEROOT* 变量,以及 Why Not Fakeroot?。
3 BitBake 任务图
以下配方和图示展示 BitBake 如何在构建目录和环境中工作,完整呈现生成最终 软件包需要的任务。
示例有两个配方:
libhello:提供共享库。
sayhello:使用该库完成工作。
备注
sayhello 编译时需要共享库完成动态链接,运行时也需要加载器找到它。 依赖详见 依赖关系。
libhello 源码包括:
LICENSE:库的许可证。
Makefile:make 构建库使用的文件。
hellolib.c:库实现。
hellolib.h:C 头文件。
sayhello 源码包括:
LICENSE:项目许可证。
Makefile:make 构建项目使用的文件。
sayhello.c:项目源码。
展示内容前,先列出将 sayhello 集成到根文件系统所需步骤:
为每个项目创建包含对应文件的 Git 仓库。
分别创建配方。
确保 sayhello 的 DEPENDS 包含 libhello。
确保 sayhello 的 RDEPENDS 包含 libhello。
将 sayhello 加入 IMAGE_INSTALL。
libhello/Makefile 内容为:
LIB=libhello.so
all: $(LIB)
$(LIB): hellolib.o
$(CC) $< -Wl,-soname,$(LIB).1 -fPIC $(LDFLAGS) -shared -o $(LIB).1.0
%.o: %.c
$(CC) -c $<
clean:
rm -rf *.o *.so*
备注
创建共享库时强烈建议遵循 Linux 惯例,背景见 共享库说明。
备注
Makefile 强烈建议使用 CC、LDFLAGS 和 CFLAGS,BitBake 会根据配置通过 环境变量设置它们。
libhello/hellolib.h 内容为:
#ifndef HELLOLIB_H
#define HELLOLIB_H
void Hello();
#endif
libhello/hellolib.c 内容为:
#include <stdio.h>
void Hello(){
puts("Hello from a Yocto demo \n");
}
sayhello/Makefile 内容为:
EXEC=sayhello
LDFLAGS += -lhello
all: $(EXEC)
$(EXEC): sayhello.c
$(CC) $< $(LDFLAGS) $(CFLAGS) -o $(EXEC)
clean:
rm -rf $(EXEC) *.o
sayhello/sayhello.c 内容为:
#include <hellolib.h>
int main(){
Hello();
return 0;
}
libhello_0.1.bb 内容为:
SUMMARY = "Hello demo library"
DESCRIPTION = "Hello shared library used in Yocto demo"
# NOTE: Set the License according to the LICENSE file of your project
# and then add LIC_FILES_CHKSUM accordingly
LICENSE = "CLOSED"
# Assuming the branch is main
# Change <username> accordingly
SRC_URI = "git://github.com/<username>/libhello;branch=main;protocol=https"
do_install(){
install -d ${D}${includedir}
install -d ${D}${libdir}
install hellolib.h ${D}${includedir}
oe_soinstall ${PN}.so.${PV} ${D}${libdir}
}
sayhello_0.1.bb 内容为:
SUMMARY = "SayHello demo"
DESCRIPTION = "SayHello project used in Yocto demo"
# NOTE: Set the License according to the LICENSE file of your project
# and then add LIC_FILES_CHKSUM accordingly
LICENSE = "CLOSED"
# Assuming the branch is main
# Change <username> accordingly
SRC_URI = "git://github.com/<username>/sayhello;branch=main;protocol=https"
DEPENDS += "libhello"
RDEPENDS:${PN} += "libhello"
do_install(){
install -d ${D}${bindir}
install -m 0700 sayhello ${D}${bindir}
}
将配方放入自定义层后,运行 bitbake sayhello 即可构建。
下图展示 BitBake 完成此工作的任务顺序: