嵌入式-Linux-开发-使用-Yocto-项目-全-

嵌入式 Linux 开发:使用 Yocto 项目(全)

原文:annas-archive.org/md5/5ee5027636716cef12ac4b33d135af11

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Linux 一直被用于前沿产品,而嵌入式系统也成为人类技术组合中的重要组成部分。

Yocto 项目处于一个理想的位置,成为你项目的首选。它提供了一整套丰富的工具,帮助你将大部分精力和资源集中在产品开发上,而不是重新发明轮子。

本书的构思基于嵌入式 Linux 产品和开发团队的常见任务和需求。然而,由活跃社区成员以实践性和简明的方式编写,本书将成为你学习过程中的一块跳板,也将助力项目的顺利推进。

在本书的第三版中,书籍已经经过彻底的重修,融入了前几版读者的反馈,并扩展了内容,以帮助读者理解与 Yocto 项目相关的复杂概念。此外,本书还进行了全面更新,反映了 Yocto 项目长期支持版本 4.0(代号 Kirkstone)所做的更改。

此外,还新增了两章,一章介绍如何通过仿真使用 QEMU 加速产品开发,另一章则讨论 Yocto 项目及一般项目指南。

本书适合的人群

本书适合有嵌入式 Linux 经验的工程师和爱好者,旨在帮助他们了解 Yocto 项目的工具,以便评估、比较或在项目中使用。本书的目标是帮助你迅速掌握知识,避免陷入常见的学习曲线陷阱。

本书内容概览

第一章认识 Yocto 项目,介绍了引入 Yocto 项目及其主要工具的基本概念和前提条件。

第二章构建我们的 Poky 系统,介绍了首次构建所需的环境。

第三章使用 Toaster 构建镜像,展示了一个用户友好的 Web 界面,既可以作为配置封装器,也可以作为构建工具使用。

第四章认识 BitBake 工具,介绍了 BitBake 元数据的概念。

第五章掌握 BitBake 工具,展示了如何管理任务及其依赖关系。

第六章详细介绍临时构建目录,详细讲解了构建过程中的临时输出文件夹。

第七章吸收打包支持,解释了作为基础的打包机制,用于创建和管理所有二进制包。

第八章深入了解 BitBake 元数据,详细说明了 BitBake 元数据语言,这将用于后续的所有章节。

第九章与 Yocto 项目一起开发,演示了获取开发环境所需的工作流。

第十章使用 Yocto 项目进行调试,展示了如何使用 Poky 生成调试环境并使用它。

第十一章探索外部层,探讨了 Yocto 项目中最重要的概念之一——使用外部层的灵活性。

第十二章创建自定义层,讲解了层创建的步骤。

第十三章自定义现有配方,展示了如何自定义现有的配方。

第十四章实现 GPL 合规性,总结了实现 copyleft 合规产品的任务和概念。

第十五章启动我们的自定义嵌入式 Linux,使用实际硬件和 Yocto 项目的工具。

第十六章通过仿真加速产品开发 - QEMU,演示了 QEMU 如何加速产品开发。

第十七章最佳实践,讨论了基于作者经验的一些 Yocto 项目和一般项目的相关指导方针。

为了最大化本书的价值

为了更好地理解本书,至关重要的是你需要具备一些相关背景知识,尤其是本书未覆盖或仅简要提及的主题,例如 Git 和 Linux 内核的基础知识以及其基本编译过程。

在深入本书中涉及的技术概念之前,我们建议先了解 Yocto 项目的大致框架。我们推荐阅读开源小册子,前往 Yocto 项目,该小册子可以在 Git 仓库中找到:git.io/vFUiI;这本小册子的内容旨在帮助新手更好地理解 Yocto 项目的目标及其潜在用途。它提供了一个项目的概述,然后再深入探讨如何实现技术细节。

需要具备 GNU/Linux 环境和嵌入式 Linux 的基本使用知识,以及开发中常见的一些概念,如编译、调试、部署和安装。此外,有一些 Shell 脚本和 Python 的经验会加分,因为这些编程语言是 Yocto 项目工具的核心技术,广泛应用于其中。

然而,你可能希望深入了解这些主题。如果是这样,我们推荐阅读 Chris Simmonds 的书籍 精通嵌入式 Linux 编程 - 第三版,ISBN-13 978-1789530384。

你不应将我们上面列举的任何缺失概念视为障碍,而应当看作是你可以学习的内容,并且在阅读本书时,实践这些概念的使用。

下载彩色图像

我们还提供了一份 PDF 文件,包含了本书中使用的截图和图表的彩色图像。你可以在此下载: https://packt.link/lbpMD。

使用的约定

本书中使用了若干文本约定。

正文中的代码:表示文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。例如:“在第 8 行,BBFILE_COLLECTIONS,我们告诉 BitBake 创建一个新的元数据集合,名为 yocto。接下来,在第 9 行,BBFILE_PATTERN_yocto,我们定义规则匹配所有以 LAYERDIR 开头的路径,从而识别属于 yocto 集合的元数据。”

任何命令行输入或输出都如下所示:

$ sudo dnf install gawk make wget tar bzip2 gzip python3 unzip perl patch diffutils diffstat git cpp gcc gcc-c++ glibc-devel texinfo chrpath ccache perl-Data-Dumper perl-Text-ParseWords perl-Thread-Queue perl-bignum socat python3-pexpect findutils which file cpio python python3-pip xz python3-GitPython python3-jinja2 SDL-devel xterm rpcgen mesa-libGL-devel perl-FindBin perl-File-Compare perl-File-Copy perl-locale zstd lz4

粗体:表示新术语、重要词汇或屏幕上显示的单词。例如,菜单或对话框中的词汇会显示为 粗体。例如:“然后,点击 Image recipes 标签,选择你要构建的镜像。”

提示或重要说明

如此显示。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果你对本书的任何方面有疑问,请通过 customercare@packtpub.com 给我们发邮件,并在邮件主题中注明书名。

勘误表:虽然我们已经尽力确保内容的准确性,但错误仍然可能发生。如果你在本书中发现错误,我们将非常感激你能向我们报告。请访问 www.packtpub.com/support/errata 并填写表格。

盗版:如果你在互联网上发现任何形式的非法复制品,我们将非常感激你能提供该内容的地址或网站名称。请通过 copyright@packt.com 联系我们,并提供该材料的链接。

如果你有兴趣成为作者:如果你在某个领域具有专业知识,并有兴趣写书或为书籍贡献内容,请访问 authors.packtpub.com

分享你的想法

阅读完《嵌入式 Linux 开发与 Yocto 项目》后,我们希望听到你的反馈!请 点击这里直接进入亚马逊的评论页面并分享你的想法。

你的评论对我们以及技术社区非常重要,它将帮助我们确保提供优质的内容。

下载本书的免费 PDF 版

感谢购买本书!

你喜欢随时随地阅读,但又无法随身携带纸质书籍吗?你购买的电子书与自己选择的设备不兼容吗?

不用担心,现在购买每本 Packt 书籍时,你都能免费获得该书的 DRM-free PDF 版本。

在任何地方、任何设备上阅读。直接从你最喜欢的技术书籍中搜索、复制并粘贴代码到你的应用程序中。

优惠还不止于此,你还可以获得独家折扣、新闻简报和每日发送到邮箱的精彩免费内容。

按照以下简单步骤即可享受这些好处:

  1. 扫描二维码或访问以下链接

packt.link/free-ebook/9781804615065

  1. 提交您的购买证明

  2. 就这样!我们会直接将您的免费 PDF 和其他福利发送到您的邮箱

第一章:认识 Yocto 项目

本章介绍 Yocto 项目。这里讨论的项目的主要概念在全书中都会不断使用。此外,我们还将简要讨论 Yocto 项目的历史、OpenEmbedded、Poky、BitBake、元数据和版本管理方案。所以,请系好安全带,欢迎登机!

什么是 Yocto 项目?

Yocto 项目是 Linux 基金会的一个工作组,定义如下:

Yocto 项目是一个开源协作项目,帮助开发人员创建专为嵌入式产品设计的定制 Linux 系统,无论产品的硬件架构如何。Yocto 项目提供了一个灵活的工具集和开发环境,允许全球的嵌入式设备开发人员通过共享的技术、软件堆栈、配置和最佳实践进行协作,从而创建这些定制的 Linux 镜像。

全球成千上万的开发人员发现,Yocto 项目在系统和应用开发、存档与管理收益、以及在速度、占用空间和内存利用方面的定制化上提供了优势。该项目在交付嵌入式软件堆栈方面已经成为标准。该项目支持多种硬件平台的软件定制和构建交换,并且软件堆栈可以进行维护和扩展。

  • Yocto 项目概览与概念手册

Yocto 项目是一个开源协作项目。它提供模板、工具和方法,帮助我们为嵌入式产品创建定制的基于 Linux 的系统,无论硬件架构如何。它可以基于 glibcmusl C 标准库以及为裸机开发提供的 实时操作系统 (RTOS) 工具链,生成定制的 Linux 发行版,正如 Zephyr 项目所做的那样。

由 Linux 基金会成员管理,该项目保持独立于成员组织,这些成员组织通过多种方式参与并为项目提供资源。

它成立于 2010 年,由许多硬件制造商、开源操作系统供应商、电子公司共同合作,旨在减少工作重复,提供资源和信息,以满足新手和经验丰富用户的需求。其中的资源之一是 OpenEmbedded Core,这是 OpenEmbedded 项目提供的核心系统组件。

Yocto 项目聚集了多个公司、社区、项目和工具,目标一致——构建基于 Linux 的嵌入式产品。这些利益相关者同舟共济,由共同的社区需求驱动,共同协作。

描述 Yocto 项目

为了帮助我们理解 Yocto 项目的职责和成果,我们可以用计算机的类比来说明。输入是一组描述我们需求的数据,即我们的规格说明。作为输出,我们得到所需的基于 Linux 的嵌入式产品。

输出由操作系统的各个部分组成,涵盖了 Linux 内核、引导加载程序和根文件系统(rootfs),这些组件被打包并组织在一起以便协同工作。

Yocto 项目的工具在所有中间步骤中都参与到生成最终 rootfs 包和其他交付物的过程。先前构建的软件组件会在不同的构建中重用——无论是应用程序、库,还是任何软件组件。

当无法进行重用时,软件组件会按照正确的顺序和所需的配置进行构建,包括从各自的代码库中提取所需的源代码,如 Linux 内核档案(www.kernel.org)、GitHub、BitBucket 和 GitLab。

Yocto 项目的工具准备其构建环境、实用工具和工具链,减少了对主机软件的依赖。这些实用工具、版本和配置选项独立于主机 Linux 发行版,从而最小化了在生成相同结果时对主机工具的依赖。一个微妙但重要的好处是,确定性大大增强,减少了构建主机的依赖,但首次构建的时间增加。

BitBake 和 OpenEmbedded Core 都在 OpenEmbedded 项目的框架下,而一些项目,如 Poky,则在 Yocto 项目的框架下。它们是互为补充的,在系统中扮演着各自特定的角色。我们将在本章以及全书中详细了解它们如何协同工作。

OpenEmbedded 项目与 Yocto 项目的联盟

OpenEmbedded 项目大约在 2003 年 1 月成立,当时一些来自 OpenZaurus 项目的核心开发者开始使用新的构建系统。从一开始,OpenEmbedded 构建系统便是一个受 Gentoo Portage 包管理系统启发并基于该系统的任务调度器,名为 BitBake。因此,该项目迅速扩展了其软件库和支持的机器列表。

由于混乱和缺乏协调的开发,使用 OpenEmbedded 在需要更稳定和精致代码库的产品中变得具有挑战性,这就是 Poky 发行版诞生的原因。Poky 起初是 OpenEmbedded 构建系统的一个子集,并且在有限的架构集上拥有更精致和稳定的代码库。此外,Poky 的体积较小,允许其开发出一些突出的技术,如 IDE 插件和快速仿真器QEMU)集成,这些技术至今仍在使用。

Yocto 项目和 OpenEmbedded 项目整合了他们的工作,形成了一个核心构建系统,称为 OpenEmbedded Core。它融合了 Poky 和 OpenEmbedded 的精华,强调更多地使用附加组件、元数据和子集。大约在 2010 年 11 月,Linux 基金会宣布 Yocto 项目将在一个由 Linux 基金会资助的项目下继续这一工作。

了解 Poky

Poky 是默认的 Yocto 项目参考发行版,使用 OpenEmbedded 构建系统技术。它由工具、配置文件和配方数据(称为元数据)组成。它是平台独立的,并使用 BitBake 工具、OpenEmbedded Core 以及一组默认的元数据进行交叉编译,如下图所示。此外,它提供了构建和组合成千上万个分布式开源项目的机制,以形成一个完全可定制、完整且一致的 Linux 软件栈。

Poky 的主要目标是提供嵌入式开发者所需的所有功能。

图 1.1 – Poky 主要组件

图 1.1 – Poky 主要组件

BitBake

BitBake 是一个任务调度器和执行系统,解析 Python 和 Shell 脚本代码。解析后的代码生成并运行任务,这些任务是按代码的依赖关系排列的一系列步骤。

BitBake 评估所有可用的元数据,管理动态变量展开、依赖关系和代码生成。此外,它跟踪所有任务以确保它们完成,最大化处理资源的利用率,从而减少构建时间并提高可预测性。BitBake 的开发在 lists.openembedded.org/g/bitbake-devel 邮件列表中进行,源代码位于 Poky 的 bitbake 子目录中。

OpenEmbedded Core

OpenEmbedded Core 元数据集合提供了 Poky 构建系统的引擎。它提供了核心功能,旨在尽可能地通用和精简。它支持七种不同的处理器架构(ARM、ARM64、x86、x86-64、PowerPC、PowerPC 64、MIPS、MIPS64、RISC-V32 和 RISC-V 64),仅支持通过 QEMU 模拟的平台。

开发工作集中在 lists.openembedded.org/g/openembedded-coremailto:openembedded-core@lists.openembedded.org)邮件列表中,并将其元数据存储在 Poky 的 meta 子目录中。

元数据

该元数据包含配方和配置文件。它由 Python 和 Shell 脚本文本文件混合组成,提供了一个极其灵活的工具。Poky 使用它来扩展 OpenEmbedded Core,并包括两个不同的层次,它们是其他元数据子集,如下所示:

  • meta-poky:此层提供默认和支持的发行版策略、视觉品牌和元数据跟踪信息(维护者、上游状态等)。这是作为一个精心策划的模板,可供发行版构建者用于创建他们自定义的发行版。

  • meta-yocto-bsp:提供了作为 Yocto 项目开发的参考硬件使用的 板级支持包 (BSP) 和 质量保证 (QA) 过程。

第九章与 Yocto 项目的开发,更详细地探讨了元数据,并且当我们编写食谱时作为参考。

Yocto 项目发布

Yocto 项目每六个月发布一次,分别在 4 月和 10 月。这个发布周期确保了持续的开发流,同时在稳定性方面提供了更多的测试和重点关注。当一个版本准备好时,它会成为稳定版本或长期支持LTS)版本。

支持期在稳定版本和长期支持(LTS)版本之间有显著差异。稳定版本的支持期为 7 个月,每个稳定版本提供 1 个月的重叠支持。LTS 版本的最短支持期为 2 年,并可选择延长。官方支持期结束后,转为社区支持,最终进入生命周期结束EOL)。

当官方发布支持期结束后,如果社区成员接手并成为社区维护者,发布版本可以转为社区支持。最终,当源代码在 2 个月内没有更改,或社区维护者不再是活跃成员时,版本将进入生命周期结束(EOL)阶段。

以下图表显示了两个发布周期:

图 1.2 – 稳定版本或 LTS 版本的发布周期

图 1.2 – 稳定版本或 LTS 版本的发布周期

表 1.1提供了 Yocto 项目版本、代号、发布日期和当前支持级别,内容如下所示。更新后的表格可在wiki.yoctoproject.org/wiki/Releases查看:

代号 版本 发布日期 支持级别
Mickledore 4.2 2023 年 4 月 未来(直到 2023 年 10 月)
Langdale 4.1 2022 年 10 月 稳定(直到 2023 年 5 月)
Kirkstone 4.0 2022 年 5 月 LTS(最短至 2024 年 4 月)
Honister 3.4 2021 年 10 月 生命周期结束(EOL)
Hardknott 3.3 2021 年 4 月 生命周期结束(EOL)
Gatesgarth 3.2 2020 年 10 月 生命周期结束(EOL)
Dunfell 3.1 2020 年 4 月 LTS(直到 2024 年 4 月)
Zeus 3.0 2019 年 10 月 生命周期结束(EOL)
Warrior 2.7 2019 年 4 月 生命周期结束(EOL)
Thud 2.6 2018 年 11 月 生命周期结束(EOL)
Sumo 2.5 2018 年 4 月 生命周期结束(EOL)
Rocko 2.4 2017 年 10 月 生命周期结束(EOL)
Pyro 2.3 2017 年 5 月 生命周期结束(EOL)
Morty 2.2 2016 年 11 月 生命周期结束(EOL)
Krogoth 2.1 2016 年 4 月 生命周期结束(EOL)
Jethro 2.0 2015 年 11 月 生命周期结束(EOL)
Fido 1.8 2015 年 4 月 生命周期结束(EOL)
Dizzy 1.7 2014 年 10 月 生命周期结束(EOL)
Daisy 1.6 2014 年 4 月 生命周期结束(EOL)
Dora 1.5 2013 年 10 月 生命周期结束(EOL)
Dylan 1.4 2013 年 4 月 生命周期结束(EOL)
Danny 1.3 2012 年 10 月 生命周期结束(EOL)
Denzil 1.2 2012 年 4 月 生命周期结束(EOL)
Edison 1.1 2011 年 10 月 生命周期结束(EOL)
Bernard 1.0 2011 年 4 月 生命周期结束(EOL)
Laverne 0.9 2010 年 10 月 生命周期结束(EOL)

表 1.1 – Yocto 项目版本列表

总结

本章概述了 OpenEmbedded 项目与 Yocto 项目之间的关系,构成 Poky 的组件以及该项目的起源。下一章将介绍 Poky 工作流,包括如何下载、配置和准备 Poky 构建环境,以及如何使用 QEMU 构建并运行第一个镜像。

第二章:构建我们的第一个基于 Poky 的系统

让我们开始动手吧!在本章中,我们将理解 Poky 工作流中涉及的基本概念。我们将介绍下载、配置和准备 Poky 构建环境的步骤,并构建一些可用的内容。本章涵盖的步骤适用于测试和开发,能让我们体验使用 Poky,并感受它的能力。

准备构建主机系统

本节描述了如何准备 Windows 和 Linux 分发版主机系统。虽然我们将描述 Windows 的步骤,但我们将重点介绍如何使用 Linux 分发版主机系统。

提示

可以使用 macOS 作为主机系统,但需要使用 CROss PlatformSCROPS)框架,该框架利用 Docker,允许使用包括 macOS 在内的外部操作系统。有关更多信息,可以参考 Yocto 项目开发任务手册 中的 设置以使用 CROss PlatformS (CROPS) 部分(docs.yoctoproject.org/4.0.4/dev-manual/start.html#setting-up-to-use-cross-platforms-crops)。

接下来,我们将提供开始构建主机系统准备所需的信息。

使用 Windows 子系统 Linux(WSLv2)

如果你是 Windows 用户,可以在 Windows 上设置一个 Linux 分发版。WSLv2 仅适用于 Windows 10+ 版本 18917 及更高版本。WSLv2 允许使用 Yocto 项目进行开发。你可以从 Microsoft Store 安装 Linux 分发版。

请参考 Yocto 项目开发任务手册 中的 设置以使用 Windows 子系统 Linux (WSLv2) 部分(docs.yoctoproject.org/4.0.4/dev-manual/start.html#setting-up-to-use-windows-subsystem-for-linux-wslv2)。一旦你设置好 WSLv2,你可以像在本地 Linux 机器上一样继续执行接下来的步骤。

准备一个基于 Linux 的系统

设置主机系统的过程取决于我们使用的 Linux 分发版。Poky 有一组支持的 Linux 分发版。假设我们是嵌入式 Linux 开发的新手,在这种情况下,建议使用支持的 Linux 分发版,以避免浪费时间调试与主机系统支持相关的问题。

如果你使用以下分发版的当前版本,你应该可以开始在你的机器上使用 Yocto 项目:

  • Ubuntu

  • Fedora

  • CentOS

  • AlmaLinux

  • Debian

  • OpenSUSE Leap

为了确认你的版本是否受支持,建议在构建主机所需软件包部分查阅官方在线文档(docs.yoctoproject.org/4.0.4/ref-manual/system-requirements.html#required-packages-for-the-build-host)。

如果你偏好的发行版不在上述列表中,并不意味着无法在其上使用 Poky。你的主机开发系统必须满足 Git、tar、Python 和 GCC 的一些特定版本要求。你的 Linux 发行版应该提供这些基本工具的兼容版本。然而,也有可能你的主机开发系统未能满足所有这些要求。在这种情况下,你可以通过安装一个包含这些工具的buildtools tar 包来解决这个问题,详细信息请参阅所需的 Git、tar、Python 和 GCC 版本(docs.yoctoproject.org/4.0.4/ref-manual/system-requirements.html#required-git-tar-python-and-gcc-versions)。

我们必须在主机系统上安装一些软件包。本书提供了有关DebianFedora的安装说明,我们接下来将详细介绍这两个发行版。其他支持的发行版所需的软件包可以在Yocto 项目参考手册中找到(docs.yoctoproject.org/4.0.4/ref-manual/system-requirements.html#required-packages-for-the-build-host)。

基于 Debian 的发行版

要安装适用于无头主机系统的必要软件包,请运行以下命令:

$ sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev zstd liblz4-tool

Fedora

要安装适用于无头主机系统所需的软件包,请运行以下命令:

$ sudo dnf install gawk make wget tar bzip2 gzip python3 unzip perl patch diffutils diffstat git cpp gcc gcc-c++ glibc-devel texinfo chrpath ccache perl-Data-Dumper perl-Text-ParseWords perl-Thread-Queue perl-bignum socat python3-pexpect findutils which file cpio python python3-pip xz python3-GitPython python3-jinja2 SDL-devel xterm rpcgen mesa-libGL-devel perl-FindBin perl-File-Compare perl-File-Copy perl-locale zstd lz4

下载 Poky 源代码

在我们将所需软件包安装到开发主机系统之后,我们可以使用 Git 下载当前 LTS 版本(截至本文编写时)的 Poky 源代码,命令如下:

$ git clone https://git.yoctoproject.org/poky -b kirkstone

提示

了解更多关于 Git 的信息,请访问git-scm.com

下载过程完成后,poky目录中应该包含以下内容:

图 2.1 – 下载后的 poky 目录内容

图 2.1 – 下载后的 poky 目录内容

注意

本章及后续章节中呈现的示例和代码以 Yocto 项目 4.0 版本(代号Kirkstone)为参考。

准备构建环境

poky目录中存在一个名为oe-init-build-env的脚本,它用于设置构建环境。但首先,必须以“源”(source)方式运行该脚本(而不是执行它),如下所示:

$ source oe-init-build-env [build-directory]

在这里,[build-directory] 是一个可选参数,表示配置环境的目录名称。如果为空,它默认为 build[build-directory] 参数是我们进行构建的地方。

source oe-init-build-env build 命令输出的内容显示了一些重要的配置,如文件位置、一些项目 URL 以及一些常见的目标,如可用的镜像。下图显示了一个输出示例:

图 2.2 – oe-init-build-env 构建命令的输出

图 2.2 – oe-init-build-env 构建命令的输出

使用不同的构建目录非常方便。我们可以并行处理多个项目或实验性设置,而不会影响其他构建。

注意

在本书中,我们将使用 build 作为构建目录。当我们需要指向构建目录中的某个文件时,我们将采用相同的约定——例如,build/conf/local.conf

了解 local.conf 文件

当我们初始化一个构建环境时,它会创建一个名为 build/conf/local.conf 的文件。这个配置文件非常强大,因为它几乎可以配置构建过程的每个方面。我们可以设置目标机器以及用于自定义交叉工具链的工具链主机架构,优化选项以最大限度地减少构建时间,等等。build/conf/local.conf 文件中的注释是极好的文档,提供了可能的变量及其默认值的参考。我们可能需要从默认值中更改的最小变量集如下:

MACHINE ??= "qemux86-64"

MACHINE 变量是我们确定希望构建的目标机器的位置。在撰写本文时,Poky 在其参考 BSP 中支持以下机器:

  • beaglebone-yocto: 这是 BeagleBone,它是 32 位 ARM 的参考平台

  • genericx86: 这是 32 位 x86 架构机器的通用支持

  • genericx86-64: 这是 64 位 x86 架构机器的通用支持

  • edgerouter: 这是 EdgeRouter Lite,它是 64 位 MIPS 的参考平台

这些机器由一个名为 meta-yocto-bsp 的层提供。除了这些机器之外,OpenEmbedded Core 在 meta 目录中还提供对以下快速模拟(QEMU)机器的支持:

  • qemuarm: 这是 QEMU ARMv7 模拟

  • qemuarmv5: 这是 QEMU ARMv5 模拟

  • qemuarm64: 这是 QEMU ARMv8 模拟

  • qemumips: 这是 QEMU MIPS 模拟

  • qemumips64: 这是 QEMU MIPS64 模拟

  • qemuppc: 这是 QEMU PowerPC 模拟

  • qemuppc64: 这是 QEMU PowerPC 64 模拟

  • qemux86-64: 这是 QEMU x86-64 模拟

  • qemux86: 这是 QEMU x86 模拟

  • qemuriscv32: 这是 QEMU RISC-V 32 模拟

  • qemuriscv64: 这是 QEMU RISC-V 64 模拟

来自多个供应商的额外 BSP 层提供对其他机器的支持。使用额外 BSP 层的过程在第十一章中展示,探索 外部层

注意

local.conf文件是一种方便的方式,可以覆盖 Yocto 项目工具中的多个全局默认配置。基本上,我们可以更改或设置任何变量——例如,向镜像文件中添加额外的软件包。更改build/conf/local.conf文件很方便;然而,源代码管理系统通常不会跟踪此目录中的临时更改。

build/conf/local.conf文件可以设置多个变量。值得花时间阅读文件中生成的注释,以大致了解可以设置的变量。

构建目标镜像

Poky 提供了几种预设计的镜像配方,我们可以用来构建二进制镜像。我们可以通过在poky目录中运行以下命令来查看可用镜像列表:

$ ls meta*/recipes*/*images/*.bb

所有配方都提供镜像,这些镜像是已解包并配置好的软件包,生成一个我们可以在硬件或支持的 QEMU 机器上使用的文件系统。

接下来,我们可以看到最常用镜像的列表:

  • core-image-minimal:这是一个小型镜像,允许设备启动。它非常适合内核和引导加载程序的测试和开发。

  • core-image-base:这个仅有控制台的镜像为目标设备提供了基本的硬件支持。

  • core-image-weston:此镜像提供了 Wayland 协议库和参考 Weston 合成器。

  • core-image-x11:这是一个基本的 X11 镜像,带有终端。

  • core-image-sato:这是一个支持 Sato 和适用于使用 X11 的移动设备的移动环境的镜像。它提供了终端、编辑器、文件管理器、媒体播放器等应用程序。

  • core-image-full-cmdline:一个仅有控制台的镜像,安装了更多完整功能的 Linux 系统。

社区提供了其他参考镜像。一些镜像支持诸如实时(Real Time)、initramfs和 MTD(闪存工具)等功能。可以查看源代码或Yocto 项目参考手册(docs.yoctoproject.org/4.0.4/ref-manual/index.html),以获取完整且更新的列表。

为目标构建镜像的过程是直接的。但首先,我们需要使用source oe-init-build-env [build-directory]来设置构建环境,然后才能使用 BitBake 构建镜像。我们可以使用以下命令中的模板来构建镜像:

图 2.3 – 如何使用 BitBake 构建配方

图 2.3 – 如何使用 BitBake 构建配方

注意

在以下示例中,我们将使用MACHINE = "qemux86-64"。你可以在build/conf/local.conf中相应设置。

例如,要构建core-image-full-cmdline,运行以下命令:

$ bitbake core-image-full-cmdline

Poky 构建如下面的图所示:

图 2.4 – bitbake core-image-full-cmdline 的结果

图 2.4 – bitbake core-image-full-cmdline 的结果

在 QEMU 中运行镜像

我们可以使用硬件仿真来加速开发过程,因为它允许在不涉及实际硬件的情况下进行测试运行。幸运的是,大多数项目中只有极小部分是依赖硬件的。

QEMU 是一个免费的开源软件包,执行硬件虚拟化。基于 QEMU 的机器允许在没有真实硬件的情况下进行测试和开发。目前支持 ARMv5、ARMv7、ARMv8、MIPS、MIPS64、PowerPC、PowerPC 64、RISC-V 32、RISC-V 64、x86 和 x86-64 仿真。我们将在sw中详细讨论 QEMU 的使用,通过仿真加速产品开发 – QEMU

OpenEmbedded Core 提供了runqemu脚本工具,这是一个便捷的封装器,使得使用 QEMU 更加简便。运行该脚本工具的方法如下:

$ runqemu <machine> <zimage> <filesystem>

这里,<machine>是要使用的机器/架构,例如qemux86-64,或任何其他支持的机器。同时,<zimage>是内核的路径(例如,bzImage-qemux86-64.bin)。最后,<filesystem>ext4镜像的路径(例如,filesystem-qemux86-64.ext4)或 NFS 目录。在前面的runqemu调用中,<zimage><filesystem>都是可选的。只需运行runqemu,即可在已设置构建环境的 shell 中启动镜像,因为它会自动从构建环境中获取默认设置。

所以,例如,如果我们运行runqemu qemux86-64 core-image-full-cmdline,我们可以看到类似以下截图的内容:

图 2.5 – Linux 内核启动时的 QEMU 屏幕

图 2.5 – Linux 内核启动时的 QEMU 屏幕

完成 Linux 启动后,您将看到一个登录提示,如图 2.6所示:

图 2.6 – 用户登录时的 QEMU 屏幕

图 2.6 – 用户登录时的 QEMU 屏幕

我们可以使用空密码登录到root账户。即使在 QEMU 内部执行,系统的表现仍然像一台普通机器。将镜像部署到真实硬件的过程有所不同,取决于所使用的存储类型、引导加载程序等。然而,生成镜像的过程是相同的。我们将在第十五章中探讨如何构建并在真实硬件上运行镜像,启动我们的定制 嵌入式 Linux

总结

在本章中,我们学习了设置 Poky 并构建第一个镜像所需的步骤。然后,我们使用runqemu运行该镜像,从而对可用功能有了一个很好的概览。在下一章中,我们将介绍 Toaster,这是一个为 BitBake 提供的人性化接口。我们将使用它来构建镜像并进一步自定义它。

第三章:使用 Toaster 构建镜像

现在我们已经知道如何在 Poky 中使用 BitBake 构建镜像,接下来我们将学习如何使用 Toaster 做同样的事情。我们将重点介绍 Toaster 的最简单使用方式,并且还会覆盖它的其他功能,以便你了解它的能力。

什么是 Toaster?

Toaster 是一个配置和运行构建的网页界面。它与 BitBake 和 Poky 构建系统进行通信,管理并收集关于构建、软件包和镜像的信息。

使用 Toaster 有两种方式:

  • 本地模式:我们可以将 Toaster 作为本地实例运行,适合单用户开发,提供 BitBake 命令行的图形界面和一些构建信息。

  • 托管模式:适用于多个用户。Toaster 服务器构建并存储用户的产物。在使用托管实例时,其组件可以分布在多台机器上。

在本章中,我们将使用 Toaster 作为本地实例。然而,如果你希望将其作为托管实例使用,请访问以下网站获取说明——Toaster 手册docs.yoctoproject.org/4.0.4/toaster-manual/index.html)。

注意

请记住,所有托管服务都需要注意其安全性。在使用托管实例之前,请考虑这一点。

安装 Toaster

Toaster 使用 Python Django 框架。安装它最简单的方式是使用 Python 的 pip 工具。在配置我们的主机机器时,我们已经安装了这个工具,具体见 第二章 中的 构建我们的基于 Poky 的系统。现在,我们可以通过运行以下命令在 Poky 源目录中安装 Toaster 的其余需求:

$ pip3 install --user -r bitbake/toaster-requirements.txt

启动 Toaster

一旦我们安装了 Toaster 的需求,我们就可以启动它的服务器。为此,我们应该进入 Poky 的目录并运行以下命令:

$ source oe-init-build-env
$ source toaster start

这些命令需要一些时间来完成。当一切设置好后,Web 服务器会启动,结果如图所示。

图 3.1 – 源 Toaster 启动结果

图 3.1 – 源 Toaster 启动结果

要访问 Toaster 网页界面,打开你喜欢的浏览器并输入以下网址:

http://127.0.0.1:8000

注意

默认情况下,Toaster 在端口 8000 启动。webport 参数允许你使用不同的端口——例如,$ source toaster start webport=8400

接下来,我们可以看到 Toaster 的启动页面:

图 3.2 – Toaster 欢迎页面

图 3.2 – Toaster 欢迎页面

为 QEMU 构建镜像

按照 第二章 中使用的相同步骤,构建我们的基于 Poky 的系统,我们将构建 QEMU x86-64 模拟的镜像。

由于我们当前没有项目、配置集合和构建,我们需要启动一个新项目。创建项目名称并选择目标版本,如下图所示:

图 3.3 – 使用 Toaster 创建新项目

图 3.3 – 使用 Toaster 创建新项目

创建 my-first-project 后,我们可以看到主项目界面,如下图所示:

图 3.4 – 项目的首页

图 3.4 – 项目的首页

qemux86-64 上:

图 3.5 – 如何选择目标机器

图 3.5 – 如何选择目标机器

之后,点击 core-image-full-cmdline

图 3.6 – 如何使用搜索找到镜像

图 3.6 – 如何使用搜索找到镜像

下图展示了构建过程:

图 3.7 – Toaster 在镜像构建中的表现

图 3.7 – Toaster 在镜像构建中的表现

构建过程需要一些时间,但之后我们可以看到构建的镜像以及一些统计信息,如下图所示:

图 3.8 – 镜像构建工件报告

图 3.8 – 镜像构建工件报告

我们还可以验证生成的文件集,如下图所示:

图 3.9 – Toaster 中显示的 core-image-full-cmdline 目录结构

图 3.9 – Toaster 中显示的 core-image-full-cmdline 目录结构

Toaster 是一款强大的工具。你可以在本地开发机器或共享服务器上使用它,以图形方式展示构建过程。你可以返回到启动 Toaster 的终端运行 runqemu qemux86-64 core-image-full-cmdline,你将看到如下图所示的内容:

图 3.10 – Linux 内核启动时的 QEMU 屏幕

图 3.10 – Linux 内核启动时的 QEMU 屏幕

完成 Linux 启动后,你将看到登录提示,如图 3.11所示。

图 3.11 – 用户登录时的 QEMU 屏幕

图 3.11 – 用户登录时的 QEMU 屏幕

我们可以使用空密码登录到 root 账户。

总结

在本章中,我们介绍了 Toaster 及其核心功能。接着,我们讲解了如何安装和配置 Toaster,并构建并检查了一个镜像。

在下一章中,我们将介绍一些关键的 BitBake 概念。我们认为这些概念对于理解 Yocto 项目至关重要。接下来的章节,我们将使用 BitBake 和命令行,因为它们能全面展示所有概念。

第四章:认识 BitBake 工具

在本章中,我们将开始学习 Yocto 项目的引擎如何在幕后运作。就像每段旅程一样,沟通至关重要,因此我们需要理解 Yocto 项目工具使用的语言,并学习如何最大限度地利用这些工具来实现我们的目标。

上述章节向我们介绍了创建和仿真镜像的标准 Yocto 项目工作流程。现在,在本章中,我们将探讨元数据的概念,以及 BitBake 如何读取这些元数据以生成内部数据集合。

理解 BitBake 工具

BitBake 任务调度器最初是从 Gentoo 分发版中使用的包管理系统 Portage 分叉出来的。然而,由于不同的使用案例,这两个项目的开发方向发生了显著分歧。Yocto 项目和 OpenEmbedded 项目是 BitBake 的重度用户。它仍然是一个独立的项目,拥有自己的开发周期和邮件列表(bitbake-devel@lists.openembedded.org)。

BitBake 是一个类似于 GNU Make 的工具。如 第一章认识 Yocto 项目 中所述,BitBake 是一个任务执行器和调度器,它解析 Python 和 Shell 脚本混合代码。

因此,BitBake 负责尽可能并行地运行多个任务,同时确保这些任务按依赖关系执行。

BitBake 元数据集合

对于 BitBake 来说,元数据没有独立于元数据集合之外的存在。相反,元数据集合有一个独特的名称,Yocto 项目用来描述这些集合的常用术语是 Layer

第一章认识 Yocto 项目,解释了我们有以下几层:

  • meta 目录

  • meta-poky 目录

  • meta-yocto-bsp 目录

上面的列表描述了层的实际示例。每个层都包含一个名为 conf/layer.conf 的文件。该文件定义了几个层属性,如集合名称和优先级。下图展示了 meta-poky 层的 conf/layer.conf 文件:

图 4.1 – meta-poky 层的 conf/layer.conf 文件

图 4.1 – meta-poky 层的 conf/layer.conf 文件

上面的示例相对简单,但它作为一个基础,帮助我们说明 conf/layer.conf 文件的原理。

在第 8 行,BBFILE_COLLECTIONS,我们告诉 BitBake 创建一个名为 yocto 的新元数据集合。接下来,在第 9 行,BBFILE_PATTERN_yocto,我们定义了匹配所有以 LAYERDIR 变量开头的路径的规则,以识别属于 yocto 集合的元数据。最后,在第 10 行,BBFILE_PRIORITY_yocto 确定了 yocto 集合与其他元数据集合之间的优先级(数字越大,优先级越高)。

层之间的依赖关系至关重要,因为它确保所有所需的元数据可供使用。一个例子是在第 18 行的LAYERDEPENDS_yocto,它来自conf/layer.conf文件,并为core添加了一个依赖关系,core由 OpenEmbedded Core 层提供。

图 4.2展示了使用bitbake-layers命令的 Poky 层,具体如下:

图 4.2 – bitbake-layers show-layers 命令的 Poky 结果

图 4.2 – bitbake-layers show-layers 命令的 Poky 结果

元数据类型

我们可以将 BitBake 使用的元数据分为三个主要领域,具体如下:

  • 配置(.conf 文件)

  • 类(.bbclass 文件)

  • 配方(.bb.bbappend 文件)

配置文件定义了全局内容,用于提供信息并配置配方的工作方式。一个典型的配置文件示例是机器文件,它列出了描述硬件的设置。

整个系统使用配方可以根据需要或默认继承的类。它们定义了系统的常用行为并提供基本方法。例如,kernel.bbclass抽象了与构建和打包 Linux 内核相关的任务,而不受版本或供应商更改的影响。

注意

配方和类结合了 Python 和 Shell 脚本代码。

类和配方描述了要运行的任务,并提供了所需的信息,允许 BitBake 生成所需的任务列表及其依赖关系。继承机制允许配方继承一个或多个类,从而促进代码重用,提高准确性,并简化维护。一个 Linux 内核配方示例是linux-yocto_5.15.bb,它继承了一组类,包括kernel.bbclass

BitBake 在所有类型的元数据(.conf.bb.bbclass)中最常用的方面已在第五章《掌握 BitBake 工具》中进行了涵盖,而元数据语法和语法的详细内容则在第八章《深入了解 BitBake 元数据》中进行了阐述。

考虑到图 4.1,我们需要注意另外两个变量——BBPATHBBFILES

第 2 行的BBPATH类似于PATH,但它将目录添加到元数据文件的搜索列表中;第 5 行的BBFILES变量列出了用于索引集合配方文件的模式。

概要

在本章中,我们了解了元数据、元数据收集的概念以及conf/layer.conf的重要性,这是理解 Yocto 项目的基础。在下一章中,我们将研究元数据知识,了解配方如何相互依赖,以及 BitBake 如何处理依赖关系。此外,我们还将更好地了解 BitBake 管理的任务,下载所有必需的源代码,构建并生成包,并查看这些包如何适配生成的镜像。

第五章:掌握 BitBake 工具

在上一章中,我们了解了元数据、元数据收集概念以及 conf/layer.conf 的重要性。在本章中,我们将更深入地研究元数据,理解配方之间的依赖关系,并了解 BitBake 如何处理这些依赖关系。

此外,我们还将介绍一系列任务,从下载源代码到生成镜像和其他制品。这些任务的一些示例包括将源代码存储在用于构建的目录中、打补丁、配置、编译、安装、生成软件包,并确定软件包如何适应生成的镜像,我们将在本章中介绍这些内容。

解析元数据

通常,我们的项目包含多个层,这些层提供不同的元数据以满足特定需求。例如,当我们初始化构建目录时,使用 source oe-init-build-env build,会生成如下文件:

图 5.1 – 使用 source oe-init-build-env build 创建的文件列表

图 5.1 – 使用 source oe-init-build-env build 创建的文件列表

build/conf/templateconf.cfg 文件指向用于创建 build/conf 目录的模板目录。

注意

用户可以使用 TEMPLATECONF 环境变量提供一个不同的模板目录——例如,TEMPLATECONF=/some/dir source oe-init-build-env build

build/conf/local.conf 文件是本地配置的占位符。我们在 第二章,《烘焙我们的第一个基于 Poky 的系统》中使用了此文件,并将在本书中贯穿使用。

BitBake 使用 build/conf/bblayers.conf 文件列出构建环境中考虑的层。以下是一个示例:

图 5.2 – 使用 source oe-init-build-env build 后的 build/conf/bblayer.conf 内容

图 5.2 – 使用 source oe-init-build-env build 后的 build/conf/bblayer.conf 内容

BBLAYERS 变量(第 8 行)是一个以空格分隔的层目录列表。BitBake 解析每个层并将其内容加载到元数据集合中。BitBake 使用的元数据可以分为三大类,具体如下:

  • 配置(.conf 文件)

  • 类(.bbclass 文件)

  • 配方(.bb.bbappend 文件)

提示

BBLAYERS 变量中列出的层的顺序由 BitBake 按照从左到右的顺序解析元数据。因此,如果您的层需要最先解析,请确保将其列在 BBLAYERS 变量中的正确位置。

在解析所有使用的层之后,BitBake 开始解析元数据。BitBake 中第一个解析的元数据是配置元数据,它的文件扩展名为 .conf。这些元数据是全局性的,因此会影响所有执行的配方和任务。

注意

一个典型的配置文件示例是机器文件,它包含描述硬件的设置列表。

BitBake 首先从 BBPATH 列表中包含的路径之一加载 meta/conf/bitbake.confmeta/conf/bitbake.conf 文件使用 include 指令来引入元数据,例如特定架构的元数据、机器配置文件和 build/conf/local.conf 文件。BitBake 配置文件(.conf)的一个显著限制是,只允许变量定义和 include 指令。

BitBake 的类(.bbclass)是 classes/ 目录中的一个基本继承机制。当在解析过程中出现 inherit 指令时,BitBake 会立即解析链接的类。类的内容会根据 BBPATH 变量列表的顺序进行搜索。

BBFILES 变量是一个以空格分隔的 .bb.bbappend 文件列表,并且可以使用通配符。它在每个层的 conf/layer.conf 中都是必需的,这样 BitBake 就知道去哪里查找配方。BitBake 配方(.bb)是要执行的任务的逻辑单元;通常,它指的是一个包。

依赖关系

从 BitBake 的角度来看,有三种不同的依赖类型:

  • 构建时

  • 执行时间

  • 任务

需要其他包(如库)的应用程序,在成功编译时有构建依赖关系。构建依赖关系包括编译器、库和本地构建工具(如 CMake)。此外,当应用程序仅在执行时需要时,构建依赖关系就会变成执行依赖关系。运行时依赖关系包括字体、图标、动态链接库和语言解释器。

提示

在 Poky 中的约定是对配方名称使用 -native 后缀。这是因为这些工具旨在在构建过程中运行,在主机构建系统中执行,而不会部署到目标系统中。

任务依赖关系在任务执行的混乱中创建了顺序——例如,为了编译一个包,需要先下载源代码。在底层,所有依赖关系都是任务依赖关系。这意味着,当包 B 依赖包 A 进行构建时,包 A 的任务需要先完成,包 B 才能开始。

元数据表示所有依赖关系。OpenEmbedded Core 提供了一组广泛的类来处理常用的默认任务依赖关系——例如,一个配方可以通过 DEPENDS 变量表示构建时的依赖关系,通过 RDEPENDS 变量表示执行时的依赖关系。

了解配方依赖链,BitBake 可以将所有配方按可行顺序排序。BitBake 以以下方式组织任务:

  • 没有依赖关系的配方任务会并行构建。

  • 依赖的配方按顺序串行构建,并以满足依赖关系的方式进行排序。

提示

所有在运行时依赖中包含的配方都会被添加到构建列表中。这看起来很明显,但即使它们在构建过程中没有任何作用,它们仍然需要准备好,以便最终生成的二进制包可供安装。这在构建镜像或填充源时是必需的。

偏好和提供配方

依赖是两者之间的关系;一方只有在另一方存在时才能被满足。然而,依赖仅指定某个功能或特性需要被满足,并不具体说明如何满足。

例如,当一个配方依赖于A时,最初的想法是它依赖于一个名为A的配方。然而,有两种可能的方式来满足A的依赖需求:

  • 一个名为A的配方

  • 提供名为A的功能或特性的配方

为了让一个配方告诉 BitBake 它可以满足某个功能或特性需求,必须使用PROVIDES关键字。一个微妙的后果是,两个或更多的配方可以提供相同的功能或特性。我们必须使用PREFERRED_PROVIDER关键字告知 BitBake 哪个配方应该满足该需求。

因此,如果一个名为foo_1.0.bb的配方依赖于bar,BitBake 会列出所有提供bar的配方。bar的依赖可以通过以下方式满足:

  • 一个以bar_<version>.bb格式命名的配方,因为每个配方默认提供自己

  • 一个包含barPROVIDES变量的配方

virtual/kernel 提供者清楚地展示了这一机制。virtual/命名空间是当我们有一组常见的覆盖提供者时所采用的约定。

所有需要构建内核的配方都可以将virtual/kernel添加到依赖列表(DEPENDS)中,BitBake 会满足该依赖。当我们有多个提供者时,我们必须选择一个来使用——例如,以下情况:

图 5.3 – 如何为虚拟/内核设置首选提供者的示例

图 5.3 – 如何为虚拟/内核设置首选提供者的示例

virtual/kernel 提供者通常在机器定义文件中设置,因为它可能因机器不同而有所变化。我们将在第十二章 创建 自定义层中看到如何创建机器定义文件。

注意

当依赖无法满足时,BitBake 会报错,这通常是因为缺少提供者。

当 BitBake 有两个不同版本的提供者时,它默认使用最高版本。然而,我们可以通过使用PREFERRED_VERSION强制 BitBake 使用不同的版本。这在 BSP(如引导加载程序)中很常见,因为厂商可能会为某个板子使用特定版本。

我们可以通过在配方文件中使用DEFAULT_PREFERENCE关键字,默认降低版本偏好来避免使用开发版或不可靠的配方版本,如下所示:

图 5.4 – 如何在配方中降低版本优先级

图 5.4 – 如何在配方中降低版本优先级

因此,即使版本较高,如果没有显式设置PREFERRED_VERSION来使用该版本,配方也不会被选择。

获取源代码

当我们下载 Poky 源代码时,我们同时下载了元数据集合和 BitBake 工具。BitBake 支持的主要功能之一是额外的源代码获取。

获取外部源代码的能力尽可能模块化和灵活。例如,每个基于 Linux 的系统都包括 Linux 内核和一些其他工具,这些工具共同构成了root文件系统,如 OpenSSH 或 BusyBox。

OpenSSH 源代码可通过其上游网站获得,作为一个托管在 HTTP 服务器上的tar.gz文件,而 Linux 内核发布版本则在一个 Git 仓库中。因此,BitBake 可以轻松获取这两种不同的源代码实例。

BitBake 支持许多不同的 fetcher 模块,允许检索 tar 包文件和其他多个 SCM 系统,例如以下几种:

  • 亚马逊 AWS S3

  • Android repo

  • Azure 存储

  • Bazaar

  • ClearCase

  • CVS

  • FTP

  • Git

  • Git Annex

  • Git 子模块

  • HTTP(S)

  • Mercurial

  • NPM

  • NPMSW(npm shrinkwrap实现)

  • openSUSE 构建服务客户端

  • Perforce

  • Rust Crate

  • SFTP

  • SSH

  • Subversion

BitBake 用于获取源代码的机制在内部称为 fetcher 后端,可以根据用户的需求进行配置,并优化源代码的获取。

远程文件下载

BitBake 支持多种远程文件下载方式,最常用的包括http://https://git://。我们不会讨论 BitBake 处理远程文件下载的内部细节,而是专注于它的可见效果。

当 BitBake 在配方中执行do_fetch任务时,它会检查SRC_URI的内容。以pm-utils配方为例(可以在meta/recipes-bsp/pm-utils/pm-utils_1.4.1.bb中找到)。处理后的变量显示在下图中:

图 5.5 – pm-utils_1.4.1.bb 配方的 SRC_URI

图 5.5 – pm-utils_1.4.1.bb 配方的 SRC_URI

BitBake 将PV变量扩展为包的版本号(在此示例中,1.4.1来自pm-utils_1.4.1.bb配方文件名),然后从pm-utils.freedesktop.org/releases/pm-utils-1.4.1.tar.gz下载文件,最后将其保存在DL_DIR指定的下载存储目录中。

下载完成后,BitBake 会将下载文件的sha256sum值与配方中的值进行比较。如果值匹配,它会创建一个${DL_DIR}/pm-utils-1.4.1.tar.gz.done文件,以标记文件已成功下载和验证,允许 BitBake 重用该文件。

注意

默认情况下,DL_DIR变量指向build/downloads。你可以通过在build/conf/local.conf文件中添加以下行来覆盖此设置 – DL_DIR = "/my/download-cache"。通过这种方式,我们可以在多个构建目录之间共享相同的下载缓存,从而节省下载时间和带宽。

Git 仓库

最常用的源代码管理系统之一是 Git。BitBake 对 Git 提供了稳定的支持,当运行do_fetch任务并发现SRC_URI变量开头是git:// URL 时,会使用 Git 后端。

BitBake Git 后端处理仓库的默认方式是将仓库克隆到${DL_DIR}/git2/<git URL> – 例如,查看以下从 meta/recipes-support/lz4/lz4_1.9.4.bb 配方中找到的lz4_1.9.4.bb文件:

图 5.6 – lz4_1.9.4.bb 配方的源代码下载配置

图 5.6 – lz4_1.9.4.bb 配方的源代码下载配置

在这里,lz4.git 仓库被克隆到 ${DL_DIR}/git2/github.com.lz4.lz4.git。这个目录名避免了可能存在相同项目名的 Git 仓库之间的冲突。

SRCREV 变量有两个影响的情况。它们如下:

  • do_fetch:此任务使用SRCREV变量来确保仓库具有所需的 Git 版本。

  • do_unpack:此任务使用SRCREV来设置工作目录并获取必要的源代码版本。

注意

我们需要使用branch=<branch name>参数,如下所示 – SRC_URI = "git://myserver/myrepo.git;branch=mybranch",来指定包含我们要使用的版本的分支。如果使用的哈希指向一个在分支上不可用的标签,则需要使用nobranch=1选项,如下所示 – SRC_URI = "git://myserver/myrepo.git;nobranch=1"

远程文件和 Git 仓库是 BitBake 最常用的获取后端。其他支持的源代码管理系统在实现上有所不同,但基本的理念和概念是相同的。

优化源代码下载

为了提高源代码下载的稳定性,Poky 提供了一个镜像机制,可以提供以下内容:

  • 用于下载的集中首选服务器

  • 一组回退服务器

为了提供这个强大的下载机制,BitBake 遵循了定义的逻辑步骤。在构建过程中,第一步是从本地下载目录(由DL_DIR指定)中搜索源代码。如果这一步失败,下一步是尝试由PREMIRRORS变量定义的位置。最后,BitBake 会在失败的情况下搜索MIRRORS变量指定的位置。总的来说,这些步骤如下:

  1. DL_DIR:在主机机器上查找下载。

  2. MIRRORS:在镜像列表中搜索下载。

  3. PREMIRRORS:用于减少从外部服务器的下载,通常在公司内部使用,以减少或禁止互联网使用。

例如,当配置本地服务器 https://mylocalserver 作为 PREMIRROR 时,我们可以在全局配置文件(例如 build/conf/local.conf)中添加以下代码:

图 5.7 – PREMIRRORS 配置示例

图 5.7 – PREMIRRORS 配置示例

上述代码将 PREMIRRORS 变量添加到前面,并指示构建系统拦截所有下载请求。它会将请求重定向到 https://mylocalserver 源的镜像。

使用 PREMIRRORS 的方式非常常见,以至于有一个类可以帮助其配置。为了简化操作,我们继承 own-mirror 类,然后在任何全局配置文件中(例如 build/conf/local.conf)设置 SOURCE_MIRROR_URL 变量为 https://mylocalserver

图 5.8 – 如何配置 own-mirror

图 5.8 – 如何配置 own-mirror

如果所需的组件在源镜像中不可用,BitBake 会回退到 MIRRORS 变量。该变量内容的示例如下图所示。它显示了在 mirrors.bbclass 中使用的一些服务器,默认情况下会继承该类:

图 5.9 – 使用 MIRRORS 变量的示例

图 5.9 – 使用 MIRRORS 变量的示例

提示

假设目标是拥有一个可共享的下载缓存。在这种情况下,建议在下载文件夹中启用 SCM 后端(例如 Git)的 tarball 生成,方法是在 build/conf/local.conf 中设置 BB_GENERATE_MIRROR_TARBALLS = "1"

禁用网络访问

有时,我们需要确保在构建过程中不连接到互联网。这样做有几个有效的理由,举例如下:

  • 政策:我们的公司不允许在产品中包含未经合法验证和审查的外部源。

  • 网络费用:当我们在路上使用移动宽带时,数据费用可能过高,因为下载的数据量可能非常庞大。

  • 下载与构建解耦:这种设置在持续集成环境中很常见,其中一个任务负责下载所有所需的源代码,而构建任务则禁用互联网访问。下载与构建的解耦确保不会重复下载源代码,并且我们已经缓存了所有必要的源代码。

  • 缺乏网络访问:有时我们无法访问网络。

为了禁用网络连接,我们需要在 build/conf/local.conf 文件中添加以下代码:

图 5.10 – 如何在构建过程中禁用网络访问

图 5.10 – 如何在构建过程中禁用网络访问

理解 BitBake 的任务

BitBake 使用执行单元,实质上是一组按顺序执行的集群指令。这些单元被称为任务。在每个配方的构建过程中,BitBake 会调度、执行并检查由类提供的许多任务,从而形成我们用来构建配方的框架。因此,理解其中的一些任务非常重要,因为我们在编写配方时,经常会使用、扩展、实现或替换它们。

当我们运行以下命令时,BitBake 会运行一组已安排的任务:

图 5.11 – 如何运行配方的所有任务

图 5.11 – 如何运行配方的所有任务

当我们希望运行特定任务时,可以使用以下命令:

图 5.12 – 如何运行特定任务

图 5.12 – 如何运行特定任务

要列出为配方定义的任务,我们可以使用以下命令:

图 5.13 – 如何列出配方的所有任务

图 5.13 – 如何列出配方的所有任务

wget 配方的 listtasks 输出如下:

图 5.14 – wget 配方的任务列表

图 5.14 – wget 配方的任务列表

我们将在这里简要描述最常用的任务:

  • do_fetch:构建配方的第一步是使用前面本章中讨论的获取后端功能来获取所需的源代码。需要注意的是,获取源代码或文件并不意味着它是远程源。

  • do_unpack:在 do_fetch 任务之后的自然任务是 do_unpack。它负责解包源代码,或在引用的源代码使用 SCM 系统的情况下,检出请求的修订版本或分支。

  • do_patch:一旦源代码被正确解包,BitBake 会启动源代码的适配过程。所有通过 do_fetch 获取的具有 .patch 扩展名的文件都被认为是需要应用的补丁。此任务应用所需的补丁列表。最终修改后的源代码将用于构建软件包。

  • do_configuredo_compiledo_install:这些任务按照此顺序执行。需要注意的是,这些任务中定义的环境变量在不同任务之间是不同的。Poky 提供了一个丰富的预定义任务集合,在类中,我们应该尽可能使用这些任务——例如,当一个配方继承了 autotools 类时,它提供了 do_configuredo_compiledo_install 任务的已知实现。

  • do_packagedo_package 任务将配方安装的文件分割为逻辑组件,例如调试符号、文档和库。我们将在第七章《吸收包支持》中更深入地介绍打包的细节。

概述

在本章中,我们学习了配方如何相互依赖,以及 Poky 如何处理依赖关系。我们了解了如何配置下载以及如何优化它。此外,我们还更好地理解了 BitBake 如何管理任务,下载所有所需的源代码,并利用这些代码来构建和生成包。

在下一章中,我们将查看在完成镜像生成后构建目录的内容,并了解 BitBake 如何在烘焙过程中使用这些内容,包括临时构建目录及其生成的文件。

第六章:详细说明临时构建目录

在本章中,我们将尝试理解镜像生成后临时构建目录的内容,并了解 BitBake 如何在烘焙过程中使用它。此外,我们还将了解这些目录如何在出现问题时,通过作为宝贵的信息源来帮助我们。

详细说明构建目录

构建目录是每个 Poky 用户的核心信息和产物源。它的主要目录如下:

  • conf:此目录包含我们用来控制 Poky 和 BitBake 的配置文件。我们第一次使用该目录是在第二章,《烘焙我们的基于 Poky 的系统》中。它存储了配置文件,如build/conf/local.confbuild/conf/bblayers.conf

  • downloads:此目录存储所有下载的构建产物,充当下载缓存。我们在第五章,《掌握 BitBake 工具》中详细讨论过它。

  • sstate-cache:此目录包含打包数据的快照。它是一个缓存,主要用于加速未来的构建过程,因为它作为构建过程的缓存。这一文件夹在第七章,《掌握打包支持》中有详细介绍。

  • tmp:这是临时构建目录,是本章的主要内容。

构建目录的构建

在前面的章节中,我们以抽象的高层次细节了解了 Poky 的输入和输出。我们已经知道,BitBake 使用元数据生成不同类型的构建产物,包括镜像。除了生成的构建产物,BitBake 在此过程中还创建了其他内容,这些内容可能根据我们的目标以不同的方式使用。

BitBake 执行多个任务,并在构建过程中不断修改构建目录。因此,我们可以通过遵循常见的 BitBake 执行流程来更好地理解它,流程如下:

  • build/download 目录。

  • build/tmp/work 目录。当源代码准备好后,将应用必要的修改(例如,应用必要的补丁并签出正确的 Git 修订版)。

  • ./configure)和构建(例如,make)。

  • make install)在build/tmp/work/<...>/image下的暂存目录中。

  • build/tmp/work/<...>/recipe-sysrootbuild/tmp/work/<...>/recipe-sysroot-native

  • .rpm.ipk.deb.tar

  • 质量保证(QA)检查:在构建配方时,构建系统对输出执行各种 QA 检查,以确保能够检测到并报告常见问题。

探索临时构建目录

了解临时构建目录(build/tmp)至关重要。临时构建目录在构建开始后立即创建,并且对于帮助我们识别某些行为未按预期发生的原因非常重要。

下图显示了build/tmp目录的内容:

图 6.1 – build/tmp 目录的内容

图 6.1 – build/tmp 的内容

其中最关键的目录如下所示:

  • deploy:该目录包含构建产物,如映像、二进制包和 SDK 安装程序。

  • sysroots-components:该目录包含 recipes-sysrootrecipes-sysroot-native 的表示,允许 BitBake 知道每个组件安装的位置。这用于在构建过程中创建特定配方的 sysroots

  • sysroots-uninative:该目录包含 glibc(C 库),当生成本地工具时使用它。反过来,这有助于在不同主机发行版之间共享状态工件的重用。

  • work:该目录包含工作源代码、任务的配置、执行日志以及生成的包的内容。

  • work-shared:这是一个用于与多个配方共享源代码的 work 目录。work-shared 仅用于一部分配方,例如 linux-yoctogcc

理解工作目录

build/tmp/work 目录按架构组织。例如,当使用 qemux86-64 机器时,我们有以下四个目录:

图 6.2 –  目录的内容

图 6.2 – build/tmp/work 目录的内容

图 6.2 显示了 x86-64 主机和 qemux86-64 目标下 build/tmp/work 目录可能的目录结构示例。它们是架构和机器相关的,如下所示:

  • all-poky-linux:该目录包含架构无关的软件包的工作构建目录。这些通常是脚本或基于解释语言的软件包,例如 Perl 脚本和 Python 脚本。

  • core2-64-poky-linux:该目录包含针对 x86-64 基于目标并使用为 core2-64 优化的包的工作构建目录。

  • qemux86_64-poky-linux:该目录包含针对 qemux86-64 机器的软件包的工作构建目录。

  • x86_64-linux:该目录包含面向构建主机机器运行的软件包的工作构建目录。

这种组件化结构是必需的,以便在一个 build 目录内为多个机器和架构构建系统映像和软件包,而不会发生冲突。我们将使用的目标机器是 qemux86-64

build/tmp/work 目录在检查异常行为或构建失败时非常有用。其内容按照以下模式组织在子目录中:

图 6.3 – 用于  目录下子目录的模式

图 6.3 – 用于 build/tmp/work 目录下子目录的模式

图 6.3 中显示的树形结构下的某些目录如下所示:

  • <sources>:这是将要构建的软件的提取源代码。WORKDIR 变量指向该目录。

  • image:该目录包含由配方安装的文件。

  • package:输出包的提取内容存储在这里。

  • packages-split:输出包的内容被提取并分割成子目录,存储在这里。

  • temp:存储 BitBake 的任务代码和执行日志。

提示

我们可以在每次配方编译周期后自动删除work目录,以减少磁盘使用量,只需在build/conf/local.conf文件中添加INHERIT += "rm_work"

work目录的结构对于所有架构都是相同的。对于每个配方,都会创建一个以配方名称命名的目录。以特定于机器的work目录并以sysvinit-inittab配方为例,我们看到以下内容:

图 6.4 – build/tmp/work/core2-64-poky-linux/pm-utils/1.4.1-r1/的内容

图 6.4 – build/tmp/work/core2-64-poky-linux/pm-utils/1.4.1-r1/的内容

sysvinit-inittab配方是一个很好的例子,因为它是特定于机器的。该配方包含定义串行控制台以启动登录过程的inittab文件,而该过程因机器而异。

注意

构建系统使用前面图中显示的目录,但这些目录在这里未详细介绍。因此,除非你从事构建工具开发,否则你不需要与这些目录打交道。

work目录对于调试非常有用,我们将在第十章中讲解,使用 Yocto 项目进行调试

理解 sysroot 目录

sysroot目录在 Yocto 项目中起着至关重要的作用。它为每个配方创建一个独立且隔离的环境。这个为每个配方设置的环境对于确保可重现性并避免与主机机器的包污染是必不可少的。

在构建procps配方版本3.3.17之后,我们会得到两套sysroot目录——recipes-sysrootrecipes-sysroot-native

在每个sysroot集合内部,有一个名为sysroot-provides的子目录。该目录列出了安装在各自sysroot上的包。以下是recipe-sysroot目录:

图 6.5 – recipe-sysroot 目录下的内容,位于 build/tmp/work,适用于 procps 配方

图 6.5 – recipe procps 的 recipe-sysroot 目录内容

recipe-sysroot-native目录包含在构建过程中用于主机系统的构建依赖项。它包括编译器、链接器、工具等。同时,recipe-sysroot目录包含目标代码中使用的库和头文件。下图显示了recipe-sysroot-native目录:

图 6.6 – recipe-sysroot-native 目录下的内容,位于 build/tmp/work,适用于 procps 配方

图 6.6 – recipe procps 的 recipe-sysroot-native 目录内容

当我们看到缺少头文件或链接失败时,我们必须仔细检查我们的sysroot目录(目标和主机)内容是否正确。

总结

在本章中,我们探讨了生成镜像后临时构建目录的内容。我们看到了 BitBake 在构建过程中如何使用它。

在下一章中,我们将更好地理解在 Poky 中如何进行打包,如何使用软件包源、软件包修订版PR)服务,以及它们如何帮助我们进行产品维护。

第七章:同化打包支持

本章介绍了理解与打包相关的 Poky 和 BitBake 方面的关键概念。我们将了解支持的二进制包格式、共享状态缓存、包版本组件,如何设置和使用二进制包源来支持我们的开发过程等内容。

使用支持的包格式

从 Yocto 项目的角度来看,一个配方可能会生成一个或多个输出包。一个包将一组文件和元数据封装起来,使其在未来可用。它们可以被安装到一个或多个镜像中,或者部署以供后续使用。

包对于 Poky 至关重要,因为它们使得构建系统能够生成各种类型的工件,如镜像和工具链。

支持的包格式列表

当前,BitBake 支持四种不同的包格式:

  • Red Hat 包管理器 (RPM):最初名为 Red Hat 包管理器,但现在被称为 RPM 包格式,因为它被多个其他 Linux 发行版采用,这是在 Linux 发行版中广泛使用的格式,如 SuSE、OpenSuSE、Red Hat、Fedora 和 CentOS。

  • Debian 包管理器 (DEB):这是一个广泛使用的格式,应用于 Debian 和多个其他基于 Debian 的发行版——Ubuntu Linux 和 Linux Mint 是最广为人知的两个。

  • 支持 IPK 格式的 opkg 包管理器,已在多个发行版中使用,如 OpenEmbedded Core、OpenWRT 和 Poky。

  • tarball 文件类型用于将多个文件打包成一个单一文件。

选择包格式

格式支持是通过一组类(例如 package_rpmpackage_debpackage_ipk)提供的。我们可以使用 PACKAGE_CLASSES 变量选择一个或多个格式,如以下示例所示:

图 7.1 – 用于配置使用哪种包格式的变量

图 7.1 – 用于配置使用哪种包格式的变量

你可以配置一个或多个包格式——例如,在 build/conf/local.conf 文件中。

提示

PACKAGE_CLASSES 中的第一个包格式是用于生成镜像的格式。

Poky 默认使用 RPM 包格式,该格式使用 DNF 包管理器。然而,选择包格式取决于多个因素,例如特定格式的特性、内存和资源使用。OpenEmbedded Core 默认使用 IPK 格式和 opkg 作为包管理器,因为它提供了更小的内存和资源占用。

另一方面,熟悉基于 Debian 的系统的用户可能更喜欢为他们的产品使用 APT 和 DEB 包格式。

在包安装过程中运行代码

包可以在安装和移除过程中使用脚本。所包含的脚本定义如下:

  • preinst:此脚本在解包包之前执行。如果包包含服务,它必须在安装或升级之前停止这些服务。

  • postinst:解包后,通常完成包的任何必要配置。许多postinst脚本在安装或升级后执行任何必要的命令来启动或重启服务。

  • prerm:它通常在删除与包相关的文件之前停止任何与包相关的守护进程。

  • postrm:这通常修改包创建的链接或其他文件。

preinstprerm脚本针对复杂的使用场景,例如在更新包时进行数据迁移。在 Yocto 项目中,postinstpostrm还负责停止和启动systemdsysvinit服务。当我们使用systemdupdate-rc.d类时,会提供默认脚本。它可以定制以包含任何特定的用例。

后安装(postinst)脚本在创建root文件系统时运行。如果脚本返回成功值,包会被标记为已安装。要为包添加postinst脚本,我们可以使用以下方法:

图 7.2 – pkg_postinst 脚本示例

图 7.2 – pkg_postinst 脚本示例

有时,我们需要确保postinst脚本在目标设备本身内运行。这可以通过使用postinst_ontarget变体来实现,例如以下示例:

图 7.3 – pkg_postinst_ontarget 脚本示例

图 7.3 – pkg_postinst_ontarget 脚本示例

提示

我们可以使用PN变量来代替包名本身,它会自动展开配方的包名。

所有后安装脚本在生成包含read-only-rootfs的镜像时必须成功,因为在只读rootfs中无法写入,检查必须在构建时进行。这确保我们在构建镜像时识别问题,而不是在目标设备的初次启动时。如果有需求在目标设备内运行任何脚本,do_rootfs任务将失败。

提示

最终,将整个镜像设为只读是不可行的。例如,一些项目可能需要持久化一些数据,甚至允许某些应用程序写入一个易失的目录。此类使用场景超出了本书的范围。然而,你可能会在Yocto 项目参考手册中找到有关overlayfsdocs.yoctoproject.org/4.0.4/ref-manual/classes.html#overlayfs-bbclass)和overlayfs-etcdocs.yoctoproject.org/4.0.4/ref-manual/classes.html#overlayfs-etc-bbclass)类的有用信息。

在创建后安装脚本时,一个常见的疏忽是没有在绝对路径前加上D变量。D有两个特性:

  • rootfs生成过程中,D被设置为工作目录的根目录

  • 在设备内,D 是空的

因此,这确保了路径在主机和目标环境中都是有效的。例如,考虑以下代码:

图 7.4 – 使用 D 变量的示例源代码

图 7.4 – 使用 D 变量的示例源代码

图 7.4 中的示例中,touch 命令使用了 D 变量,因此它根据其值通用地工作。

另一个常见的错误是尝试运行特定于或依赖于目标架构的进程。在这种情况下,最简单的解决方案是将脚本执行推迟到目标(使用pkg_postinst_ontarget)。然而,正如之前提到的,这会阻止使用只读文件系统。

理解共享状态缓存

Poky 的默认行为是从头开始构建所有内容,除非 BitBake 确定某个配方无需重新构建。从头开始构建的主要优势是结果是全新的,并且不会有以前的数据导致问题的风险。然而,重新构建所有内容需要计算时间和资源。

判断是否需要重新构建配方的策略非常复杂。BitBake 尝试尽可能多地追踪构建过程中每个任务、变量和代码块的信息。然后,BitBake 为每个任务使用的信息生成校验和,包括来自其他任务的依赖关系。总之,BitBake 会递归地追踪使用的变量、任务源代码和配方及其依赖关系。

Poky 使用 BitBake 提供的所有这些信息,将这些任务的快照作为一组打包数据存储在一个名为共享状态缓存(sstate-cache)的缓存中。这个缓存将每个任务输出的内容包装在存储在 SSTATE_DIR 目录中的包中。每当 BitBake 准备运行某个任务时,它首先检查是否存在与所需的计算校验和匹配的 sstate-cache 包。如果该包存在,BitBake 将使用预构建的包。

整个共享状态机制涵盖了相当复杂的代码,前面的解释对此进行了简化。为了获得详细的描述,建议你阅读 Yocto 项目概述和概念 手册 中的 共享状态缓存 部分(docs.yoctoproject.org/4.0.4/overview-manual/concepts.html#shared-state-cache)。

在使用 Poky 进行多次构建时,我们必须记住,sstate-cache 需要定期清理,因为它在每次构建后都会不断增长。清理它有一个直接的方法。请在 poky 目录下使用以下命令:

图 7.5 – 移除重复的共享状态缓存的命令行

图 7.5 – 移除重复的共享状态缓存的命令行

提示

当我们需要从头开始重新构建时,可以执行以下任一操作:

  • 删除 build/tmp 以便我们可以使用 sstate-cache 来加速构建

  • 删除 build/tmpsstate-cache,以便在构建过程中不重用任何缓存

解释包版本控制

包版本控制用于区分同一包在其生命周期不同阶段的版本。从 Poky 的角度来看,它也作为生成 BitBake 用于验证是否需要重新构建任务的校验和的公式的一部分。

包版本,也称为 PV,在我们选择构建哪个配方时起着主导作用。Poky 的默认行为是总是优先选择最新的配方版本,除非有其他明确的偏好,如第五章《掌握 BitBake 工具》中所讨论的那样。例如,假设我们有两个版本的 myrecipe 配方:

  • myrecipe_1.0.bb

  • myrecipe_1.1.bb

默认情况下,BitBake 会构建版本为 1.1 的配方。在配方内部,我们可能有其他变量与 PV 变量共同组成包的版本信息。这些变量是 PEPR

这些变量通常遵循以下模式:

图 7.6 – 完整的版本模式

图 7.6 – 完整的版本模式

PE 变量的默认值为零。当包版本模式发生变化,打破常规排序的可能性时,PE 会被使用。PE 会被加到包版本中,在需要时强制使用更高的数字。

例如,假设一个包使用日期来构成 PV 变量,如 20220101,并且有版本模式更改来发布 1.0 版本。那么,无法确定版本 1.0 是否高于版本 20220101。因此,使用 PE = "1" 来更改配方纪元,强制版本 1.0 高于版本 20220101,因为 1:1.0 大于 0:20220101

PR 变量的默认值为 r0,并且是包版本的一部分。当它被更新时,会强制 BitBake 重新构建特定配方的所有任务。我们可以在配方元数据中手动更新它,以强制我们知道需要重新构建的内容。但这是一种脆弱的做法,因为它依赖于人工干预和知识。BitBake 使用任务校验和来控制需要重新构建的内容。手动递增 PR 仅在任务校验和没有变化时在极少数情况下使用。

指定运行时包依赖关系

大多数配方的结果是由包管理器管理的包。正如我们在前面几节中看到的,它需要关于这些包及其相互关系的信息。例如,一个包可能依赖或与另一个包冲突。

包之间存在多重约束关系;然而,这些约束是特定于包格式的,因此 BitBake 有特定的元数据来抽象这些约束。

下面是最常用的包运行时约束的列表:

  • RDEPENDS:必须在运行时可用的包列表,连同定义它的包一起使用。

  • RPROVIDES:这是软件包提供的符号名称列表。默认情况下,软件包始终将软件包名称作为符号名称。它也可以包括该软件包提供的其他符号名称。

  • RCONFLICTS:这是已知与该软件包冲突的软件包列表。最终的镜像必须不包括冲突的软件包。

  • RREPLACES:这是一个符号名称列表,表示软件包可以替换的名称。

来自meta/recipes-devtools/python/python3-dbus_1.2.18.bb的完整配方如下:

图 7.7 – 如何使用 RDEPENDS 的示例

图 7.7 – 如何使用 RDEPENDS 的示例

图 7.6中的配方显示,python3-dbus软件包在第 21 行列出了几个 Python 模块的运行时依赖关系。

使用软件包生成 rootfs 镜像

Poky 最常用的功能之一是rootfs镜像生成。rootfs镜像应该视为一个用于目标的可用根文件系统。该镜像可以由一个或多个文件系统组成。它可能包括在生成过程中可用的其他工件,如 Linux 内核、设备树和引导加载程序二进制文件。生成镜像的过程包含多个步骤,其最常见的用途如下:

  1. 生成rootfs目录

  2. 创建所需的文件

  3. 根据特定需求封装最终的文件系统(它可能是一个包含多个分区和内容的磁盘文件)

  4. 最后,如果适用的话,进行压缩

do_rootfs的子任务执行所有这些步骤。rootfs是一个包含所需软件包并且之后应用了必要调整的目录。这些调整对rootfs的内容进行细微修改——例如,在构建开发镜像时,rootfs会被调整,以便我们可以无密码登录为root

要安装到rootfs中的软件包列表由IMAGE_INSTALL列出的软件包和IMAGE_FEATURES中包含的软件包的联合定义;镜像定制的详细信息见第十二章创建自定义层。每个镜像功能可以包括额外的软件包供安装——例如,dev-pkgs,它安装列出要安装到rootfs中的所有软件包的开发库和头文件。

要安装的软件包列表现在由PACKAGE_EXCLUDE变量过滤,该变量列出了不应安装的软件包。列在PACKAGE_EXCLUDE中的软件包仅从明确要安装的软件包列表中排除。

在确定最终安装的软件包列表后,do_rootfs任务可以启动解包并配置每个软件包及其所需的依赖项到rootfs目录中。rootfs生成使用本地软件包源,我们将在下一节中介绍。

在解压 rootfs 内容后,必须运行所引用软件包的非目标后安装脚本,以避免在第一次启动时执行它们带来的惩罚。

现在,目录已经准备好生成文件系统了。IMAGE_FSTYPES 列出了将要生成的文件系统类型——例如,EXT4UBIFS

do_rootfs 任务完成后,生成的镜像文件会被放置在 build/tmp/deploy/image/<machine>/ 目录下。创建镜像的过程及 IMAGE_FEATURESIMAGE_FSTYPES 的可能值在第十二章《创建自定义层》中有详细描述。

软件包源

如在第五章《掌握 BitBake 工具》中讨论的那样,软件包在生成镜像时起着至关重要的作用,因为 do_rootfs 会使用本地仓库来获取二进制软件包。这个仓库被称为软件包源。

这个仓库不应该仅仅用于镜像或 SDK 构建步骤。将此仓库设置为可远程访问是有多个合理原因的,既可以在我们的开发环境中内部使用,也可以公开访问。以下是其中的一些原因:

  • 你可以在开发阶段轻松测试更新后的应用程序,而无需重新安装整个系统。

  • 你可以使附加软件包更加灵活,以便它们能够安装到运行中的镜像中

  • 你可以在现场更新产品

为了生成一个稳定的软件包源,我们必须确保每次更改软件包时,软件包的修订版本都有一致的递增。手动实现这一点几乎是不可能的,Yocto 项目提供了一个专门的 PR 服务来帮助实现这一目标。

每次 BitBake 检测到任务中的校验和变化时,PR 会在没有人工干预的情况下自动增加。它会以 ${PR}.X 的格式在 PR 中注入一个后缀。例如,如果我们有 PR = "r34",那么在后续的 PR 服务交互后,PR 值将变为 r34.1r34.2r34.3,以此类推。使用 PR 服务对于稳定的软件包源至关重要,因为它要求版本号按线性顺序增加。

提示

即使我们应该使用 PR 服务来确保稳定的软件包版本管理,但在特殊情况下,仍然需要手动设置 PR

默认情况下,PR 服务并未启用或运行。我们可以通过在 BitBake 配置中添加 PRSERV_HOST 变量来启用它进行本地运行——例如,在 build/conf/local.conf 中,配置如下:

图 7.8 – 如何配置 PR 服务以本地运行

图 7.8 – 如何配置 PR 服务以本地运行

当构建发生在单台计算机上时,这种方法是足够的,构建过程会处理软件包源中的每一个软件包。BitBake 在每次构建时启动和停止服务器,并自动增加所需的 PR 值。

对于更复杂的设置,多个计算机共同使用共享包源时,我们必须运行一个单一的 PR 服务,所有与包源相关的构建系统都需使用该服务。在这种情况下,我们需要在服务器上通过 bitbake-prserv 命令启动 PR 服务,如下所示:

图 7.9 – 启动 PR 服务服务器的命令行

图 7.9 – 启动 PR 服务服务器的命令行

除了手动启动服务外,我们还需要更新每个构建系统的 BitBake 配置文件(例如 build/conf/local.conf),该文件通过 PRSERV_HOST 变量连接到服务器,如前所述,以便每个系统指向服务器的 IPport

使用包源

使用包源时,以下两个组件是必需的:

  • 服务器提供对包的访问

  • 客户端访问服务器并下载所需的包

包源提供的包集由我们构建的配方决定。我们可以构建一个或多个配方并提供它们,或者构建一组镜像以生成所需的包。一旦对所提供的包满意,我们必须创建包源提供的包索引。以下命令可执行此操作:

图 7.10 – 创建包索引的命令行

图 7.10 – 创建包索引的命令行

包可以在 build/tmp/deploy 目录中找到。我们必须根据所选的包格式选择相应的子目录。默认情况下,Poky 使用 RPM,因此我们必须提供 build/tmp/deploy/rpm 目录中的内容。

提示

确保在构建所有包后运行 bitbake package-index,否则包索引将不包含这些包。

包索引和包必须通过传输协议(如 HTTP)提供。我们可以使用任何服务器来执行此任务,例如 Apache、Nginx 和 Lighttpd。为了方便在本地开发中通过 HTTP 提供包,可以使用 Python 简单的 HTTP 服务器,如下所示:

图 7.11 – 如何使用 Python 简单 HTTP 服务器提供包源

图 7.11 – 如何使用 Python 简单 HTTP 服务器提供包源

为了向镜像中添加包管理支持,我们需要做一些更改。我们需要在 EXTRA_IMAGE_FEATURES 中添加 package-management,并在 PACKAGE_FEED_URIS 中设置包获取的 URI。例如,我们可以将其添加到 build/conf/local.conf 中:

图 7.12 – 如何配置远程包源

图 7.12 – 如何配置远程包源

我们将在第十二章《创建自定义层》中详细讲解IMAGE_FEATURESEXTRA_IMAGE_FEATURES变量。如果我们想要一个不支持软件包管理的小型镜像,应该从EXTRA_IMAGE_FEATURES中省略package-management

PACKAGE_FEED_URISEXTRA_IMAGE_FEATURES配置确保客户端上的镜像可以访问服务器,并且具备安装、删除和升级软件包所需的工具。在完成这些步骤后,我们可以在目标设备上使用运行时软件包管理。

例如,如果我们为镜像选择 RPM 软件包格式,可以使用以下命令获取仓库信息:

图 7.13 – 获取软件包源仓库的命令行

图 7.13 – 获取软件包源仓库的命令行

使用dnf search <package>dnf install <package>命令可以从仓库中查找并安装软件包。

根据选择的软件包格式,目标设备更新软件包索引、查找和安装软件包的命令是不同的。请参阅下表中针对每种软件包格式提供的命令行:

软件包格式 RPM IPK DEB
更新软件包索引 dnf check-updates opkg update apt-get update
查找软件包 dnf search``<``package> opkg search``<``package> apt-cache search``<``package>
安装软件包 dnf install``<``package> opkg install``<``package> apt-get install``<``package>
系统升级 dnf upgrade opkg upgrade apt-get dist-upgrade

表 7.1 – 软件包管理命令比较

在本地开发阶段使用软件包源非常有利,因为它们使我们能够在已经部署的镜像中安装软件包。

注意

在实际使用中,使用软件包源进行系统升级需要大量的测试工作,以确保系统不会进入损坏状态。验证所有不同的升级场景需要巨大的测试工作量。通常,完整的镜像升级对于生产环境的使用来说更为安全。

软件包源的管理要复杂得多,它涉及到多个方面,如软件包依赖链和不同的升级场景。创建复杂的软件包源外部服务器超出了本书的范围,因此请参阅 Yocto 项目文档以获取更多详细信息。

总结

本章介绍了打包的基本概念,这在 Poky 和 BitBake 中扮演着重要角色(软件包版本控制),并且阐明了在重新构建软件包和软件包源时,如何影响 Poky 的行为。还展示了如何配置镜像,以便使用远程服务器提供的预构建软件包进行更新。

在下一章中,我们将学习关于 BitBake 元数据语法及其操作符的内容,以及如何向变量、变量扩展等追加、前置和移除内容。然后我们将能够更好地理解 Yocto 项目引擎中使用的语言。

第八章:深入 BitBake 元数据

到目前为止,我们已经学会了如何生成镜像和包,并如何使用包源——基本上,掌握了使用 Poky 的基本知识。接下来,我们将学习如何控制 Poky 的行为,以实现我们的目标,并从整个 Yocto 项目中获得最大收益。

本章将帮助我们增强对 BitBake 元数据语法的理解。我们将学习如何使用 BitBake 操作符来修改变量的内容、变量扩展等。这些是我们可以用来制作配方的关键概念,接下来我们将学习如何进行定制。

理解 BitBake 的元数据

BitBake 使用的元数据量非常庞大。因此,为了最大限度地利用 Poky,我们必须掌握它。正如我们在 第四章 中学到的,与 BitBake 工具相遇,元数据覆盖了三个主要领域:

  • .conf 文件):配置文件定义了配置类和配方工作方式的全局内容。

  • .bbclass 文件):类可以继承,以便更容易维护、促进代码重用并避免代码重复。

  • .bb.bbappend 文件):这些配方描述了要执行的任务,并提供必要的信息,以便 BitBake 生成所需的任务链。它们是最常用的元数据,因为它们定义了配方的变量和任务。最常见的配方类型是生成包和镜像。

类和配方使用混合的 Python 和 Shell 脚本代码,由 BitBake 解析,生成大量的任务和本地状态,这些任务和状态在解析后仍需执行。

我们还将学习构建配方所需的操作符和基本概念。

使用元数据

BitBake 元数据使用的语法可能会误导人,有时很难追踪。然而,我们可以通过使用 bitbake 选项(-e--environment)来检查 BitBake 生成的、预处理过的配方数据中每个变量的值,如下所示:

图 8.1 – 如何显示 BitBake 环境

图 8.1 – 如何显示 BitBake 环境

要了解 BitBake 的工作原理,请参考 BitBake 用户手册docs.yoctoproject.org/bitbake/2.0)。接下来的章节将展示配方中常用的大部分语法。

基本变量赋值

如下所示可以完成变量的赋值:

图 8.2 – 变量赋值示例

图 8.2 – 变量赋值示例

在前面的示例中,FOO 变量的值被赋给 bar。变量赋值是 BitBake 元数据语法的核心,因为大多数示例都使用变量。

变量展开

BitBake 支持变量引用。语法与 Shell 脚本非常相似,如下所示:

图 8.3 – 一个示例,展示了变量展开

图 8.3 – 一个示例,展示了变量展开

上述示例导致A包含aValueB包含before-aValue-after。需要记住的是,变量只有在使用时才会展开,如下所示:

图 8.4 – 变量仅在使用时展开

图 8.4 – 变量仅在使用时展开

图 8.4 说明了 BitBake 评估中使用的惰性评估B变量的值是before-${A}-after,直到任务需要该变量的值。A变量在第 3 行被赋值为aNewValue,因此B的值变为before-aNewValue-after

如果变量未赋值,则使用?=进行赋值

当需要仅在变量尚未赋值时才赋值时,可以使用?=运算符。以下代码展示了其用法:

图 8.5 – 一个值的示例

图 8.5 – 一个值的示例

如果对同一变量进行多个?=赋值操作,也会发生相同的行为。第一次使用?=运算符负责赋值该变量。让我们来看以下示例:

图 8.6 – 一个示例,展示了第二次赋值被忽略

图 8.6 – 一个示例,展示了第二次赋值被忽略

A变量在第 1 行已被赋值为value,然后在第 2 行赋值给ignoredAsAlreadyAssigned,该赋值被忽略。

我们需要考虑到=运算符比?=运算符更强,因为它独立于之前的变量状态进行赋值,如下所示:

图 8.7 – 一个示例,展示了运算符比运算符弱

图 8.7 – 一个示例,展示了?=运算符比=运算符弱

因此,A变量被赋值为changeValue

使用??=进行默认值赋值

使用??=运算符旨在为变量提供默认值,它是?=运算符的弱版本。

请查看以下代码:

图 8.8 – 一个示例,展示了如何分配默认值

图 8.8 – 一个示例,展示了如何分配默认值

在第 1 行,A的默认值被赋值为firstValue,然后在第 2 行,A的默认值被更改为secondValue。由于没有对A变量进行其他赋值,最终的值是secondValue

?=是赋值运算符,正如之前所见,它优先于??=运算符,以下例子可以证明:

图 8.9 – 一个示例,展示了运算符比运算符弱

图 8.9 – 一个示例,展示了??=运算符比?=运算符弱

A变量的最终值是thirdValue,因为直到第 3 行才有赋值。

立即变量展开

:= 操作符在需要强制立即扩展变量时使用。它会导致变量的内容立即扩展,而不是等到变量被使用时再扩展,如下所示:

图 8.10 – 立即变量扩展的示例

图 8.10 – 立即变量扩展的示例

B 的值在第 2 行立即赋值,并扩展为 aValue-after。然而,C 的值只有在使用时才会赋值,随后设为 newValue,因为 A 的值已在第 3 行设置。

列表追加和预添加

+= 操作符,称为 列表追加,将新值添加到原值之后,且与原值之间有空格,如下所示:

图 8.11 – 列表追加的示例

图 8.11 – 列表追加的示例

在这个示例中,A 的最终值是 originalValue appendedValue

=+ 操作符,称为 列表预添加,将新值添加到原值之前,且与原值之间有空格,如下所示:

图 8.12 – 列表预添加的示例

图 8.12 – 列表预添加的示例

在这个示例中,A 的最终值是 prependedValue originalValue

字符串追加和预添加

.= 操作符,称为 字符串追加,将一个新值添加到原值之后,中间不留空格,如下所示:

图 8.13 – 字符串追加的示例

图 8.13 – 字符串追加的示例

在这个示例中,A 的最终值是 originalValueAppendedValue

=. 操作符,称为 字符串预添加,将新值添加到原值之前,中间不留空格,如下所示:

图 8.14 – 字符串预添加的示例

图 8.14 – 字符串预添加的示例

在这个示例中,A 的最终值是 prependedValueOriginalValue

:append 和 :prepend 操作符

:append 操作符将新值添加到原值之后,中间不留空格,如下所示:

图 8.15 – 使用 :append 操作符的示例

图 8.15 – 使用 :append 操作符的示例

在这个示例中,A 的最终值是 originalValueAppendedValue

:prepend 操作符将新值添加到原值之前,中间不留空格,如下所示:

图 8.16 – 使用 :prepend 操作符的示例

图 8.16 – 使用 :prepend 操作符的示例

在这个示例中,A 的最终值是 prependedValueOriginalValue

你可能注意到,:append:prepend 操作符与字符串追加(.=)和预添加(=.)操作符相似。但在解析 :append:prepend 操作符与字符串追加和预添加操作符时,存在微妙的差别,如下所示:

图 8.17 – :append 和 .= 操作符之间差异的示例

图 8.17 – :append 和 .= 操作符之间差异的示例

使用 :append 操作符将操作排队等待执行,这将在 第 2 行 分配后发生,结果是 A 变成了 valueAppendedValue。而 .= 操作符是立即执行的,因此 第 4 行 的赋值会替代 第 3 行 中设置的值,导致 B 变为 value

列表项移除

:remove 操作符从原始内容中删除一个列表项。例如,见下文:

图 8.18 – 如何使用 :remove 操作符的示例

图 8.18 – 如何使用 :remove 操作符的示例

在此示例中,A 现在是 value1 value3:remove 操作符将变量值视为由空格分隔的字符串列表,因此该操作符可以从列表中删除一个或多个项目。请注意,每个追加和预加操作都已经完成,才会执行 :remove

条件元数据设置

BitBake 提供了一种非常易于使用的方式,通过名为 overrides 的机制编写条件元数据。

OVERRIDES 变量包含由冒号(:)分隔的值,并从左到右进行评估。每个值都是我们希望具有条件元数据的项目。

让我们考虑下一个示例:

图 8.19 – OVERRIDES 变量的示例

图 8.19 – OVERRIDES 变量的示例

linux 覆盖比 armmymachine 更不具体。以下示例展示了如何使用 OVERRIDES 条件地设置 A 变量:

图 8.20 – 使用 OVERRIDES 条件设置的示例

图 8.20 – 使用 OVERRIDES 条件设置的示例

在此示例中,由于 OVERRIDES 中包含 linuxA 将是 linuxSpecificValue

条件追加

BitBake 还支持基于是否在 OVERRIDES 中,进行变量追加和预加,如下所示:

图 8.21 – 使用 OVERRIDES 条件追加的示例

图 8.21 – 使用 OVERRIDES 条件追加的示例

在上面的示例中,A 被设置为 value armValue

文件包含

BitBake 提供了两种文件包含指令 – includerequire

使用 include 关键字,BitBake 尝试在关键字位置插入文件,因此它是可选的。假设 include 行上指定的路径是相对路径;然后,BitBake 会在 BBPATH 中找到的第一个实例进行定位。相比之下,如果无法找到所需文件,require 关键字会引发 ParseError

提示

Yocto 项目中通常采用的约定是使用 .inc 文件在两个或更多的配方文件之间共享公共代码。

Python 变量扩展

BitBake 使得在变量扩展中使用 Python 代码变得简单,语法如下:

图 8.22 – Python 扩展语法示例

图 8.22 – Python 扩展语法示例

这为用户提供了极大的灵活性。我们可以在以下示例中看到 Python 函数的调用:

图 8.23 – 打印当前日期的 Python 命令示例

图 8.23 – 打印当前日期的 Python 命令示例

这导致 A 变量包含今天的日期。

定义可执行的元数据

元数据配方(.bb)和类文件(.bbclass)可以使用 Shell 脚本代码,如下所示:

图 8.24 – 任务定义示例

图 8.24 – 任务定义示例

任务定义与设置变量相同,不同之处在于此变量恰好是可执行的 Shell 脚本代码。在编写任务代码时,我们不应使用 Bash 或 Zsh 特有的功能,因为任务只能依赖于 POSIX 兼容的功能。如果有疑问,测试代码是否安全的一个好方法是使用 Dash shell 来尝试。

另一种注入代码的方式是使用 Python 代码,如此处所示:

图 8.25 – Python 任务定义示例

图 8.25 – Python 任务定义示例

任务定义类似,但它将任务标记为 Python 任务,这样 BitBake 就知道如何相应地运行它。

在全局命名空间中定义 Python 函数

当我们需要为变量或其他用途生成一个值时,可以通过以下类似的代码在配方(.bb)和类(.bbclass)中快速完成:

图 8.26 – 处理 Python 代码中变量值的代码示例

图 8.26 – 处理 Python 代码中变量值的代码示例

通常,在编写 Python 函数时,我们需要访问 BitBake 数据存储。因此,所有元数据之间的约定是使用一个名为 d 的参数来指向 BitBake 的数据存储。它通常是函数的最后一个参数。

图 8.26 中,我们在第 2 行向数据存储请求 SOMECONDITION 变量的值,并根据该值返回一个结果。

该示例导致 DEPENDS 变量的值包含 dependencyWithCondition

继承系统

inherit 指令指定我们的配方(.bb)提供哪些功能类,提供了类似面向对象编程语言的基本继承机制。例如,我们可以抽象出使用 Autoconf 和 Automake 构建工具所涉及的任务,并将它们放入配方类中以供重用。给定的 .bbclass 文件通过在 BBPATH 中查找 classes/filename.bbclass 来定位。因此,在使用 Autoconf 或 Automake 的配方中,我们可以使用以下内容:

图 8.27 – 继承类的示例

图 8.27 – 如何继承一个类的示例

图 8.27 的第 1 行指示 BitBake 使用 inherit autotools.bbclass,为大多数基于 Autoconf 或 Automake 的项目提供默认任务。

总结

在这一章中,我们详细学习了 BitBake 元数据语法、用于操作变量内容的运算符,以及变量展开,包括一些使用示例。

在下一章中,我们将学习如何使用 Poky 创建外部编译工具,并生成适合目标开发的root文件系统。

第九章:使用 Yocto 项目进行开发

到目前为止,本书中我们使用了 Poky 作为构建工具。换句话说,我们把它当作设计和生成交付产品镜像的工具。

在本章中,我们将学习如何设置目标设备内部使用的开发环境,并了解标准 SDK可扩展 SDK工具,这些工具可以帮助我们在目标设备外开发应用。例如,它们允许我们进行交叉编译应用、配方和镜像。

什么是软件开发工具包?

在嵌入式开发中,工具链通常由跨平台工具组成,这些工具在一种架构上执行,然后生成用于另一种架构的二进制文件——例如,一个在 x86-64 兼容机器上运行并生成 ARM 机器二进制文件的 GCC 工具就是一个交叉编译器。当一个工具和由该工具生成的二进制文件依赖于相同主机上的依赖项时,这通常被称为本地构建。构建和目标架构可能相同,但如果目标二进制文件使用分阶段的根文件系统来查找其依赖项,则这是交叉编译。

软件开发工具包SDK)是一组用于开发和调试应用程序的工具和文件。这些工具包括编译器、链接器、调试器、外部库、头文件和二进制文件,也称为工具链。它可能还包括额外的实用工具和应用程序。我们可以有两种类型的 SDK:

  • 交叉开发 SDK:这些 SDK 旨在用于开发主机,以为目标设备生成二进制文件

  • 本地 SDK:这些工具旨在运行在目标设备上

为设备开发生成本地 SDK

一些嵌入式设备的性能足以作为开发环境使用。然而,构建所需的资源因库或应用的不同而有显著差异,因此使用目标设备作为构建环境可能并不总是可行的。开发镜像需要以下内容:

  • 头文件和库

  • 工具链

以下行将这些属性添加到镜像中:

图 9.1 - 如何配置镜像以包含开发制品

图 9.1 - 如何配置镜像以包含开发制品

前述示例中的IMAGE_FEATURES扩展了镜像的功能,如下所示:

  • dev-pkgs: 为给定镜像中安装的所有包安装开发包(头文件和额外的库链接)

  • tools-sdk: 安装在设备上运行的工具链

IMAGE_FEATURES变量在第十二章中有更详细的描述,创建 自定义层

提示

如果我们只想修改build/conf/local.conf,应该使用的变量是EXTRA_IMAGE_FEATURES

目标设备可以在应用程序开发周期中使用此镜像,并且可以在所有开发人员之间共享此镜像。每个开发人员将拥有一个副本,开发团队将始终使用相同的开发环境。

了解交叉开发 SDK 的类型

Yocto 项目可以生成两种类型的交叉开发 SDK,以满足不同的需求。它们的定义如下:

  • 标准 SDK:提供用于应用程序开发的工件,无论是用于引导加载程序、Linux 内核开发,还是其他用户空间软件

  • sysroot目录,以及在 Yocto 项目控制环境中集成的配方和应用程序

标准 SDK 包括一个工具链和调试应用程序。其目标是允许用户生成用于目标设备的二进制文件。可扩展 SDK 更强大,可以构建镜像和配方。两种 SDK 之间的一个显著区别是可扩展 SDK 中包含devtool

devtool负责提供可扩展 SDK 的附加功能。它是用于使用 BitBake 和recipetool功能的接口。devtoolrecipetool命令也可以在传统的 Yocto 项目环境中使用。

使用标准 SDK

通常,一个 SDK 必须提供一组库和应用程序,这些是针对产品量身定制的镜像中定义的。这些被称为基于镜像的 SDK。例如,我们可以使用以下命令为core-image-full-cmdline生成标准 SDK:

图 9.2 – 如何为 core-image-full-cmdline 生成标准 SDK

图 9.2 – 如何为 core-image-full-cmdline 生成标准 SDK

另一种选择是使用工具链和调试工具创建通用 SDK。这个通用 SDK 称为meta-toolchain,主要用于 Linux 内核和引导加载程序的开发及其调试过程。它可能不足以构建具有复杂依赖关系的应用程序。要创建meta-toolchain,使用以下命令:

图 9.3 – 如何生成通用 SDK

图 9.3 – 如何生成通用 SDK

在这两种情况下,生成的 SDK 自安装文件位于build/tmp/deploy/sdk/。考虑到我们使用了标准 SDK 来构建core-image-full-cmdline,我们可以看到以下生成的文件集:

图 9.4 – 运行 bitbake core-image-full-cmdline -c populate_sdk 后的生成文件

图 9.4 – 运行 bitbake core-image-full-cmdline -c populate_sdk 后的生成文件

创建标准 SDK 后的下一步是安装它,因为标准 SDK 被打包在一个安装脚本中,可以像任何其他脚本一样执行。以下步骤展示了使用标准目标目录安装标准 SDK 的过程:

图 9.5 – 标准 SDK 安装过程

图 9.5 – 标准 SDK 安装过程

上述标准 SDK 演示了如何生成和安装标准 SDK。然而,使用未经针对当前需求定制的标准镜像并不是理想选择。因此,强烈建议创建一个适合我们应用需求的自定义镜像。也建议基于此自定义镜像来构建标准 SDK。

标准 SDK 是根据我们使用 MACHINE 变量设置的机器架构生成的。要使用标准 SDK 构建自定义应用程序,例如 hello-world.c,我们可以使用以下命令,目标架构为 x86-64:

图 9.6 – 使用标准 SDK 构建 C 应用程序的步骤

图 9.6 – 使用标准 SDK 构建 C 应用程序的步骤

另一个常用项目是 Linux 内核。当我们想要构建 Linux 内核源代码时,可以使用以下命令顺序:

图 9.7 – 使用标准 SDK 构建 Linux 内核的步骤

图 9.7 – 使用标准 SDK 构建 Linux 内核的步骤

必须使用 unset LDFLAGS 来避免使用 GCC 进行链接,这是基于 Yocto 项目的标准 SDK 默认的做法。

使用可扩展 SDK

可扩展 SDK 扩展了标准 SDK 的功能。包括的一些主要功能如下:

  • 生成配方

  • 构建配方

  • 构建镜像

  • 在内部工具链中安装软件包

  • 将软件包部署到目标

这些额外功能是由 devtool 工具提供的,devtool 在标准 Yocto 项目环境中也可用。

生成可扩展 SDK,请使用以下命令:

图 9.8 – 生成可扩展 SDK 的命令

图 9.8 – 生成可扩展 SDK 的命令

生成的文件位于 build/tmp/deploy/sdk/ 中。考虑到我们使用可扩展 SDK 进行 core-image-full-cmdline 构建,我们可以看到以下文件集:

图 9.9 – 运行 bitbake core-image-full-cmdline -c populate_sdk_ext 后生成的文件

图 9.9 – 运行 bitbake core-image-full-cmdline -c populate_sdk_ext 后生成的文件

创建可扩展 SDK 后的下一步是安装它。要安装它,我们可以执行生成的脚本。以下命令序列展示了使用标准目标目录安装可扩展 SDK 的过程:

图 9.10 – 可扩展 SDK 安装过程

图 9.10 – 可扩展 SDK 安装过程

上述截图展示了我们如何生成并安装一个可扩展的 SDK。然而,使用一个不符合当前需求的标准镜像并不是理想的做法。因此,强烈建议创建一个适合您应用需求的自定义镜像,并基于此创建可扩展 SDK。但是,我们可以通过使用可扩展 SDK 来构建并安装任何额外的依赖项。

在我们的例子中,我们将可扩展 SDK 安装在/home/user/poky_sdk目录中。安装完成后,下一步是使用提供的脚本导出所需的环境变量,这样就可以使用可扩展 SDK,命令如下:

图 9.11 – 导出环境变量以允许使用可扩展 SDK

图 9.11 – 导出环境变量以允许使用可扩展 SDK

在接下来的部分中,我们将讨论一些使用devtool的案例。所有命令都在终端中执行,并且已导出可扩展 SDK 的环境变量。

可扩展 SDK 是一种不同的方式来交付相同的 Yocto 项目工具和元数据。它将以下内容打包在一起:

  • 用于 Yocto 项目环境执行的一组基本二进制文件

  • 用于开发的标准 SDK

  • 一个共享状态缓存,以减少本地构建

  • Yocto 项目元数据和配置的快照

本质上,可扩展 SDK 是用于创建它的环境的快照。因此,所有devtool命令,包括我们将在接下来的部分中使用的命令,都可以在 Yocto 项目环境中使用。

使用 devtool 构建镜像

让我们从创建镜像开始。可扩展 SDK 能够创建任何支持的镜像。例如,要创建core-image-full-cmdline,我们可以使用以下命令行:

图 9.12 – 使用 devtool 构建 core-image-full-cmdline

图 9.12 – 使用 devtool 构建 core-image-full-cmdline

运行devtool命令后,生成的文件可以在/home/user/poky_sdk/tmp/deploy/images/qemux86-64目录中找到。

在 QEMU 上运行镜像

我们可以使用之前构建的镜像core-image-full-cmdline通过 QEMU 仿真目标硬件,命令如下:

图 9.13 – 使用 devtool 和 QEMU 进行仿真

图 9.13 – 使用 devtool 和 QEMU 进行仿真

它启动 QEMU 执行并生成启动画面,如下图所示:

图 9.14 – QEMU 启动画面

图 9.14 – QEMU 启动画面

从外部 Git 仓库创建配方

devtool还能够从外部 Git 仓库生成配方。在这里,我们将使用github.com/OSSystems/bbexample

图 9.15 – 使用 devtool 创建配方

图 9.15 – 使用 devtool 创建配方

devtool 为给定的仓库创建一个基本的配方文件。它创建了一个包含包源代码和所需元数据的工作区。在运行 devtool add https://github.com/OSSystems/bbexample 命令后,devtool 使用的文件结构如下所示:

图 9.16 – 使用 devtool 创建配方时生成的文件结构

图 9.16 – 使用 devtool 创建配方时生成的文件结构

当前,devtool 根据以下内容生成一个暂定的配方:

  • Autotools(autoconfautomake

  • CMake

  • Scons

  • qmake

  • 一个普通的 Makefile

  • Node.js 模块

  • 使用 setuptoolsdistutils 的 Python 模块

使用 devtool 构建配方

现在,配方已经在工作区目录下创建,我们可以使用以下命令构建它:

图 9.17 – 使用 devtool 构建配方

图 9.17 – 使用 devtool 构建配方

使用 devtool 部署到目标

使用 devtool 构建完包后,我们可以将其部署到目标设备。在我们的示例中,目标设备是运行中的 QEMU。要访问它,请使用默认的 QEMU IP 地址 192.168.7.2,如下所示的命令:

图 9.18 – 使用 devtool 部署到目标

图 9.18 – 使用 devtool 部署到目标

应用程序已安装在目标设备上。我们可以看到 bbexample 在 QEMU 目标上执行,如下图所示:

图 9.19 – 在目标上执行 bbexample

图 9.19 – 在目标上执行 bbexample

扩展 SDK

可扩展 SDK 的目标之一是允许我们在 SDK 环境中安装不同的配方。例如,为了使 libusb1 可用,我们可以运行以下命令:

图 9.20 – 在可扩展 SDK 中安装新配方

图 9.20 – 在可扩展 SDK 中安装新配方

提示

Yocto 项目的可扩展 SDK 允许分布式开发,开发者可以在项目生命周期内更新和扩展现有的 SDK 环境。为了正确使用可扩展 SDK 作为 sstate-cache 镜像和可扩展 SDK 服务器,需要一些基础设施的设置,这涉及复杂的配置,超出了本书的范围。更多细节,请参阅《Yocto 项目应用开发与可扩展软件开发工具包 (eSDK)》中的 安装后向可扩展 SDK 提供更新 部分 (docs.yoctoproject.org/4.0.4/sdk-manual/appendix-customizing.html#providing-updates-to-the-extensible-sdk-after-installation)。

总结

在本章中,我们学习了 Yocto 项目如何用于开发和镜像创建。我们学习了如何创建不同类型的工具链以及如何使用它们。

在下一章中,我们将探讨如何配置 Poky 来帮助我们进行调试过程,如何配置系统以便使用 GDB 提供远程调试所需的工具,以及如何通过buildhistory来跟踪我们的更改。

第十章:使用 Yocto 项目进行调试

调试过程是每个开发周期中的一个关键步骤。在本章中,我们将学习如何配置 Poky 来帮助我们进行调试过程;例如,如何配置我们的系统以提供进行远程调试所需的工具,如何使用便利的调试工具,如 oe-pkgdata-utilbitbake-getvardevshell

区分元数据调试和应用程序调试

在深入调试细节之前,我们需要认识到调试有不同类型,如元数据调试和运行时代码调试。

元数据调试是必需的,以确保 BitBake 任务的行为符合我们的目标,并在不符合时找出罪魁祸首。例如,某个配方可能需要修复以启用某个功能。在这种情况下,我们可以使用 BitBake 在主机上生成的多个日志文件来帮助追踪相关任务的执行路径。

另一方面,调试运行时代码更为自然,因为它本质上与应用程序、库或内核的典型开发周期相同。根据我们希望解决的问题,所需的工具可能从调试器到代码插装(例如,添加调试打印)不等。

跟踪镜像、包和 SDK 内容

确保我们拥有镜像、包和 buildhistory 机制的最简单方法。

当某个配方被更新为新版本或其代码发生变化时,可能会影响生成包的内容,从而影响镜像或 SDK。

Poky 涉及许多配方和镜像或 SDK 通常包含数十或数百个包。因此,跟踪包的内容可能会非常具有挑战性。Poky 提供的工具帮助完成这一任务的是 buildhistory

buildhistory,顾名思义,保存了在使用 Poky 过程中构建的多个工件的历史记录。它跟踪包、镜像和 SDK 的构建及其内容。

要在系统中启用 buildhistory,我们需要在 build/conf/local.conf 文件中添加以下几行代码:

图 10.1 – 如何启用 buildhistory 支持

图 10.1 – 如何启用 buildhistory 支持

INHERIT 方法包括在构建过程中调用 buildhistory 类的钩子。同时,BUILDHISTORY_COMMIT 行使 BitBake 为每个新包、镜像或 SDK 构建创建 buildhistory 仓库中的新 Git 提交。Git 提交使得使用 git diff 比较两个提交之间的差异变得非常简单。数据存储在 build/buildhistory 目录下,以文本文件的形式方便使用。

Poky 提供了一种工具,称为 buildhistory-diff,以更简洁的方式输出两个 buildhistory 状态之间的差异,这在检查变化时非常有用。buildhistory-diff 工具以更有意义的方式输出任何两个 Git 版本之间的差异。

例如,假设我们在 core-image-minimal 镜像中添加了 strace 软件包并进行了构建。在这种情况下,可以使用 buildhistory-diff 命令检查生成的更改,如下图所示:

图 10.2 – buildhistory-diff 的结果

图 10.2 – buildhistory-diff 的结果

对于每个软件包构建,buildhistory 会生成一个子软件包清单、安装脚本、文件所有权和大小清单、依赖关系等。此外,还会为镜像和 SDK 创建软件包、文件系统文件和依赖图之间的依赖关系。

为了更好地了解 buildhistory 提供的功能和特性,请参考 Yocto 项目开发任务中的构建输出质量维护 手册docs.yoctoproject.org/4.0.4/dev-manual/common-tasks.html#maintaining-build-output-quality)。

调试软件包

在更复杂的配方中,我们将已安装的内容拆分成几个子软件包。子软件包可以是可选功能、模块或任何其他可选安装的文件集。

要检查配方内容如何被拆分,我们可以使用 build/tmp/work/<arch>/<recipe name>/<software version>/packages-split 目录。它包含每个子软件包的子目录,并且在子树中有其内容。

在错误的内容拆分中,可能的原因我们定义如下:

  • 内容未被安装(例如,安装脚本中的错误)

  • 应用程序或库配置错误(例如,禁用的功能)

  • 元数据错误(例如,错误的软件包顺序)

构建失败的另一个常见问题是 sysroot 目录中缺少必需的工件(例如,头文件或动态库)。sysroot 生成的对照可以在 build/tmp/work/<arch>/<recipe_name>/<software_version>/sysroot-destdir 中查看。

如果这还不够,我们可以通过这些日志记录功能来修改任务代码,以确定导致意外结果的逻辑错误或 bug。

检查软件包

Yocto 项目的核心之一是处理软件包。因此,该项目设计了 oe-pkgdata-util 来帮助我们检查已构建的软件包和相关数据。例如,在运行 bitbake bluez5 后,我们可以使用以下命令查找所有与 bluez 相关的软件包:

图 10.3 – 列出所有可用的软件包并过滤出与 bluez 相关的

图 10.3 – 列出所有可用的软件包并过滤出与 bluez 相关的

有时,我们需要查找包含此特定文件的软件包。我们可以使用以下命令查询软件包数据库:

图 10.4 – 查找哪个软件包提供 /usr/bin/rfcomm

图 10.4 – 查找哪个软件包提供了 /usr/bin/rfcomm

另一个使用场景是当我们需要查找软件包的当前版本时,可以使用以下命令:

图 10.5 – 列出 bluez5 软件包的信息

图 10.5 – 列出 bluez5 软件包的信息

我们还可以使用以下命令列出给定软件包的所有文件:

图 10.6 – 列出 bluez5 软件包中的文件

图 10.6 – 列出 bluez5 软件包中的文件

oe-pkgdata-util 脚本是一个有用的工具,帮助我们调试打包过程。

任务执行期间的日志信息

BitBake 提供的日志工具对于跟踪代码执行路径非常有用。BitBake 提供了用于 Python 和 Shell 脚本代码的日志功能,具体如下:

  • bb.fatalbb.errorbb.warnbb.notebb.plainbb.debug

  • bbfatalbberrorbbwarnbbnotebbplainbbdebug

这些日志功能非常相似,但有一些小的区别,具体如下:

  • bb.fatalbbfatal:这些日志消息具有最高优先级,因为它们打印消息并终止处理,导致构建中断。

  • bb.errorbberror:这些显示错误信息,但不会强制停止构建。

  • bb.warnbbwarn:这些用于向用户发出警告。

  • bb.notebbnote:这些用于向用户添加备注,仅供参考。

  • bb.plainbbplain:这些用于输出信息。

  • bb.debugbbdebug:这些用于添加调试信息,显示内容取决于使用的调试级别。

在 Python 和 Shell 脚本中使用日志功能有一个微妙的区别。Python 中的日志功能直接由 BitBake 处理,显示在控制台,并存储在 build/tmp/log/cooker/<machine> 中的执行日志中。而在 Shell 脚本中使用日志功能时,信息会输出到单独的任务日志文件,这些文件可以在 build/tmp/work/<arch>/<recipe name>/<software version>/temp 中找到。

在 temp 目录中,我们可以检查每个任务的脚本,使用 run.<task>.<pid> 模式,并使用 log.<task>.<pid> 模式查看输出。符号链接指向最后的日志文件,使用 log.<task> 模式。例如,我们可以检查 log.do_compile,以验证在构建过程中是否使用了正确的文件。

build/tmp/work 目录在 第六章 中有详细介绍,详细说明临时构建目录

调试元数据变量

要调试元数据变量,我们可以使用 bitbake-getvar 脚本。它使用 BitBake 内部数据获取特定变量的值及其归属历史。

例如,要检查 procps 配方的 PACKAGECONFIG 变量,我们可以使用以下命令:

图 10.7 – bitbake-getvar -r procps PACKAGECONFIG 的结果

图 10.7 – bitbake-getvar -r procps PACKAGECONFIG 的结果

图 10.7 中,我们可以看到 PACKAGECONFIG 在末尾是空的。我们还可以看到 defaultvalmeta/recipes-extended/procps/procps_3.3.17.bb 文件的第 33 行被设置为 "${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)}"

我们可以在以下截图中看到 procps 食谱的第 33 行和第 34 行:

图 10.8 - procps 食谱的第 33 行和第 34 行

图 10.8 - procps 食谱的第 33 行和第 34 行

bitbake-getvar 脚本可以用来检查某个特性是否启用,或者确保某个变量已按预期展开。

使用开发 shell

开发 shell 在编辑软件包或调试构建失败时是一个有用的工具。使用 devshell 时会执行以下步骤:

  1. 源文件被提取到工作目录中。

  2. 补丁已应用。

  3. 在工作目录中打开了一个新的终端。

所有构建所需的环境变量都可以在新终端中使用,因此我们可以使用 configuremake 等命令。这些命令的执行方式就像构建系统正在运行它们一样。

以下命令是一个示例,使用 devshell 在名为 linux-yocto 的目标上:

图 10.9 – 运行 devshell 为 linux-yocto 食谱

图 10.9 – 运行 linux-yocto 食谱的 devshell

来自 图 10.9 的命令使我们能够重新处理 Linux 内核源代码、构建它,并根据需要修改其代码。在 图 10.10 中,您可以看到执行 bitbake linux-yocto -c devshell 命令后的日志:

图 10.10 – bitbake linux-yocto -c devshell 的日志

图 10.10 – bitbake linux-yocto -c devshell 的日志

注意

需要特别注意的是,devshell 内部所做的更改在不同的构建之间不会持久化;因此,我们必须在离开时记录任何重要的更改。

由于我们有源代码可用,可以使用它生成额外的补丁。一个方便的方法是使用 Git 和 git format-patch 创建补丁,然后将其包含到食谱中。

以下截图显示了在调用 devshell 任务后打开的 devshell 窗口:

图 10.11 – WORKDIR 目录中的文件列表

图 10.11 – WORKDIR 目录中的文件列表

devshell 命令适合处理小任务。但当需要进行更复杂的修改时,使用外部工具链或 devtool 可能是更好的选择。

要将生成的补丁包含到食谱中并使其持久化,请参见 第十三章自定义 现有食谱

使用 GNU 调试器进行调试

在开发任何项目时,我们时常会遇到难以理解的微妙错误。GDB 作为一个包包含在 Poky 中。它默认安装在 SDK 镜像中,正如在第九章中详细介绍的那样,使用 Yocto 项目进行开发

注意

要在镜像中安装包含调试符号和工具的调试包,请在build/conf/local.conf中添加IMAGE_FEATURES += "dbg-pkgs tools-debug"

使用 SDK 或包含调试包和工具的镜像,可以直接在目标设备上调试应用程序,复制我们通常在机器上进行的相同开发工作流程。

由于内存或磁盘空间限制,GDB 可能无法在某些目标设备上使用。此限制的主要原因是 GDB 需要在开始调试之前加载调试信息和调试过程的二进制文件。

为了克服这些限制,我们可以使用gdbserver,它在使用tools-debug时默认包含在IMAGE_FEATURES中。它在目标设备上运行,不会从被调试进程加载任何调试信息。相反,GDB 实例会在构建主机上处理调试信息。主机 GDB 向gdbserver发送控制命令,以控制被调试的应用程序,因此目标设备不需要安装调试符号。

然而,我们必须确保主机能够访问包含调试信息的二进制文件。因此,建议目标设备的二进制文件在没有优化的情况下编译,以便于调试过程。

使用gdbserver并正确配置主机和目标的过程详细介绍了在 Yocto 项目开发任务手册中的远程调试 GNU 项目调试器(GDB)部分docs.yoctoproject.org/4.0.4/dev-manual/common-tasks.html#debugging-with-the-gnu-project-debugger-gdb-remotely)。

总结

本章中,我们学习了如何配置 Poky 来帮助我们进行调试过程。我们了解了可用于调试的部署目录内容,并且学习了如何使用buildhistory跟踪我们的更改。我们还介绍了如何使用oe-pkgdata-util查看包信息,使用bitbake-getvar调试变量展开,如何使用devshell模拟 BitBake 找到的相同构建环境,以及如何配置系统以提供进行 GDB 调试所需的工具。

在下一章中,我们将学习如何使用外部层扩展 Poky 源代码。首先,我们将介绍层次结构的概念。接下来,我们将详细学习每种层类型的目录结构和内容。

第十一章:探索外部层

Poky 最具魅力的特点之一是使用外部层的灵活性。在本章中,我们将探讨为什么这是一个至关重要的功能,以及如何利用它。我们还将了解不同类型的层及其目录树布局。最后,在本章结束时,我们将学习如何将新层包含到我们的项目中。

通过层提供灵活性

Poky 包含散布在配置定义文件(如机器和发行版文件)、类和食谱中的元数据,涵盖了从简单应用程序到完整图形堆栈和框架的所有内容。BitBake 可以从多个地方加载元数据集合,这些地方被称为元数据层。

使用层的最大优势是能够将元数据分割成逻辑单元,这使得用户只需选择项目所需的元数据集合。

使用元数据层使我们能够执行以下操作:

  • 改善代码复用

  • 在不同团队、社区和供应商之间共享并扩展工作

  • 提高 Yocto 项目社区的代码质量,因为多个开发者和用户会共同关注他们感兴趣的特定元数据层。

我们可以出于不同的原因配置系统,例如需要启用/禁用某个功能或更改构建标志以启用特定架构优化。这些都是使用层进行定制的示例。

此外,我们应该将元数据组织在不同的层中,而不是创建自定义项目环境、更改食谱或修改 Poky 层中的文件。组织越分离,未来项目中重用层就越容易,因为 Poky 源代码也分为不同的层。默认情况下,它包含三个层,正如我们从以下命令行的输出中看到的那样:

图 11.1 – bitbake-layers show-layers 的结果

图 11.1 – bitbake-layers show-layers 的结果

命令行输出显示任何层的以下三个基本属性:

  • meta 字符串。

  • BBPATH 变量。

  • .bbappend文件应该是连接在一起的。这意味着,如果两个层包含相同的食谱文件(.bb),则优先级最高的那个文件会被使用。对于.bbappend,每个.bbappend文件都会包含在原始食谱中。层的优先级决定了包含的顺序,因此优先级最高层中的.bbappend文件会最先附加,其他层的文件则依次附加。

以 Poky 为例,它有三个主要的单独层。meta-yocto-bsp层是 Poky 的参考板级支持包BSP)层。它包含机器配置文件和食谱,用于为机器配置软件包。作为参考 BSP 层,它可以作为示例使用。

meta-poky 层是 Poky 参考分发层。它包含 Yocto 项目默认使用的分发配置。此默认分发在 poky.conf 文件中进行了描述,广泛用于测试产品。它可以作为设计自定义分发时的起点。

另一种层是软件层,它仅包含应用程序或应用程序的配置文件,可以用于任何架构。软件层的种类繁多。举几个例子,meta-javameta-qt5meta-browsermeta-java 层提供 Java 运行时和 SDK 支持,meta-qt5 层包括 Qt5 支持,meta-browser 支持 Firefox 和 Chrome 等网页浏览器。

meta 层是 OpenEmbedded 核心元数据,包含食谱、类和 QEMU 机器配置文件。它可以视为一种混合层类型,因为它具有软件集合、BSP 定义和 Yocto 项目用作基础的分发。

有时,您的产品可能有特殊需求,需要根据要求修改 build/conf/local.conf 文件。最合适且可维护的解决方案是创建一个分发层,将分发定义文件放在其中。

提示

build/conf/local.conf 文件是一个易变的文件,不应由 Git 跟踪。

我们不应依赖它来设置包版本、提供者和产品的系统功能,而应仅将其作为开发过程中测试的快捷方式。

避免在 build/conf/local.conf 中添加自定义设置,有助于使我们的构建过程在之后可复现。

详细描述一个层的源代码

通常,一个层会有一个目录树,如下图所示:

图 11.2 – 标准层布局

图 11.2 – 标准层布局

在此目录中有两个文件,<layer>/COPYING<layer>/README,分别是许可证和给用户的消息。在 <layer>/README 中,我们必须指定层的用户需要知道的任何其他依赖关系和信息。meta- 前缀对于层来说不是必需的,但它是常用的命名约定。

classes 文件夹应包含该层特定的类(.bbclass 文件)。这是一个可选的目录。

<layer>/conf 文件夹是必需的,应提供配置文件(.conf 文件)。层配置文件 <layer>/conf/layer.conf 将在下一章详细介绍,它是包含层定义的文件。

图 11.2所示,<layer>/conf 文件夹的目录布局示例,其中 (a) 展示了 BSP 层的结构,(b) 展示了分发层的结构:

图 11.3 – BSP 和分发层的 /conf 布局

图 11.3 – BSP 和分发层的 /conf 布局

recipe-* 文件夹是按类别分隔的配方集群——例如,recipes-corerecipes-bsprecipes-graphicrecipes-multimediarecipes-kernel。在每个以 recipes- 为前缀的文件夹内,有一个目录,存放着配方名称或一组配方。里面放置的是以 .bb.bbappend 结尾的配方文件。例如,我们可以从 meta 层中找到以下截图:

图 11.4 – 配方-*布局示例

图 11.4 – 配方-*布局示例

添加元层

我们可以在 layers.openembedded.org 找到大多数可用的元层。这里有来自 Yocto 项目、OpenEmbedded、各社区和公司的数百个元层,可以手动克隆到项目的源代码目录中。

例如,要在我们的项目中包含 meta-oemeta-openembedded 仓库中的多个元层之一),我们可以修改配置文件的内容,或使用 BitBake 命令行。然而,我们首先需要获取该层的源代码。请在您的 Poky 源代码目录中运行以下命令:

图 11.5 – 克隆 meta-openembedded 层

图 11.5 – 克隆 meta-openembedded 层

我们需要修改 build/conf/bblayer.conf 文件,添加层的位置,使用其绝对路径。请参见第 12 行,如 图 11.6 所示:

图 11.6 – 在包含 meta-openembedded 层后, 的内容

图 11.6 – 在包含 meta-openembedded 层后,build/conf/bblayers.conf 的内容

另外,我们可以使用bitbake-layers工具来为我们执行包含操作。这可以通过在build目录下运行以下命令来完成:

图 11.7 – 添加层位置的命令行

图 11.7 – 添加层位置的命令行

Yocto 项目层生态系统

创建一个层是非常方便的。为了让所有可用的层更容易访问,OpenEmbedded 社区开发了一个索引,地址为 layers.openembedded.org,大部分层都可以在这里找到。其Layers标签页的示例如下所示:

图 11.8 – OpenEmbedded 层索引(Kirkstone)

图 11.8 – OpenEmbedded 层索引(Kirkstone)

OpenEmbedded 层索引网站的另一个方便的用例是搜索特定的软件类型或配方。OpenEmbedded 层索引可以通过允许我们搜索以下内容来帮助我们:

  • 机器

  • 发行版

  • 配方

  • 类别

bitbake-layers 工具还支持使用 OpenEmbedded 层索引。例如,要添加 meta-oe 层,我们可以使用以下命令:

图 11.9 – 从 OpenEmbedded 层索引获取一个层

图 11.9 – 从 OpenEmbedded 层索引中获取一个层

小结

在本章中,我们介绍了层的概念。我们详细了解了目录结构以及每种层类型中的内容。此外,我们还学习了如何手动或使用 BitBake 命令行将外部层添加到我们的项目中,以及如何使用 OpenEmbedded 层索引轻松找到我们需要的可用层。

在下一章,我们将进一步了解为什么需要创建新层,以及它们中包含的常见元数据是什么(如机器定义文件、食谱和镜像)。最后,我们将通过一个分发定制的例子来总结。

第十二章:创建自定义层

除了使用来自社区或供应商的现有层之外,本章将教我们如何为我们的产品创建层。此外,我们还将学习如何创建机器定义和分发,并从中获益,以便更好地组织我们的源代码。

创建新层

在创建我们的层之前,最好先检查一下以下网站是否已有类似的层:layers.openembedded.org

如果我们仍在寻找适合我们需求的层,下一步是创建目录。通常,层的名称以meta-开头,但这不是技术限制。

<layer>/conf/layer.conf文件是每个层所需的层配置文件。新层可以通过一个名为bitbake-layers的工具创建,该工具来自 Poky 的 BitBake,如下命令所示:

图 12.1 – 使用 bitbake-layers 创建新层

图 12.1 – 使用 bitbake-layers 创建新层

创建层之后,我们需要使用以下命令将其包含在build/conf/bblayers.conf文件中:

图 12.2 – 将 meta-newlayer 添加到 build/conf/bblayers.conf

图 12.2 – 将 meta-newlayer 添加到 build/conf/bblayers.conf

提示

bitbake-layers工具默认生成的层优先级为 6。我们仍然可以通过参数自定义优先级。

最后一条命令会生成层,如下图所示:

图 12.3 – 创建时的 meta-newlayer 布局

图 12.3 – 创建时的 meta-newlayer 布局

meta-newlayer的默认层配置文件是让层正常工作的最小配置。然而,它可以定制以包含未来所需的配置。

以下图示展示了我们刚创建的meta-newlayer层的默认conf/layer.conf内容:

图 12.4 – 最小配置

图 12.4 – meta-newlayer/conf/layer.conf最小配置

一些常用的变量可能需要添加或更改,它们是LAYERVERSIONLAYERDEPENDS。这些变量在我们的层需要其他层才能工作时很有用。两个变量的名称必须以层的名称作为后缀,如下所示:

  • LAYERVERSION:这是一个可选变量,用于指定层的版本号。该变量在LAYERDEPENDS变量中使用,用于依赖特定版本的层—例如,LAYERVERSION_meta-newlayer = "``1"

  • LAYERDEPENDS:列出了配方所依赖的层,层之间用空格隔开—例如,我们通过LAYERDEPENDS_meta-newlayer += "``meta-otherlayer:2"添加对版本2meta-otherlayer的依赖。

如果无法满足依赖关系或版本号不匹配,则会发生错误。层结构的基础已创建。在接下来的章节中,我们将学习如何扩展它。

向层添加元数据

层元数据可以实现两个目标——添加新软件或特性,以及修改现有元数据。

我们可以在新层中包含多个元数据文件,如配方、图像和 bbappend 文件。meta-yocto-bspmeta-yocto 中有几个 bbappend 文件的示例。我们将在 第十三章 中探讨它们的一些常见用途,自定义 现有配方

在接下来的章节中,我们将介绍一些常见的层元数据修改。

创建图像

图像文件本质上是一组为某个目的而分组的包,并以受控方式进行配置。我们可以从头开始创建图像,也可以通过重用现有图像并添加额外的必要包来创建图像。

我们应尽可能重用现有图像,这样可以使代码维护更加易于管理,并突出功能差异。例如,我们可能希望包含一个应用程序并从 core-image-full-cmdline 图像文件中移除一个图像功能。在这种情况下,我们可以在 recipes-mine/images/my-image-full-cmdline.bb 文件中创建一个图像,包含以下代码行:

图 12.5 – my-image-full-cmdline.bb 的内容

图 12.5 – my-image-full-cmdline.bb 的内容

core-image 类提供了图像功能,提供了常用功能的有用构建块,创建图像时应该使用它。例如,我们可以在 recipes-mine/images/my-image-strace.bb 文件中创建一个包含以下代码行的图像:

图 12.6 – my-image-strace.bb 的内容

图 12.6 – my-image-strace.bb 的内容

提示

列表追加操作符(+=)确保可以通过 build/conf/local.conf 添加新的 EXTRA_IMAGE_FEATURES 变量。

CORE_IMAGE_EXTRA_INSTALL 是我们在继承 core-image 类时应使用的变量,它有助于图像的创建。该类为 IMAGE_FEATURES 变量添加了支持,避免了代码重复。

目前,支持以下图像功能,详细信息请见 Yocto 项目参考手册 中的 图像功能 部分 (docs.yoctoproject.org/4.0.4/ref-manual/features.html#image-features):

  • allow-empty-password:允许 Dropbear 和 OpenSSH 接受来自密码为空的账户的登录。

  • allow-root-login:允许 Dropbear 和 OpenSSH 接受 root 登录。

  • dbg-pkgs:为给定图像中安装的所有包安装调试符号包。

  • debug-tweaks: 使镜像适合开发(例如,允许 root 登录、无密码登录——包括 root 登录,并启用后安装日志记录)。

  • dev-pkgs: 为给定镜像中所有已安装的包安装开发包(头文件和额外的库链接)。

  • doc-pkgs: 安装所有已安装包的文档包。

  • empty-root-password: 如果希望允许以空密码进行 root 登录,则需要此功能或 debug-tweaks

  • hwcodecs: 安装硬件加速编解码器。

  • lic-pkgs: 安装所有已安装包的许可证包。

  • nfs-server: 安装 NFS 服务器。

  • overlayfs-etc: 配置 /etc 目录使用 overlayfs。这使你可以将设备特定的信息存储在其他位置,特别是当根文件系统配置为只读时。

  • package-management: 安装包管理工具并保留包管理数据库。

  • perf: 安装性能分析工具,如 perfsystemtapLTTng

  • post-install-logging: 使你能够在目标系统的首次启动时,将后安装脚本的运行记录到 /var/log/postinstall.log 文件中。

  • ptest-pkgs: 安装为所有启用 ptest 的配方提供的 ptest 包。

  • read-only-rootfs: 创建一个根文件系统为只读的镜像。

  • read-only-rootfs-delayed-postinsts: 与 read-only-rootfs 配合使用时,指定仍然允许后安装脚本的执行。

  • serial-autologin-root: 与 empty-root-password 配合使用时,将自动在串行控制台上以 root 用户登录。

  • splash: 使你能够在启动过程中显示启动画面。默认情况下,该画面由 psplash 提供,允许进行自定义。

  • ssh-server-dropbear: 安装 Dropbear 最小化 SSH 服务器。

  • ssh-server-openssh: 安装 OpenSSH SSH 服务器,其功能比 Dropbear 更全面。请注意,如果 IMAGE_FEATURES 中同时存在 OpenSSH SSH 服务器和 Dropbear 最小化 SSH 服务器,则 OpenSSH 会优先安装,Dropbear 将不会被安装。

  • stateless-rootfs: 指定创建一个无状态的镜像——在使用 systemd 时,systemctl-native 将不会在镜像上运行,镜像将在运行时由 systemd 填充。

  • staticdev-pkgs: 安装静态开发包,这是为给定镜像中所有已安装包提供的静态库(例如,*.a 文件)。

  • tools-debug: 安装调试工具,如 stracegdb

  • tools-sdk: 安装一个完整的 SDK,运行在设备上。

  • tools-testapps: 安装设备测试工具(例如,触摸屏调试)。

  • weston: 安装 Weston(参考 Wayland 环境)。

  • x11-base: 安装具有最小环境的 X 服务器。

  • x11: 安装 X 服务器。

  • x11-sato: 安装 OpenedHand Sato 环境。

添加包配方

Poky 包含多个类,这些类将基于 Autotools、CMake 和 Meson 的常见开发工具的过程抽象为项目。一个软件包食谱是我们如何指示 BitBake 执行 fetchunpackpatchconfigurecompileinstall 等任务的方式,适用于我们的应用程序、内核模块或项目提供的任何软件。此外,可以在Yocto 项目参考手册部分查看 Poky 中包含的类的列表(docs.yoctoproject.org/4.0.4/ref-manual/classes.html)。

提供了一个简单的食谱,显式地执行 compileinstall 任务,如下所示:

图 12.7 – 手动创建的 helloworld 食谱

图 12.7 – 手动创建的 helloworld 食谱

do_compiledo_install 代码块为我们提供了 Shell 脚本命令,用于构建并将生成的二进制文件安装到目标目录中,该目录被引用为 ${D},其目的是将安装目录重新定位到 build/tmp/work/ 目录中的一个路径。假设我们正在处理一个基于 Autotools 的项目。如果是这样,我们可以通过使用 autotools 类来避免大量的代码重复,示例如下,从 poky/meta/recipes-core/dbus-wait/dbus-wait_git.bb 文件中的食谱中提取:

图 12.8 – poky/meta/recipes-core/dbus-wait/dbus-wait_git.bb 的内容

图 12.8 – poky/meta/recipes-core/dbus-wait/dbus-wait_git.bb 的内容

第 19 行继承 autotools 类的简单操作就是提供执行以下任务所需的所有代码:

  • 更新 configure 脚本代码和工件

  • 更新 libtool 脚本

  • 运行 configure 脚本

  • 运行 make

  • 运行 make install

相同的概念适用于其他构建工具,正如CMakeMeson的情况。此外,支持的类数量在每个版本中不断增长,以支持新的构建系统并避免代码重复。

使用 devtool 自动创建基本包食谱

正如我们在第九章从外部 Git 仓库创建食谱一节中所学到的,使用 Yocto 项目开发devtool 可以通过以下命令自动化基于现有项目创建食谱的过程:

图 12.9 – 用于生成 bbexample 食谱的命令行

图 12.9 – 用于生成 bbexample 食谱的命令行

在幕后,devtool 运行了 recipetool 来生成食谱,并将所有预构建的信息自动配置到新的食谱文件中。最终结果存储在 workspace 目录中,这是 devtool 维护的一个层。要将食谱文件复制到目标层,我们可以使用 devtool 命令,如下所示:

图 12.10 – 将 bbexample 配方部署到 meta-newlayer 的命令行

图 12.10 – 将 bbexample 配方部署到 meta-newlayer 的命令行

创建的meta-newlayer/recipes-bbexample/bbexample/bbexample_git.bb文件如下所示:

图 12.11 – bbexamle_git.bb 的内容

图 12.11 – bbexamle_git.bb 的内容

devtool已创建一个基础配方,这不应被视为最终配方。你应该检查编译选项、额外的元数据等。

为新机器定义添加支持

尽管为 Poky 创建新机器定义是一个简单的任务,但不应低估其复杂性。根据我们在 BSP 层需要支持的功能集,可能需要检查引导加载程序、内核和硬件支持驱动程序。

Yocto 项目支持 ARM、ARM64、x86、x86-64、PowerPC、PowerPC 64、MIPS、MIPS64、RISC-V 32 和 RISC-V 64,代表目前最常用的嵌入式架构。

机器定义中使用的主要变量集如下:

  • TARGET_ARCH: 设置机器架构——例如,ARM 和 x86-64

  • PREFERRED_PROVIDER_virtual/kernel: 如果需要使用特定内核,这会覆盖默认的内核(linux-yocto

  • SERIAL_CONSOLES: 定义串行控制台及其速度

  • MACHINE_FEATURES: 描述硬件特性,以便所需的软件栈默认包含在映像中

  • KERNEL_IMAGETYPE: 用于选择内核镜像类型——例如,bzImageImage

  • IMAGE_FSTYPES: 设置生成的文件系统映像类型——例如,tar.gzext4ubifs

你可以在 Poky 源代码中的meta-yocto-bsp/conf/machine/目录中查看机器定义文件的示例。描述新机器时,我们应特别注意在MACHINE_FEATURES中支持的特定功能。这样,所需的软件就会安装到映像中。当前MACHINE_FEATURES的可用值如下:

  • acpi: 硬件具有 ACPI(仅限 x86/x86-64)

  • alsa: 硬件具有 ALSA 音频驱动

  • apm: 硬件使用 APM(或 APM 仿真)

  • bluetooth: 硬件集成了蓝牙

  • efi: 支持通过 EFI 引导

  • ext2: 硬件硬盘或微型硬盘

  • keyboard: 硬件具有键盘

  • numa: 硬件具有非统一内存访问

  • pcbios: 支持通过 BIOS 引导

  • pci: 硬件具有 PCI 总线

  • pcmcia: 硬件具有 PCMCIA 或 CompactFlash 插槽

  • phone: 移动电话(语音)支持

  • qemu-usermode: QEMU 可以为此机器提供用户模式仿真

  • qvga: 机器具有 QVGA(320x240)显示屏

  • rtc: 机器具有实时时钟

  • screen: 硬件具有显示屏

  • serial: 硬件具有串行支持(通常是 RS232)

  • touchscreen: 硬件具有触摸屏

  • usbgadget:硬件支持 USB 小工具设备

  • usbhost:硬件支持 USB 主机功能

  • vfat:支持 FAT 文件系统

  • wifi:硬件集成了 Wi-Fi

为你的机器打包一个镜像

创建一个可供机器使用的镜像应该在任何 BSP 支持层开发的最后进行。镜像的类型取决于处理器、板上包含的外设和项目的限制。

wic提供了一种灵活的方式来生成此镜像。它允许基于模板文件(.wks)创建分区镜像,模板文件是用描述目标镜像布局的通用语言编写的。语言定义可以在OpenEmbedded Kickstart (.wks)参考部分中找到,该部分来自Yocto 项目参考手册(docs.yoctoproject.org/4.0.4/ref-manual/kickstart.html#openembedded-kickstart-wks-reference)。

.wks文件被放置在我们层中的wic目录下。这个目录通常会有多个文件,用于指定不同的镜像布局。然而,必须记住所选择的结构必须与机器匹配——例如,当考虑使用一个基于 i.MX 的机器,该机器通过 SD 卡上的 U-Boot 启动,SD 卡有两个分区,一个用于启动文件,另一个用于rootfs。相应的.wks文件如下所示:

图 12.12 – 使用 SPL 的 i.MX 设备的.wks 文件示例

图 12.12 – 使用 SPL 的 i.MX 设备的.wks 文件示例

要启用基于wic的镜像生成,只需将wic添加到IMAGE_FSTYPES中即可。我们还可以通过设置WKS_FILE变量来定义要使用的.wks文件。

使用自定义发行版

创建一个发行版是简单与复杂的结合。创建发行版文件是直接的,但对 Poky 的行为有重大影响。根据我们的选项,它可能会导致与以前构建的二进制文件的不兼容。

发行版是我们定义全局选项的地方,如工具链版本、图形后端和对OpenGL的支持。只有当 Poky 提供的默认设置无法满足我们的要求时,我们才应该创建一个发行版。

通常,我们打算改变 Poky 中的少量选项。例如,我们删除了<layer>/conf/distro/my-distro.conf文件,其内容如下:

图 12.13 – 自定义发行版文件的示例

图 12.13 – 自定义发行版文件的示例

要使用刚刚创建的发行版,我们需要将以下代码添加到build/conf/local.conf文件中:

图 12.14 – 设置 DISTRO 的行,在中

图 12.14 – 设置 DISTRO 的行,在build/conf/local.conf

DISTRO_FEATURES 变量可能会影响配方的配置方式以及包在镜像中的安装方式——例如,如果我们想在任何机器和镜像中使用声音,必须具备 alsa 功能。以下列表显示了 DISTRO_FEATURES 支持值的当前状态,详细信息请参见 Distro Features 部分,位于 Yocto Project Reference Manualdocs.yoctoproject.org/4.0.4/ref-manual/features.html#distro-features):

  • 3g: 包括对蜂窝数据的支持

  • acl: 包括访问控制列表(Access Control List,简称 ACL)支持

  • alsa: 包括高级 Linux 声音架构(Advanced Linux Sound Architecture,简称 ALSA)支持(如果有的话,会安装 OSS 兼容内核模块)

  • api-documentation: 在配方构建过程中启用生成 API 文档

  • bluetooth: 包括蓝牙支持(仅集成 BT)

  • cramfs: 包括 CramFS 支持

  • debuginfod: 包括通过 debuginfod 服务器获取 ELF 调试信息的支持

  • ext2: 包括支持具有内部 HDD/Microdrive 的设备的工具(用于存储文件,适用于仅 Flash 设备以外的设备)

  • gobject-introspection-data: 包括支持 GObject 反射的数据

  • ipsec: 包括 IPSec 支持

  • ipv4: 包括对 IPv4 的支持

  • ipv6: 包括对 IPv6 的支持

  • keyboard: 包括对键盘的支持

  • ldconfig: 包括对目标设备上的 ldconfigld.so.conf 的支持

  • ld-is-gold: 使用 gold 链接器,而非标准的 GNU 链接器(bfd

  • lto: 启用链接时优化(Link-Time Optimization)

  • multiarch: 使您能够构建支持多种架构的应用程序

  • nfc: 包括对近场通信(Near Field Communication,简称 NFC)的支持

  • nfs: 包括 NFS 客户端支持

  • nls: 包括 本地语言 支持NLS

  • opengl: 包括 Open Graphics Library 支持,它是一个跨语言、多平台的 API,用于渲染二维和三维图形

  • overlayfs: 包括 OverlayFS 支持

  • pam: 包括 可插拔身份验证模块PAM)支持

  • pci: 包括 PCI 总线支持

  • pcmcia: 包括对 PCMCIA/CompactFlash 的支持

  • polkit: 包括 Polkit 支持

  • ppp: 包括 PPP 拨号支持

  • ptest: 使您能够构建由各个配方支持的包测试

  • pulseaudio: 包括对 PulseAudio 的支持

  • seccomp: 使您能够构建支持 seccomp 的应用程序,从而严格限制应用程序可调用的系统调用

  • selinux: 包括对 meta-selinux 的支持

  • smbfs: 包括 SMB 网络客户端支持

  • systemd: 包括对此 init 管理器的支持,完全替代 init,并具备并行启动服务、减少 shell 开销等功能

  • usbgadget: 包括 USB 小工具设备支持

  • usbhost: 包括 USB 主机支持

  • usrmerge:将 /bin/sbin/lib/lib64 目录合并到 /usr 目录下的相应目录中,以提供更好的包和应用程序兼容性

  • vfat:包括对 FAT 文件系统的支持

  • vulkan:包括对 Vulkan API 的支持

  • wayland:包括 Wayland 显示服务器协议及其支持库

  • wifi:包括 Wi-Fi 支持(仅限集成)

  • x11:包括 X 服务器及其库

  • xattr:包括对扩展文件属性的支持

  • zeroconf:包括对零配置网络的支持

MACHINE_FEATURES 与 DISTRO_FEATURES

DISTRO_FEATURESMACHINE_FEATURES 变量一起工作,为最终系统提供可行的支持。当机器支持某个特性时,并不意味着目标系统也支持它,因为发行版必须提供其底层基础支持。

例如,如果某台机器支持 Wi-Fi,但该发行版不支持,那么操作系统使用的应用程序将构建为禁用 Wi-Fi 支持的版本,因此最终的系统将不包含 Wi-Fi 支持。另一方面,如果发行版提供了 Wi-Fi 支持而机器不支持,那么构建该机器的镜像时将不会安装 Wi-Fi 所需的模块和应用程序。然而,操作系统及其模块将启用 Wi-Fi 支持。

理解变量的作用范围

BitBake 元数据包含成千上万的变量,但这些变量可用的作用范围取决于定义它的位置。变量有两种类型,如下所示:

  • 配置文件中定义的变量是全局的,适用于每个配方,也称为配置元数据。主配置文件的解析顺序如下所示:

    • build/conf/local.conf

    • <``layer>/conf/machines/<machine>.conf

    • <``layer>/conf/distro/<distro>.conf

  • 在配方文件中定义的变量具有配方可见性范围,仅在执行任务时对特定配方有效。

总结

在本章中,我们介绍了如何创建新的层和元数据。首先,我们了解了如何创建机器配置、发行版定义和配方文件。然后,我们学习了如何制作镜像并将应用程序包含到镜像中。

在下一章中,我们将访问一些额外层常用的定制案例示例,例如修改现有包、向 autoconf 添加额外选项、应用新补丁以及将新文件包含到包中。我们将看到如何配置 BusyBoxlinux-yocto,这两个包是嵌入式系统制作过程中常见的定制包。

第十三章:自定义现有食谱

在使用 Yocto 项目工具的过程中,预计我们需要自定义现有的食谱。在本章中,我们将探讨一些示例,例如更改编译选项、启用或禁用食谱的功能、应用额外的补丁,以及使用配置片段来自定义一些食谱。

理解常见的使用案例

现在,项目通常会有一组层来提供所需的功能。我们肯定需要在这些层的基础上进行更改,以适应我们的特定需求。这些更改可能是外观上的,也可能是实质性的,但实现方法是相同的。

我们必须创建一个 .bbappend 文件来更改项目层中的现有食谱。例如,假设原始食谱名为 <original-layer>/recipes-core/app/app_1.2.3.bb。当你创建一个 .bbappend 文件时,可以使用 % 通配符来匹配食谱名称。因此,.bbappend 文件可以有以下不同的形式:

  • App_1.2.3.bbappend:这只适用于 1.2.3 版本

  • app_1.2.%.bbappend:这只适用于 1.2.y 版本

  • app_1.%.bbappend:这只适用于 1.x1.x.y 版本

  • app_%.bbappend:这适用于任何版本

我们可以有多个 .bbappend 文件,具体取决于我们希望对应用食谱进行的更改。有时我们可以将更改限制在某个版本,但有时我们希望更改所有可用的食谱。

注意

当一个食谱有多个 .bbappend 文件时,所有文件都会按照层的优先级顺序合并。

.bbappend 文件可以看作是附加在原始食谱末尾的文本。它为我们提供了一个高度灵活的机制,避免了重复源代码来应用所需的更改到项目的层。

扩展任务

当任务内容无法满足我们的要求时,我们会替换它(提供我们自己的实现)或附加内容。正如我们在第八章《深入了解 BitBake 元数据》中将更广泛地学习 BitBake 元数据语法一样,:append:prepend 运算符可以用来扩展任务并加入额外内容。例如,要扩展 do_install 任务,我们可以使用以下代码:

图 13.1 – 扩展 do_install 任务的示例

图 13.1 – 扩展 do_install 任务的示例

这样,新的内容就会连接到原始任务中。

基于 Autotools 向食谱添加额外选项

假设我们有一个基于 Autotools 的应用程序,并且有一个现有的食谱,我们希望执行以下操作:

  • 启用 my-feature

  • 禁用 another-feature

用于修改的 .bbappend 文件内容如下:

图 13.2 – 向 Autoconf 标志添加额外配置

图 13.2 – 向 Autoconf 标志添加额外配置

如果我们需要根据构建的硬件有条件地启用它,同样的策略也可以使用,具体如下:

图 13.3 – 有条件地添加额外的配置到 Autoconf 标志

图 13.3 – 有条件地添加额外的配置到 Autoconf 标志

Yocto 项目支持多种不同的构建系统,配置它们的变量如下表所示:

构建系统 变量
Autotools EXTRA_OECONF
Cargo EXTRA_OECARGO
CMake EXTRA_OECMAKE
Make EXTRA_OEMAKE
Meson EXTRA_OEMESON
NPM EXTRA_OENPM
SCons EXTRA_OESCONS
WAF EXTRA_OEWAF

表 13.1 – 配置每个构建系统的变量列表

表 13.1中的变量作为各自构建系统的参数传递。

应用补丁

在需要对现有软件包应用补丁的情况下,我们应使用FILESEXTRAPATHS,该变量会将新目录包含到搜索算法中,使额外的文件对 BitBake 可见,如下所示:

图 13.4 – .bbappend 文件内容仅用于应用 mypatch.patch

图 13.4 – .bbappend 文件内容仅用于应用 mypatch.patch

在前面的示例中,THISDIR展开为当前目录,PNPV分别展开为软件包名称和版本。然后,这个新路径会被包含到用于文件搜索的目录列表中。预处理操作符至关重要,因为它保证即使未来在优先级较低的层中添加了相同名称的文件,文件仍然会从此目录中被选中。

BitBake 假定每个.patch.diff扩展名的文件都是补丁文件,并会相应地应用它们。

向现有软件包添加额外的文件

如果我们需要包含额外的配置文件,应使用FILESEXTRAPATHS,正如之前示例中所解释的,并在以下代码行中展示:

图 13.5 – 安装新配置文件的.bbappend 文件内容

图 13.5 – 安装新配置文件的.bbappend 文件内容

do_install:append函数将提供的代码块追加到原始do_install函数中已存在的元数据下方。它包括将新配置文件复制到软件包的文件系统中的命令。文件从${WORKDIR}复制到${D},因为这两个目录是 Poky 用于构建软件包的目录,也是 Poky 用于创建软件包的目标目录。

在我们的配方中,有许多变量用来定义路径,例如 bindirdatadirsysconfdirpoky/meta/conf/bitbake.conf 文件定义了所有这些常用变量。这些变量的存在使得可以根据使用场景自定义二进制文件的安装路径。例如,本地 SDK 二进制文件需要特定的安装路径,以避免与目标二进制文件发生冲突。

以下表格显示了最常用的变量及其默认展开值:

变量 默认展开值
base_bindir /``bin
base_sbindir /``sbin
sysconfdir /``etc
localstatedir /``var
datadir /``usr/share
bindir /``usr/bin
sbindir /``usr/sbin
libdir /usr/lib/usr/lib64
libexecdir /``usr/libexec
includedir /``usr/include

表 13.2 – 常用变量及其默认展开值的列表

配方中应避免使用硬编码路径,以减少配置错误的风险。例如,当使用 usrmerge DISTRO_FEATURE 时,所有配方都会将 base_bindir 设置为 bindir,因此如果某个配方使用 /bin 作为硬编码路径,安装将无法按预期进行。

理解文件搜索路径

当一个文件(如补丁或通用文件)被包含在SRC_URI中时,BitBake 会搜索 FILESPATHFILESEXTRAPATH 变量。默认设置是查找以下位置:

  1. <``recipe>-<version>/

  2. <``recipe>/

  3. files/

除此之外,它还会检查每个文件在每个文件夹中是否有要覆盖的OVERRIDES。为了说明这一点,考虑foo_1.0.bb配方。对于该文件,OVERRIDES = "<board>:<arch>"变量将在以下目录中按精确顺序进行搜索:

  1. foo-1.0/<board>/

  2. foo-1.0/<arch>/

  3. foo-1.0/

  4. foo/<board>/

  5. foo/<arch>/

  6. foo/

  7. files/<board>/

  8. files/<arch>/

  9. files/

这仅仅是举例,因为 OVERRIDES 的列表庞大且与机器相关。当我们使用配方时,可以使用 bitbake-getvar OVERRIDES 查找特定机器的可用覆盖项列表,并相应地使用它们。以下是 Poky 的输出示例:

图 13.6 – 使用 bitbake-getvar 获取 OVERRIDES 变量的值

图 13.6 – 使用 bitbake-getvar 获取 OVERRIDES 变量的值

该命令在调试过程中非常有用,可以帮助调试元数据。

更改配方功能配置

PACKAGECONFIG 是一种简化配方功能集定制的机制。它提供了一种启用和禁用配方功能的方法。例如,配方有如下配置:

图 13.7 – PACKAGECONFIG 示例

图 13.7 – PACKAGECONFIG 示例

图 13.7 有两个功能:feature1feature2。每个功能的行为由六个参数定义,参数之间用逗号分隔。您可以省略任何参数,但必须保留分隔逗号。顺序至关重要,指定如下:

  1. 如果启用了该功能,额外的参数。

  2. 如果功能被禁用,额外的参数。

  3. 如果启用了该功能,额外的构建依赖项(DEPENDS)。

  4. 如果启用了该功能,额外的运行时依赖项(RDEPENDS)。

  5. 如果启用了该功能,额外的运行时推荐项(RRECOMMENDS)。

  6. 该功能的任何冲突(互斥)PACKAGECONFIG 设置。

我们可以创建一个 .bbappend 文件,扩展 PACKAGECONFIG 变量的默认值,以启用 feature2,如下面所示:

图 13.8 – 用于扩展 PACKAGECONFIG 变量的 .bbappend 文件内容

图 13.8 – 用于扩展 PACKAGECONFIG 变量的 .bbappend 文件内容

注意

要将相同的功能添加到 build/conf/local.conf 文件中,我们可以使用 PACKAGECONFIG:pn-<recipename>:append = ' feature2'

必须在配方文件中检查特定包的可用 PACKAGECONFIG 功能列表,因为没有工具可以列出所有功能。

基于 Kconfig 的项目的配置片段

Kconfig 配置基础设施因其灵活性和表现力而受到欢迎。尽管它最初用于 Linux 内核,但一些其他项目也使用相同的基础设施,例如 U-Boot 和 BusyBox。

该配置基于选择性特性,您可以启用或禁用一个特性,并将此选择的结果保存在文件中以供以后使用。请考虑以下图示:

图 13.9 – 在 BusyBox KConfig 上启用或禁用 TFTPD

图 13.9 – 在 BusyBox KConfig 上启用或禁用 TFTPD

我们可以控制是否启用 BusyBox 中的 TFTPD 支持 (a) 或不启用 (b)

Yocto 项目提供了一个专门的类来处理基于 Kconfig 的项目配置,允许进行称为配置片段的微小修改。我们可以使用它来启用或禁用您的机器功能,例如,在配置 linux-yocto 时,我们可以像下面的代码一样使用 <layer>/recipes-kernel/linux/linux-yocto_%.bbappend

图 13.10 – 应用片段的 .bbappend 内容

图 13.10 – 应用片段的 .bbappend 内容

每个配置片段必须使用 .cfg 文件扩展名。因此,<layer>/recipes-kernel/linux/linux-yocto/linux-yocto/enable-can.cfg 文件的内容如下所示:

图 13.11 – enable-can.cfg 的内容

图 13.11 – enable-can.cfg 的内容

我们可以使用 BitBake 配置或生成 Linux 内核配置文件。允许我们配置 Linux 内核的命令 bitbake virtual/kernel -c menuconfig 如下截图所示:

图 13.12 – 使用 bitbake virtual/kernel -c menuconfig 启用 CAN 总线子系统支持

图 13.12 – 使用 bitbake virtual/kernel -c menuconfig 启用 CAN 总线子系统支持

图 13.12 显示了如何使用 Linux 内核的 menuconfig 启用 CAN 总线支持。退出并保存 menuconfig 时,内核配置会发生变化。

下一步是使用 bitbake virtual/kernel -c diffconfig 创建片段,如下截图所示:

图 13.13 –  选项生成配置片段

图 13.13 – diffconfig 选项生成配置片段

图 13.13 显示了命令执行后的日志。需要注意的是,片段文件会在 <build>/tmp/work/ 目录下创建,并且绝对路径会在日志中显示。我们必须将此片段文件复制到层中,并在 .bbappend 文件中使用它,以便应用配置。

提示

要保存完整的配置,我们可以使用 bitbake virtual/kernel -c savedefconfig 命令。此命令生成一个 defconfig 文件来复制相同的配置。这是一个完整的配置,而不是片段文件。

配置片段支持适用于以下食谱:

  • Linux 内核

  • U-Boot

  • BusyBox

这些食谱还提供了 menuconfigdiffconfig 任务。

总结

在本章中,我们学习了如何使用 .bbappend 文件定制现有的食谱,并通过此方法避免了源代码的重复。我们了解了如何启用或禁用某个功能,如何应用补丁,以及如何使用配置片段支持。

在下一章中,我们将讨论 Yocto 项目如何帮助我们处理使用不同许可证的包构建基于 Linux 的系统的法律方面。我们将了解需要哪些构件,以及如何配置 Poky 以生成应作为 copyleft 合规过程一部分共享的构件。

第十四章:实现 GPL 合规性

在本章中,我们将看到如何确保开源许可证合规性,并使用 Poky 提供所需的工件,如源代码、许可文本和衍生作品列表。这对大多数如今推出的市场产品至关重要,因为开源代码需要与专有代码共同存在。

理解 copyleft

Copyleft 是一种利用版权法最大化权利并表达自由的合法方式。然而,它会影响我们的产品。我们必须满足所有开源和自由软件许可证的义务。

在构建 Linux 发行版时,至少使用了两个项目:Linux 内核和编译器。GNU 编译器集合 (GCC) 仍然是最常用的编译器。Linux 内核使用 通用公共许可证 (GPL) v2 许可证,而 GCC 根据所使用的项目不同,使用 GPLv2GPLv2.1GPLv3 许可证。

然而,一个基于 Linux 的系统可以包含全球几乎所有的项目,此外还包括公司为其产品开发的所有应用程序。那么,我们如何知道包含了多少项目和许可证,并且如何满足 copyleft 合规性要求呢?

注意

本章描述了 Yocto 项目如何帮助你完成此任务,但请注意,你必须清楚地知道你需要提供的内容以及可能的许可证不兼容问题。如果有任何疑问,请咨询你的法律部门或版权律师。

在下一节中,我们将探讨 Yocto 项目如何帮助我们完成实现 copyleft 合规性所需的常见任务。

理解 copyleft 合规性与专有代码的区别

理解专有代码和 copyleft 覆盖代码能够共存于同一产品中是非常重要的。虽然这是如今大多数产品的标准,但我们必须小心我们链接到代码的库,因为有些库可能存在许可证兼容性问题。

一个基于 Linux 的系统由多个项目组成,每个项目都有不同的许可证。Yocto 项目帮助开发者理解,大多数 copyleft 项目有以下义务:

  • 项目的源代码

  • 项目的许可证

  • 对项目的任何修改

  • 配置和构建所需的任何脚本

如果某个 copyleft 项目被修改,许可证文本、基础源代码以及任何修改都必须包含在最终交付物中。

这些假设涵盖了 copyleft 许可证所保障的大多数权利。这些是 Yocto 项目可以帮助我们的部分。然而,在发布任何内容之前,建议我们审计所有待发布的材料,以确保它们的完整性。

使用 Poky 管理软件许可

一个重要的 Poky 特性是能够管理许可证。大多数时候,我们只关心我们的 bug。然而,管理许可证和所使用的许可证类型对于创建产品至关重要。

Poky 会跟踪每个配方中的许可证。此外,它在开发周期中有一个与专有应用程序协作的策略。

注意

需要了解的一件重要事情是,配方是根据特定许可证发布的,并且代表一个在不同许可证下发布的项目。因此,配方和项目是两个独立的实体,各自有具体的许可证,因此必须将这两个许可证视为产品的一部分。

在大多数配方中,信息通常是一个包含版权、许可证和作者姓名的注释,这些信息与配方本身相关。然后,有一组变量用于描述包的许可证,它们如下所示:

  • LICENSE:它描述了包发布所依据的许可证。

  • LIC_FILES_CHKSUM:乍一看,这可能看起来没有什么用处。它描述了特定包的许可证文件和校验和,我们可能会发现一个项目在描述其许可证时有很多不同的方式。最常见的许可证文件存储在meta/files/common-licenses/中。

一些项目包含诸如 COPYINGLICENSE 等文件,指定源代码的许可证。其他项目则在每个文件或主文件中使用头部注释。LIC_FILES_CHKSUM 变量包含项目许可证文本的校验和;如果任何字母发生变化,校验和也会变化。这确保了任何更改都被记录下来,并且开发人员有意识地接受了这些更改。许可证的变化可能只是修复了一个拼写错误;然而,它也可能是法律义务的变化,因此开发人员需要审查并理解这种差异。

当检测到不同的许可证校验和时,BitBake 会触发构建错误,并指向许可证发生更改的项目。发生这种情况时,必须小心,因为许可证的变化可能会影响软件的使用。为了能够重新构建任何东西,必须相应地更改LIC_FILE_CHKSUM值,并更新LICENSE字段以匹配许可证的变化。如果许可证条款发生了变化,应咨询你的法务部门。如果许可证有变动,最好在提交信息中记录更改的原因,以便将来参考。

理解商业许可证

默认情况下,Poky 不使用任何具有商业许可证限制的配方。在配方文件中,LICENSE_FLAGS 变量用于标识该配方具有的许可证限制。对于 gstreamer1.0-plugins-ugly 配方,许可证相关的变量从 第 5 行第 10 行,如 图 14.1 所示:

图 14.1 – gstreamer1.0-plugins-ugly 配方的许可证相关变量

图 14.1 – gstreamer1.0-plugins-ugly 配方的许可证相关变量

第 10 行 表示该配方需要明确接受 commercial 许可证标志才能使用该配方。为了允许使用 gstreamer1.0-plugins-ugly 配方,我们可以使用以下方法:

图 14.2 – 如何接受安装带有商业许可证限制的食谱

图 14.2 – 如何接受安装带有商业许可证限制的食谱

我们可以在自定义发行版(例如 <my-layer>/conf/distro/my-distro.conf)中,或在 build/conf/local.conf 中配置 LICENSE_FLAGS_ACCEPTED,以便在开发初期接受安装每个需要此标志的食谱。使用 commercial 标志可以接受安装每个需要此标志的食谱。但有时我们希望管理所使用的食谱,并要求特定的许可证条款。在这种情况下,我们可以使用以下格式:

图 14.3 – 如何只接受安装 gstreamer1.0-plugins-ugly

图 14.3 – 如何只接受安装 gstreamer1.0-plugins-ugly

使用 图 14.3 中的代码,我们仅接受 gstreamer1.0-plugins-ugly(这是食谱名称)上的 commercial 许可证标志。确保启用此标志对于一组已获商业使用许可的食谱是一个良好的实践。请咨询您的法务部门以确保这一点。

使用 Poky 实现 copyleft 合规

到这一步,我们已经了解了如何使用 Poky,并理解了它的主要目标。接下来是时候了解在构建基于 Linux 的系统时,如何处理使用不同许可证下的软件包的法律问题了。

我们可以配置 Poky 生成应作为 copyleft 合规过程一部分共享的工件。

理解许可证审计

为了帮助我们实现 copyleft 合规,Poky 在镜像构建过程中生成一个许可证清单,位于 build/tmp/deploy/licenses/<image_name-machine_name>-<datastamp>/

为了演示这个过程,我们将使用 qemux86-64 机器的 core-image-full-cmdline 镜像。首先查看位于 build/tmp/deploy/licenses/core-image-full-cmdline-qemux86-64-<datastamp> 下的文件,内容如下:

  • image_license.manifest:该文件列出了食谱名称、版本、许可证和在 build/tmp/deploy/image/<machine> 中可用但未安装到 根文件系统rootfs)中的软件包文件。最常见的例子是引导加载程序、Linux 内核镜像和 DTB 文件。

  • package.manifest:该文件列出了镜像中的所有软件包。

  • license.manifest:该文件列出了所有已安装软件包的名称、版本、食谱名称和许可证。此清单可用于 copyleft 合规性审计。

图 14.4 – build/tmp/deploy 下许可证清单的目录布局

图 14.4 – build/tmp/deploy 下许可证清单的目录布局

每个食谱的许可证清单位于 build/tmp/deploy/licenses/<package-name> 下。图 14.4 显示了一些软件包的目录布局。

提供源代码

Poky 帮助我们提供每个项目源代码的最直接方式是共享 DL_DIR 的内容。然而,这种方法有一个关键的陷阱——任何专有源代码如果直接共享,将会被放入 DL_DIR 中。此外,这种方法还会共享任何源代码,包括那些不需要 copyleft 合规性的部分。

必须在创建最终镜像之前配置 Poky 以归档源代码。为了实现这一点,我们可以像 图 14.5 所示,将以下变量添加到 build/conf/local.conf 中:

图 14.5 – 配置 Poky 以提供 copyleft 下的软件包源代码

图 14.5 – 配置 Poky 以提供 copyleft 下的软件包源代码

archiver 类会复制源代码、补丁和过滤后的许可证集合的脚本。默认配置是将 COPYLEFT_LICENSE_INCLUDE 设置为 "GPL* LGPL* AGPL*",因此使用这些许可证的源代码将被复制到 build/tmp/deploy/sources/<architecture> 文件夹中:

图 14.6 – build/tmp/deploy/sources 目录布局

图 14.6 – build/tmp/deploy/sources 目录布局

该类还支持 COPYLEFT_LICENSE_EXCLUDE 变量,以确保使用特定许可证源代码的软件包永远不会进入 sources 目录。默认情况下,它设置为 "CLOSED Proprietary"图 14.6 显示了在 core-image-full-cmdline 构建后的一些食谱示例。

提供编译脚本和源代码修改

使用前面章节中提供的配置,Poky 会为每个项目打包原始源代码。如果我们想要包括经过修改的源代码,只需使用 ARCHIVER_MODE[src] = "patched";这样,Poky 会在 do_patch 任务后将项目源代码打包。这包括了来自食谱或 .bbappend 文件的修改。

这样,源代码和任何修改都可以轻松共享。然而,仍然有一种信息需要被创建:用于配置和构建项目的过程。

为了拥有可重复的构建环境,我们可以共享配置后的项目,换句话说,就是 do_configure 任务之后的项目。我们可以将 ARCHIVER_MODE[src] = "configured" 添加到 build/conf/local.conf 来实现这一点。

需要记住的是,我们必须考虑到对方可能并没有使用 Yocto 项目来确保 copyleft 合规;或者,如果他们使用了 Yocto 项目,他们必须知道原始源代码和配置过程的修改是不可用的。这就是为什么我们共享配置后的项目:它允许任何人重现我们的构建环境。

对于所有种类的源代码,默认生成的文件是一个 tarball;其他选项会将 ARCHIVER_MODE[srpm] = "1" 添加到 build/conf/local.conf,生成的文件将是一个 SRPM 包。

提供许可证文本

在提供源代码时,许可文本会与其一起共享。如果我们希望将许可文本包含在最终镜像中,可以将以下内容添加到 build/conf/local.conf 中:

图 14.7 – 如何配置 Poky 将许可文本部署到最终镜像中

图 14.7 – 如何配置 Poky 将许可文本部署到最终镜像中

这样,许可证文件将被放置在 rootfs 内的 /usr/share/common-licenses/ 目录下。

摘要

本章中,我们了解了 Poky 如何帮助遵守 copyleft 许可证以及为什么它不应该作为法律资源使用。Poky 使我们能够为我们分发的包生成源代码、复制作业脚本和许可文本。此外,我们还了解了生成的镜像中的许可证清单可以用于审核镜像。

在下一章中,我们将学习如何在实际硬件上使用 Yocto 项目的工具。然后,我们将使用 Yocto 项目为几个实际的开发板生成镜像。

第十五章:启动我们自定义的嵌入式 Linux

是时候了!我们已经准备好启动我们的自定义嵌入式 Linux,因为我们已经掌握了所需的概念,并对 Yocto 项目和 Poky 有了足够的了解。在本章中,我们将实践迄今为止学到的内容,使用外部 BSP 层与 Poky 一起生成镜像,并使用 SD 卡启动以下机器:

  • BeagleBone Black

  • Raspberry Pi 4

  • VisionFive

本章中的概念适用于任何其他开发板,只要该厂商提供了一个可以与 Yocto 项目一起使用的 BSP 层。

寻找合适的 BSP 层

第十一章《探索外部层》中,我们了解到 Yocto 项目允许将其元数据分散到不同的层中。它组织这些元数据,使我们可以选择将哪个具体的元层添加到我们的项目中。

找到开发板的 BSP 层的方式各不相同,但通常我们可以通过访问layers.openembedded.org来找到。我们可以搜索机器名称,网站会在其数据库中查找包含该层的内容。

回顾影响硬件使用的各个方面

本章使用的开发板维护良好且易于使用。然而,使用其他开发板也是一个有效的选择,但实际效果可能有所不同。

当我们选择一块开发板时,第一步是验证其软件支持的质量。低级组件包括以下内容:

  • 引导加载程序(如 U-Boot、GRUB 或 systemd-boot)

  • Linux 内核(包括其他必需的驱动程序,如 GPU 或 WiFi)

  • 硬件加速所需的用户空间软件包

这些都是至关重要的,但并不是唯一需要考虑的方面。Yocto 项目中的集成通常以 BSP 层的形式出现,减少了开发板使用时的摩擦,因为它通常提供以下内容:

  • 可重用的磁盘分区布局(例如,WIC .wks 模板)

  • 开箱即用的机器定义

  • 为硬件加速集成的用户空间软件包(开箱即用)

软件启用的成熟度以及 Yocto 项目 BSP 对使用开发板时的摩擦和使用 Poky 时的开箱体验有着重要影响。

看一看广泛使用的 BSP 层

在本章中,我们将看到一个广泛使用的 BSP 层列表。请注意,这不是一个完整的列表,也不是一个权威的列表。我们希望能帮助你在身边有特定厂商的开发板时,方便地找到所需的层。以下是按字母顺序排列的列表:

  • 全志科技: 这有meta-allwinner

  • AMD: 这有meta-amd

  • 英特尔: 这有meta-intel

  • NXP: 这有meta-freescalemeta-freescale-3rdparty

  • 树莓派: 这有meta-raspberrypi

  • RISC-V: 这有meta-riscv

  • 德州仪器: 这有meta-ti

在接下来的章节中,我们将开始使用示例开发板。

使用物理硬件

为了更好地探索 Yocto 项目的功能,拥有一个真实的开发板是很有帮助的,这样我们可以享受启动定制嵌入式系统的体验。为此,我们尽力收集了最广泛可用的开发板,以便你拥有其中之一的可能性更高。

接下来的章节将介绍以下开发板的操作步骤:

  • BeagleBone Black:BeagleBone Black 是一个基于社区的开发板,拥有来自全球的成员。更多信息请访问beagleboard.org/black/

  • Raspberry Pi 4:全球最著名的 ARM64 开发板,拥有最广泛的社区支持。查看更多详情,请访问www.raspberrypi.org/

  • VisionFive:世界上首个旨在运行 Linux 的经济型 RISC-V 开发板。查看更多详情,请访问www.starfivetech.com/en

所有列出的开发板都由非营利性教育和辅导组织维护,使得社区成为探索嵌入式 Linux 世界的肥沃土壤。下表总结了这些开发板及其主要特点:

开发板版本 特点
BeagleBone Black TI AM335x(单核)512 MB 内存
Raspberry Pi 4 Broadcom BCM2711 64 位 CPU(四核)1 GB 至 8 GB 内存
VisionFive U74 双核 8 GB 内存

表 15.1 – 涵盖的开发板硬件规格

在接下来的章节中,我们将为每个建议的开发板烘焙和启动 Yocto 项目映像。建议你只阅读适用于自己所拥有的开发板的章节。确保参考开发板的文档,以便了解如何为操作做好准备。

BeagleBone Black

在接下来的两个章节中,我们将介绍为BeagleBone Black开发板烘焙和启动映像的步骤。

为 BeagleBone Black 构建映像

要使用此开发板,我们可以依赖meta-yocto-bsp层,该层默认包含在 Poky 中。可以通过git.yoctoproject.org/meta-yocto/tree/meta-yocto-bsp?h=kirkstone访问该 meta 层。

为了创建源结构,请使用以下命令行下载 Poky:

git clone git://git.yoctoproject.org/poky -b kirkstone

完成此步骤后,我们需要创建用于构建的目录。可以使用以下命令行来实现:

source oe-init-build-env build

在我们正确设置了build目录和 BSP 层之后,就可以开始构建了。在build目录中,我们必须运行以下命令:

MACHINE=beaglebone-yocto bitbake core-image-full-cmdline

MACHINE变量可以根据我们希望使用的开发板进行更改,或者在build/conf/local.conf中设置。

启动 BeagleBone Black

构建过程完成后,映像文件将保存在build/tmp/deploy/images/beaglebone-yocto/目录中。我们想要使用的文件是core-image-full-cmdline-beaglebone-yocto.wic

确保指向正确的设备,并再次确认不要在硬盘上进行写入操作。

要将core-image-full-cmdline镜像复制到 SD 卡上,我们应该使用dd工具,如下所示:

sudo dd if=core-image-full-cmdline-beaglebone-yocto.wic of=/dev/<media>

将内容复制到 SD 卡后,插入 SD 卡插槽,连接 HDMI 电缆,然后开机。系统应该能正常启动。

注意

BeagleBone Black 的启动序列首先会尝试从 eMMC 启动,只有当 eMMC 启动失败时才会尝试从 SD 卡启动。开机时按住USER/BOOT按钮会暂时改变启动顺序,确保从 SD 卡启动。要进一步为您的板卡定制这些说明,请参考www.beagleboard.org/black的文档。

Raspberry Pi 4

在接下来的两个章节中,我们将介绍为Raspberry Pi 4板卡制作和启动镜像的步骤。

为 Raspberry Pi 4 制作镜像

要将此板卡支持添加到我们的项目中,我们需要包含meta-raspberrypi元层,它是支持 Raspberry Pi 板卡(包括 Raspberry Pi 4,但不限于它)的 BSP 层。可以通过git.yoctoproject.org/cgit.cgi/meta-raspberrypi/log/?h=kirkstone访问该元层。

要创建源结构,请使用以下命令行下载 Poky:

git clone git://git.yoctoproject.org/poky -b kirkstone

完成此步骤后,我们需要创建一个用于构建的build目录并添加 BSP 层。可以使用以下命令行进行操作:

source oe-init-build-env build
bitbake-layers layerindex-fetch meta-raspberrypi

在我们正确设置了build目录和 BSP 层后,可以开始构建。在build目录下,我们必须运行以下命令:

MACHINE=raspberrypi4 bitbake core-image-full-cmdline

MACHINE变量可以根据我们想要使用的板卡进行修改,或者在build/conf/local.conf中设置。

启动 Raspberry Pi 4

在构建过程完成后,镜像文件将位于build/tmp/deploy/images/raspberrypi4/目录下。我们需要使用的文件是core-image-full-cmdline-raspberrypi4.wic.bz2

确保指向正确的设备,并仔细检查不要写入硬盘。

要将core-image-full-cmdline镜像复制到 SD 卡上,我们应该使用dd工具,如下所示:

bzcat core-image-full-cmdline-raspberrypi4.wic.bz2 | sudo dd of=/dev/<media>

将内容复制到 SD 卡后,插入 SD 卡插槽,连接 HDMI 电缆,然后开机。系统应该能正常启动。

VisionFive

在接下来的两个章节中,我们将介绍为VisionFive板卡制作和启动镜像的步骤。

为 VisionFive 制作镜像

要将此板卡支持添加到我们的项目中,我们需要包含meta-riscv元层,它是支持基于 RISC-V 的板卡(包括 VisionFive,但不限于它)的 BSP 层。可以通过github.com/riscv/meta-riscv/tree/kirkstone访问该元层。

要创建源结构,请使用以下命令行下载 Poky:

git clone git://git.yoctoproject.org/poky -b kirkstone

完成此步骤后,我们必须创建一个用于构建的 build 目录,并添加 BSP 层。我们可以使用以下命令行来实现:

source oe-init-build-env build
bitbake-layers layerindex-fetch meta-riscv

在我们正确设置了 build 目录和 BSP 层后,就可以开始构建了。在 build 目录中,我们必须调用以下命令:

MACHINE=visionfive bitbake core-image-full-cmdline

MACHINE 变量可以根据我们要使用的板卡进行更改,或者在 build/conf/local.conf 中设置。

启动 VisionFive

构建过程结束后,镜像将出现在 build/tmp/deploy/images/visionfive/ 目录中。我们要使用的文件是 core-image-full-cmdline-visionfive.wic.gz

确保指向正确的设备,并仔细检查不要写入你的硬盘。

要将 core-image-full-cmdline 镜像复制到 SD 卡,我们应该使用 dd 工具,命令如下:

zcat core-image-full-cmdline-visionfive.wic.gz | sudo dd of=/dev/<media>

将内容复制到 SD 卡后,插入 SD 卡槽,连接 HDMI 电缆,并开机启动。

注意

VisionFive 没有默认的启动目标,需要手动干预才能启动。请使用以下命令在 U-Boot 提示符下通过串口控制台进行操作:

setenv bootcmd “****run distro_bootcmd”

saveenv

启动

命令 saveenv 是可选的,用于使新配置持久化,以便在重启后能够直接使用。

查看如何获取串口控制台,请参阅 快速入门 指南 (doc-en.rvspace.org/VisionFive/Quick_Start_Guide/)。

采取下一步行动

呼!我们完成了!现在你应该掌握 Yocto 项目构建系统的基本知识,并能够扩展你在其他领域的知识。我们尝试涵盖了使用 Yocto 项目的最常见日常任务。有一些事情你可能需要练习:

  • 创建 bbappend 文件以应用补丁或对配方进行其他更改

  • 创建你的自定义镜像

  • 更改 Linux 内核配置文件(defconfig

  • 更改 BusyBox 配置并包括配置片段以在层中添加或删除功能

  • 为软件包添加新的配方

  • 创建一个包含特定产品机器、配方和镜像的产品层

记住,源代码是最终的知识来源,所以要善用它。

在查找如何做某件事时,找到一个相似的配方可以节省你测试不同方法解决问题的时间。

最终,你很可能会发现自己处于修复或增强 OpenEmbedded Core、一个 meta 层或一个 BSP 的位置。所以,不要害怕——提交补丁,并将反馈和修改请求视为学习和改进解决问题方式的机会。

总结

我们学习了如何发现我们项目中需要使用的板卡的 BSP。通过添加外部 BSP 层并在实际板卡上使用这些层生成镜像,我们巩固了 Yocto 项目的知识。我们还巩固了学习 Yocto 项目其他相关方面所需的背景信息。

在下一章中,我们将探讨如何通过使用 QEMU 来加速产品开发,避免每次开发周期都依赖硬件。

第十六章:通过仿真加速产品开发——QEMU

本章将探讨通过仿真缩短产品开发周期的可能性,并减少对真实硬件的依赖。你将了解使用 QEMU 相比硬件的好处,以及在何种情况下选择真实硬件更为可取。我们还将介绍 runqemu 的功能并展示一些使用案例。

什么是 QEMU?

快速模拟器QEMU)是一个免费的开源软件工具,允许用户在同一物理机器上运行多个架构。它是一个系统模拟器,可以虚拟化完整的设备硬件,包括 CPU、内存、存储和外设。

使用 QEMU 进行测试和调试可以在开发过程中节省时间和精力。它允许开发人员在各种模拟环境中测试他们的代码。

除了其他因素外,Yocto 项目使用 QEMU 在每次发布时对最终镜像进行自动化质量保证QA)测试。在 Yocto 项目的上下文中,QEMU 允许你将使用 Yocto 项目构建的完整镜像作为另一个任务在构建系统上运行。此外,QEMU 帮助在支持的 Yocto 项目架构上运行和测试镜像及应用程序,而无需实际的硬件。

使用 QEMU 相比硬件的好处是什么?

在某些情况下,使用 QEMU 代替真实硬件进行测试和调试可能更为实际:

  • 它允许你在各种模拟环境中快速、轻松地测试代码,而无需不断将其部署到目标设备上

  • 如果你没有运行软件的硬件,或者硬件的可用性有限

  • 当你需要在多个硬件平台上测试软件,而不必设置多个物理机器时

  • 当你希望在受控环境中调试软件,例如内存受限时,以观察其行为

  • 当你想验证不依赖硬件的通用软件,并希望减少测试所需的时间,例如烧录、板级接线等

然而,必须注意,QEMU 是一个软件模拟器,并不总是能完美替代真实硬件。因此,测试软件在真实硬件上是否正确运行可能是必要的。

什么时候选择真实硬件更为可取?

在某些情况下,使用真实硬件进行测试和调试可能更为实用,甚至是必需的,以下是一些例子:

  • 当软件依赖于特定硬件特性时,例如某个特定的视频处理单元VPU)或图形处理单元GPU)特性

  • 在评估软件性能时,QEMU 可能无法复制真实硬件的性能

虽然 QEMU 是一个非常有价值的测试和调试软件的工具,但它并不总是能完全替代真实硬件。

使用 runqemu 功能

QEMU 与 Yocto 项目深度集成,学习如何利用这种集成至关重要,这样我们才能规划项目的测试。runqemu的使用列出了可用的多种选项,您可以在下图中看到:

图 16.1 – runqemu 使用方法

图 16.1 – runqemu 使用方法

有一些 QEMU 的使用场景需要重点强调:

  • 允许选择不同的内核镜像进行测试

  • 允许选择不同的rootfs进行启动

  • 传递内核启动参数的功能

  • 支持使用 OpenGL 或 OpenGL ES 选项的图形环境

  • 可以传递额外的 QEMU 命令行参数

  • 允许仅使用串口控制台进行快速镜像测试

  • 测试音频堆栈支持

  • 测试不同的初始化系统(例如,systemd)

在接下来的几个章节中,我们以qemux86-64机器为参考,介绍一些常见的用例,展示runqemu的主要功能。

使用 runqemu 测试图形应用程序

当我们旨在验证应用程序时,可以忽略嵌入式设备 GPU 的性能,依赖 QEMU 进行验证,例如一个 Qt 或 GTK+应用程序。首先,我们需要构建core-image-weston镜像。接下来,我们可以按以下方式运行验证:

图 16.2 – 运行 QEMU 并启用图形支持后的日志

图 16.2 – 运行 QEMU 并启用图形支持后的日志

接下来,您将看到在 QEMU 中执行core-image-weston的情况:

图 16.3 – QEMU 运行 core-image-weston 的截图

图 16.3 – QEMU 运行 core-image-weston 的截图

上面的截图显示了 Wayland 终端打开,展示了正在运行的 Linux 内核的信息。

使用 runqemu 验证内存限制

当我们旨在验证应用程序的内存使用情况时,可以依赖 QEMU 进行验证。首先,我们需要构建core-image-full-cmdline镜像,并使用以下命令行运行 QEMU:

图 16.4 – 运行 QEMU 并分配 128 MB RAM 后的日志

图 16.4 – 运行 QEMU 并分配 128 MB RAM 后的日志

在下图中,我们可以看到 QEMU 中正在使用的内存量:

图 16.5 – QEMU 运行 core-image-full-cmdline 并分配 128 MB RAM 的截图

图 16.5 – QEMU 运行 core-image-full-cmdline 并分配 128 MB RAM 的截图

更改用于运行 QEMU 的命令行可以帮助我们通过仿真测试一组不同的内存大小。

使用 runqemu 帮助进行镜像回归测试

Yocto 项目提供了一个自动化测试框架,这是 Yocto 项目质量保证过程中的关键部分。集成或验证测试支持使用testimage类在目标设备内执行镜像。

提示

测试框架可以测试现有的配方和镜像,并通过自定义测试进行增强,以验证新应用程序和集成。测试框架的功能在Yocto 项目测试类型概述部分中进行了描述(docs.yoctoproject.org/4.0.4/test-manual/intro.html#yocto-project-tests-types-of-testing-overview)。

首先,我们通过在build/conf/local.conf中添加IMAGE_CLASSES += "testimage"启用了testimage支持,并确保构建了core-image-weston镜像。

警告

在镜像测试过程中,使用了sudo命令进行网络设置,并可能根据主机配置触发错误。请查看Yocto 项目开发任务手册中的在 QEMU 上启用运行时测试部分(docs.yoctoproject.org/4.0.4/dev-manual/common-tasks.html#enabling-runtime-tests-on-qemu),了解如何避免这些错误。

然后,我们必须构建core-image-weston镜像。现在我们可以开始执行testimage,命令如下:

图 16.6 – 运行 testimage 任务的结果,适用于 core-image-weston

图 16.6 – 运行 testimage 任务的结果,适用于 core-image-weston

在之前的日志中,我们看到了回归测试结果。

总结

在本章中,我们学习了如何使用 QEMU,以及它如何通过在可能的情况下进行仿真、在不可能的情况下进行描述,从而缩短开发周期。还介绍了一些runqemu的使用案例。

在最后一章,我们提供了一份清单,列出了多年来在开发基于 Yocto 项目的产品时,作者们一直使用的最佳实践。

第十七章:最佳实践

本章旨在提供我们(作者)多年来在嵌入式设备和嵌入式 Linux 开发方面的个人经验。我们收集了一些常常被低估或完全忽视的方面,以便为你下一个项目提供灵感。

我们将本章分为两个独立部分,一部分涉及与 Yocto 项目相关的指导原则,另一部分涉及项目的一些更一般性的方面。这样,你就不需要按照特定顺序学习这两个部分。

Yocto 项目遵循的指导原则

本节旨在收集一些关于 Yocto 项目元数据和项目组织的指导原则,这些原则可以让我们在短期和长期维护方面更加轻松。

管理层次

随着我们在产品开发中的进展,我们自然会使用多个仓库来满足我们面临的需求。跟踪这些仓库是一个复杂的挑战,因为我们需要做以下几件事:

  • 确保我们将来能够复现之前的构建

  • 允许多个团队成员在同一代码库中工作

  • 使用持续集成工具验证我们所做的更改

  • 避免我们使用的层次中出现细微的变化

这些目标令人畏惧,但目前已有一些工具在使用,并且采取了不同的策略来克服这些挑战。

最简单的解决方案使用image-buildinfo类(docs.yoctoproject.org/4.0.4/ref-manual/classes.html#image-buildinfo-bbclass),该类默认将一个包含构建信息和层次修订的纯文本文件写入目标文件系统的${sysconfdir}/buildinfo。一些工具已经被开发出来,可以帮助这一过程。以下是讨论这些工具的内容:

  • Google 为 Android 开发开发了repo工具(source.android.com/docs/setup/download#repo)。它已经被其他项目采纳。repo 的一个关键特点是,它需要一些工具与基于 Yocto 项目的项目集成,以自动化构建目录和环境配置。可以参考O.S. Systems 嵌入式 Linux 项目github.com/OSSystemsEmbeddedLinux/ossystems-embedded-linux-platform)作为使用 repo 的灵感来源。

  • Siemens 开发了kasgithub.com/siemens/kas),提供了一种简便机制,用于下载源代码、自动化构建目录和环境配置等。

  • Garmin 开发了Whisk (github.com/garmin/whisk),用于通过 OpenEmbedded 和 Yocto 项目管理复杂的产品配置。其主要特点包括单一源树、多个配置轴、多个产品构建、隔离层配置等。

  • Agilent 开发了Yocto Buddy (github.com/Agilent/yb)。该设计旨在简化设置,并保持基于 Yocto 项目的环境同步。Yocto Buddy 的灵感来源于前面提到的所有工具,当前仍处于早期开发阶段。

这是现有工具的一个子集,不应被视为完整列表。理想情况下,您应该在做出决定前先使用它们,因为选择取决于项目用例和团队的专业知识。

避免创建过多的层

Yocto 项目的一个显著优势是,它能够使用和创建多个层。这使我们能够做到以下几点:

  • 重用半导体供应商的 BSP 层

  • 通过共享可重用的模块来减少工作重复,从而支持新的或特定的应用程序、编程语言等。

然而,在开发项目或一组产品时,创建多个层可能没有成效。例如,以下情况中开发仅包含 BSP 的层是有意义的:

  • 系统模块SoM)供应商的案例中,板卡即是产品

  • 当外部需要访问某个层时,然而我们希望限制非 BSP 源的访问

使用单一层来为产品甚至公司提供支持,有许多优势,例如:

  • 促进可重用组件的开发,例如多个产品共享的packagegroup开发工具或网络工具包

  • 降低由于为特定产品或板卡做更改而引发的意外副作用的风险

  • 增加跨多个产品的 bug 修复复用,并重用 BSP 低级组件,如 Linux 内核或引导加载程序

  • 在多个产品之间推动标准化,减少新团队成员的学习曲线

是否使用一个或多个层取决于多个方面;然而,我们建议从简单开始,未来如有需要再拆分层。

为新版本的 Yocto 项目发布准备产品元数据

随着我们产品的增长,元数据也在增长,并且需要良好的组织结构。以下是在产品开发过程中常见的一些用例:

  • 需要回溯一个新配方版本,以修复 bug 或增加新特性

  • Yocto 项目配方中尚未提供缺失的包配置或 bug 修复

我们使用两个配方目录来组织这类内容:

  • recipes-backport: 从新的 Yocto 项目版本回溯的配方

  • recipes-staging: 新的配方或bbappend文件,添加缺失的包配置或 bug 修复

我们持续将新的配方或错误修复从recipes-staging发送到相应的上游项目(例如,OpenEmbedded Core)。然后,当补丁被接受后,我们将此更改从recipes-staging移动到recipes-backport目录。这种方法使我们能够跟踪待上游提交的任务,并轻松将我们的元层升级到新的 Yocto Project 版本。此外,我们可以快速处理回移目录并将其删除。

创建您的自定义发行版

在使用 Yocto Project 时,我们通常会在build/conf/local.conf中添加许多配置。然而,正如书中所讨论的,这样做不好,因为它不在源代码管理中,并且可能在开发者之间有所不同。使用自定义发行版有许多好处,以下是其中一些重点:

  • 允许多个开发者之间的一致使用

  • 提供清晰的视图,展示我们与基础发行版(例如poky)相比所使用的不同DISTRO_FEATURES

  • 提供一个集中地点,让我们可以全局查看产品所需的所有配方配置,从而减少配置配方所需的bbappend文件数量(例如,PACKAGECONFIG:pn-<myrecipe>:append = " myfeature"

除了那些更技术性的方面,使用自定义发行版还允许对 SDK 或其他 Yocto Project 生成的工件进行适当的品牌化。

我们在第十二章使用自定义发行版部分中学会了如何创建自定义发行版,创建 自定义层

避免为您的产品重复使用现有的镜像

镜像是所有内容汇集的地方。当我们开发产品时,出于多种原因,重要的是最小化镜像中安装的包的数量:

  • 减少 rootfs 大小

  • 减少构建时间

  • 减少需要处理的许可证数量

  • 降低安全漏洞的攻击面

一个典型的起点是将core-image-base.bb文件复制到我们的自定义层,并命名为myproduct-image.bb,然后对其进行扩展,添加我们需要的产品镜像内容。此外,我们创建一个名为myproduct-image-dev.bb的镜像,用于开发过程中,并确保它依赖于myproduct-image.bb以及仅用于开发的工件,避免代码重复。这样,我们就有了两个镜像,一个用于生产,一个用于开发,但它们共享相同的核心特性和包。

标准 SDK 通常被低估

应用程序开发意味着一个互动过程,主要是因为我们通常会持续构建应用程序,直到实现我们所期望的目标。这个用例不太适合 Yocto Project,主要是由于以下原因:

  • 每次开始构建配方时,它都会丢弃之前的构建对象

  • 部署应用程序或镜像所需的时间更长

  • 缺乏在 IDE 环境中的适当集成

对于一些主题,有一些替代方案,例如使用devtool来重用构建对象并帮助部署应用程序。我们在《使用 devtool 部署到目标》和《使用 devtool 构建配方》章节中看到了如何使用devtool,但开发体验仍然很繁琐。

对于应用程序和其他组件(如 Linux 内核和引导程序)的开发,使用标准 SDK 仍然更可取。这样我们可以专注于更快的开发,推迟或并行化 Yocto 项目的集成任务。

避免对 Linux 内核和引导程序进行过多的补丁修改

在嵌入式 Linux 开发中,内核和引导程序的补丁需求是内在的,因为我们很少在没有任何更改的情况下使用硬件。这些组件的修改程度与您的硬件设计有关,例如:

  • 使用单板计算机SBC),变更数量应尽量少

  • 在使用系统级模块SOM)与自定义基板时,变更数量可能会因供应商基板硬件设计的修改数量而异

  • 最终,使用自定义硬件设计意味着开发自定义 BSP,因此会有大量修改

这些并非一成不变。例如,考虑使用 SBC 启动项目。后来,我们发现供应商没有提供良好的参考 BSP,因此 BSP 的修改数量和工作量将大幅增加。

当我们有小的变更时,最好将这些变更作为补丁文件添加到组件配方中处理。但是当维护组件的工作量增加时,有必要将这些变更保留在单独的组件分支中。使用存储库分支具有以下优势:

  • 变更历史记录

  • 不同的开发和生产分支或标签

  • 与其他提供者合并的可能性

  • 这允许使用更简单的配方,因为我们不需要进行个别的补丁维护

总之,我们应该使用适合项目的策略。最终会有所变化,但采用正确的方法可以减少支持所用硬件的总体工作量。

避免使用 AUTOREV 作为 SRCREV

在开发产品时通常会将AUTOREV作为SRCREV。我们必须交互式地更改代码,并在 Yocto 项目中尝试该代码。话虽如此,这也伴随着几个缺点:

  • 由于每次重建映像时都可能使用不同的配方修订版,因此很难重现先前的构建。

  • 当 BitBake 使特定配方的缓存无效时,才会应用AUTOREV值。这发生在我们修改配方本身或更改触发 BitBake 缓存重建的任何.conf文件时。

这些缺点使得AUTOREV非常脆弱,而其他替代方案可以更一致地覆盖交互式代码更改。通常使用devtool,因为它允许我们直接在工作区中更改代码,并强制食谱使用此作为源代码。另一个替代方法是使用externalsrc.bbclass类(docs.yoctoproject.org/4.0.4/index.html#ref-classes-externalsrc),它允许我们配置一个食谱,以使用一个目录作为构建源。

创建软件物料清单

Poky 构建系统可以描述镜像中使用的所有组件,包括每个软件组件的许可证。这个描述会生成一个软件物料清单SBOM),使用软件包数据交换SPDX)标准(spdx.dev/)。使用 SPDX 格式的好处是能够利用现有工具,从而实现额外的自动化,这是使用 Poky 的标准许可证输出格式无法做到的。

SBOM 对确保开源许可证合规性至关重要。然而,SBOM 默认不会生成。你可以参考《Yocto 项目开发任务手册》中的创建软件物料清单部分(docs.yoctoproject.org/4.0.4/dev-manual/common-tasks.html#creating-a-software-bill-of-materials)。

通用项目的指南

本节讨论了一些与项目相关的指南,帮助减少一般项目风险,避免常见的陷阱。

持续监控项目的许可证约束

根据我们所从事的项目,许可证合规性可能是一个大问题,也可能是一个小问题。一些项目有非常严格的许可证约束,例如以下内容:

  • 无法使用 GPLv3 发布的软件

  • 项目特定知识产权的复制左传播

  • 公司范围内的许可证约束

建议在项目开始时就启动这一过程,从而减少整个项目中的返工量。然而,项目的许可证约束和项目组件的许可证可能会发生变化,这要求我们持续监控许可证合规性。

安全问题可能会危害你的项目

在我们这个超连接的时代,每个连接的设备都是潜在的安全攻击目标。作为嵌入式设备开发人员,我们应当为创建一个更安全的环境做出贡献。我们应该做以下几点:

  • 扫描我们的嵌入式 Linux 软件以查找已知的安全漏洞

  • 监控关键软件的安全修复

  • 实施修复现场设备的过程

我们可以使用 Yocto 项目的基础设施,正如《Yocto 项目开发任务手册》中的检查漏洞部分所讨论的那样(docs.yoctoproject.org/4.0.4/dev-manual/common-tasks.html#checking-for-vulnerabilities),来扫描我们配方中的已知常见漏洞和暴露(CVE)信息。我们不应仅限于此,因为我们的 BSP 组件也可能需要安全修复,而 BSP 供应商通常会忽视这些修复。不过,过度谨慎的程度取决于项目的细分领域。

不要低估维护成本

起初,上游我们的更改可能看起来并不具备战略意义,原因如下:

  • 上游工作使用资源来适应修改

  • 上游审查反馈可能需要额外的互动和返工

  • 需要完成与产品无直接关联的开发工作

通常,开发和管理团队低估了维护的总成本。但不幸的是,这往往是项目中最昂贵的部分,因为它会持续多年。将我们的更改上游到相应的项目,可以让我们做到以下几点:

  • 避免多年来的工作重复

  • 减少升级新 Yocto 项目版本时的摩擦

  • 接受关于我们正在上游的更改的关键性和建设性反馈

  • 减少与安全更新和漏洞修复相关的工作量

  • 减少我们需要维护的代码量

上游工作是持续性的。每当我们添加一个新特性时,我们可能会增加代码与上游之间的差距。因此,我们可以推迟上游工作,但当你开始更新到下一个 Yocto 项目版本时,上游工作的成本将成倍增加。

尽早解决项目的风险点和限制

由于软件和硬件必须协同工作,因此有些方面直接依赖于我们的硬件设计。为了降低项目风险,我们应该尽可能预见到许多关键的软件和硬件需求,以便能够验证一些方面,例如:

  • 我们打算使用的内存量是否足够或过多?

  • 硬件使用的功率是否足够满足我们的限制?

  • 目标 GPU 是否能够渲染我们需要的动画?

  • 所有计划的外设设备是否都已准备好可用的 Linux 内核驱动,还是我们需要规划开发这些驱动?

前述问题可以通过使用参考板或已准备好的 BSP 板来回答。这使我们能够在无需设计自定义硬件的情况下,生产出最小可行产品(MVP)。在验证项目的风险和限制后,这些板仍然是宝贵的资产,用于以下内容:

  • 继续开发我们的软件,直到自定义板和 BSP 准备好使用

  • 作为与我们自定义设计进行比较的基础

  • 作为验证某个错误是否仅限于我们定制板和 BSP 的参考

考虑到我们可以使用参考板或知名板开发软件,我们应尽可能推迟设计定制板。推迟设计为我们提供了改变项目多个方面的自由,例如由于特定驱动程序需要更换外设,甚至在应用程序和功能成熟后更改计划中的 CPU 和内存能力。

当我们最终决定使用定制设计时,应该尽量保持它与我们选择的参考板相似。但当然,有时我们需要偏离参考设计。然而,这样做有引入设计问题和增加定制 BSP 成本的风险。

摘要

呼!在这一最终章节中,您已了解一系列作者在实际项目中使用的最佳实践。我们希望这些内容能为您在规划下一个项目时提供一些值得考虑的要点。

在本书中,我们已经涵盖了必要的背景知识,以便您能够独立学习 Yocto 项目的其他任何方面。因此,您现在对当您请求 BitBake 构建食谱或镜像时发生的幕后过程有了大致了解。从现在开始,您已经准备好释放思维,尝试新事物。球现在在您这一方——真正的乐趣才刚刚开始!

posted @ 2025-07-04 15:40  绝不原创的飞龙  阅读(453)  评论(0)    收藏  举报