书接上文,前面我们主要说了OpenEmbedded构建系统中的构建开源软件包和OpenEmbedded工作流,接下来我们说一下OpenEmbedded构建系统架构的部分,从而对OpenEmbedded构建系统有完整的印象
1.OpenEmbedded构建系统架构
3个基础组件组成了OpenEmbedded构建系统架构:
- 构建系统
- 构建环境
- 元数据层
以下是组件及其相互关系图
除了少量随任何Linux发行版而来的额外软件包以外,OpenEmbedded构建系统为构建嵌入式Linux操作系统栈提供了所有必要组件。包含在内的是:
- BitBake构建引擎;
- 一套为便利性而提供额外功能性的集成脚本;
- 各种工具;
- OpenEmbedded核心——BitBake所需要的用以构建镜像的核心元数据集合;
- DocBook格式的完整的文档。
Poky参考发行版也包括了最小集合的基础元数据层:
- Yocto项目板支持包(meta-yocto-bsp)
- Yocto项目发行版(meta-yocto)
我们在前面的文章中用到了脚本oe-init-build-env,并描述了这个脚本用于创建并初始化构建环境。其实这个脚本是包含在构建系统内的脚本之一。构建系统和构建环境形成1∶n的关系:构建系统可以和任何数量的构建环境相关联,但是构建环境仅仅可以和构建系统相关联。在你一次使用多于一个Yocto项目发布时,这是重要的、你需要知道的限制。你仅仅可以用构建环境和它原来被创建时用到的版本的构建系统。使用不同于原来被用来创建这个构建环境的构建系统来初始化构建环境会导致构建失败。
构建系统总是要包含元数据层,它提供菜谱和配置文件。当你用构建系统的oe-init-build-env脚本创建构建环境的时候,脚本自动设置包含了3个基础层的conf/bblayers.conf文件:
- meta
- meta-yocto-bsp
- meta-yocto。
这些基础层足够来构建标准的Poky参考发行版。然而,作为嵌入式Linux开发者,你最终希望创建你自己的发行版、增加你自己的软件包并最终为目标硬件提供你自己的板支持包。这个目标是通过包含构建系统的其他元数据层来实现的。
1.1.构建系统结构
不管你以tar包的形式下载Poky发行版并且解压它,还是你直接从Yocto项目Git仓库克隆它,它都会得到以下结构的目录
tree -L 1 poky
因为OpenEmbedded构建系统是几乎自包含的,并且不安装任何它的组件到你构建主机的系统目录中,所以在相同的构建主机上使用构建系统的多个版本和不同构建环境是相当容易的。这是非常便利的特性,因为你最终用版本的构建系统来开发和维护产品代,同时用更新的版本来开发下一代以利用新的功能性和特性。
- bitbake
BitBake构建引擎,是OpenEmbedded构建系统不可分割的部分。它与构建一起演进,Yocto项目开发者向BitBake增加新功能性以支持构建系统所需要的新特性。结果,BitBake被包含在构建系统中,你可以在bitbake子目录中找到它。需要知道的是,一些Linux发行版包含你可以使用发行版包管理系统安装的BitBake包。如果你已经在开发主机上安装了BitBake作为发行版的一部分,那么我们建议卸载它,因为它可能干扰包含在构建系统中的版本。构建系统和BitBake是相互匹配的。疏忽地使用不匹配构建系统的BitBake版本可能导致构建失败。
目录bitbake包含包括了BitBake文档和man页面的doc子目录。文档是以DocBook格式写的,并且通过调用make命令,你可以构建PDF和HTML版本的手册。
- documentation
目录documentation包含针对Poky构建系统的文档。以下是你也可以在Yocto项目网站上找到的各种手册:
- brief-yoctoprojectqs - Yocto 项目快速入门
- summary-manual - Yocto 项目概述和概念手册
- contribute-guide - Yocto 项目和 OpenEmbedded 贡献者指南
- ref-manual - Yocto 项目参考手册
- bsp-guide - Yocto 项目板级支持包 (BSP) 开发人员指南
- dev-manual - Yocto 项目开发任务手册
- kernel-dev - Yocto 项目 Linux 内核开发手册
- profile-manual - Yocto 项目分析和跟踪手册
- sdk-manual - Yocto 项目软件开发工具包 (SDK) 开发人员指南。
- toaster-manual - Toaster 用户手册
- test-manual - Yocto 项目测试环境手册
- migration-guides - Yocto 项目发布和迁移说明
各自的手册是以DocBook格式的源在独立的目录中提供的。为了创建PDF和HTML格式的手册,使用命令
make DOC=<manual>
#把<manual>替换成目录的名字。例如,
make DOC=meta-manual
#创建把所有手册包含进一个文件的“mega-manual”。
- LICENSE
LICENSE文件包含了针对Poky构建系统的许可信息。MIT和GPLv2许可混合使用。BitBake是在GPLv2许可下被许可的,并且所有的元数据是在MIT许可下被许可的。如果有任何源代码包含在针对各个菜谱的树中,例如补丁,那么它是以在各个菜谱中阐述的许可被许可的。
对任何文件的许可信息要么是在各自的文件中明确陈述的,要么是在其缺失的情况下默认为GPLv2。
重要的是注意,可能存在并且常见的是,许可一个包的源代码和构建那个包的元数据(菜谱)有差别。一定要保证不要混淆这两者,因为它可能对你正在开发的产品产生影响。
以meta开头的目录都是元数据层:
- meta:OpenEmbedded核心元数据层
- meta-selftest:oe-selftest脚本使用的用来测试BitBake的层
- meta-skeleton:用来创建自己的层的模板
- meta-yocto:Yocto项目发行版层
- meta-yocto-bsp:Yocto项目板支持包层
- oe-init-build-env
脚本oe-init-build-env创建和初始化构建环境。oe-init-build-env以默认设置来创建空的构建环境和初始化先前被创建的构建环境。命令行是
source ./oe-init-build-env <buildenv>
被替换成构建环境的名字。如果没有构建环境名字提供,那么脚本使用默认的名字构建。脚本使用提供的构建环境名字在当前目录下创建子目录。在那个目录中,它创建了2个被任何构建环境需要的配置文件bblayers.conf和local.conf的名为conf的子目录。在那之后,脚本初始化所有必要shell环境变量并且切换目录到构建环境。
如果构建环境目录已经存在并且是OpenEmbedded构建环境,那么oe-init-build-env仅仅初始化shell环境变量和切换目录。
- scripts
有scripts子目录,它包含了一套用于和Yocto项目构建一起使用的集成和支持脚本。最常用到的脚本如下:
- autobuilder-worker-prereq-tests: 可能用于检查自动构建工作者的先决条件是否满足。
- bitbake-prserv-tool: 用于 Bitbake 的 prserv 工具,可能涉及 Bitbake 的状态或性能优化。
- bitbake-whatchanged: 检查 Bitbake 配置或源代码的变更。
- buildall-qemu: 用于构建 QEMU 虚拟机的所有相关组件。
- buildhistory-collect-srcrevs: 收集源代码修订版以生成构建历史记录。
- buildhistory-diff: 比较构建历史记录的不同版本。
- buildstats-diff: 比较构建统计数据的不同版本。
- combo-layer: 可能用于处理多个层的组合和管理。
- combo-layer.conf.example: combo-layer 的示例配置文件。
- combo-layer-hook-default.sh: 默认的 combo-layer 钩子脚本。
- contrib: 贡献的脚本和工具,可能包括社区或第三方开发的工具。
- cp-noerror: 复制文件时忽略错误的工具。
- create-pull-request: 自动创建拉取请求(Pull Request)。
- cross-intercept: 处理交叉编译的拦截或操作。
- crosstap: 可能涉及交叉编译环境的工具或脚本。
- devtool: 用于开发工具集的管理,可能涉及创建、修改或调试配方。
- gen-lockedsig-cache: 生成锁定签名的缓存。
- gen-site-config: 生成站点配置。
- git: Git 相关的工具或脚本。
- install-buildtools: 安装构建工具。
- lib: 库文件目录,包含用于构建的脚本和工具库。
- multilib_header_wrapper.h: 多库头文件的包装器。
- native-intercept: 处理本地构建任务的拦截或操作。
- nativesdk-intercept: 处理 Native SDK 构建任务的拦截或操作。
- oe-buildenv-internal: 用于内部构建环境设置。
- oe-build-perf-report: 生成构建性能报告。
- oe-build-perf-test: 运行构建性能测试。
- oe-check-sstate: 检查 sstate 缓存的状态。
- oe-debuginfod: 处理调试信息的工具。
- oe-depends-dot: 生成依赖关系图。
- oe-find-native-sysroot: 查找本地系统根目录。
- oe-git-archive: 处理 Git 存档。
- oe-git-proxy: 处理 Git 代理。
- oe-gnome-terminal-phonehome: 可能涉及 GNOME 终端的工具。
- oe-pkgdata-browser: 浏览软件包数据。
- oe-pkgdata-browser.glade: oe-pkgdata-browser 的 GUI 布局文件。
- oe-pkgdata-util: 软件包数据实用工具。
- oe-publish-sdk: 发布 SDK 的工具。
- oepydevshell-internal.py: 内部 Python 脚本,用于开发环境。
- oe-pylint: 用于 Pylint 的检查工具。
- oe-run-native: 运行本地构建任务。
- oe-selftest: 运行 Yocto 自身的测试。
- oe-setup-builddir: 设置构建目录。
- oe-test: 运行 Yocto 测试。
- oe-time-dd-test.sh: 时间和调试测试的脚本。
- oe-trim-schemas: 精简 schema 文件的工具。
- opkg-query-helper.py: 辅助 OPKG 查询的 Python 脚本。
- postinst-intercepts: 处理安装后拦截的工具。
- pybootchartgui: 用于图形化显示 Bootchart 的 Python 工具。
- pythondeps: Python 依赖工具。
- recipetool: 用于处理配方的工具。
- relocate_sdk.py: 重新定位 SDK 的 Python 脚本。
- resulttool: 处理构建结果的工具。
- rootfs_rpm-extract-postinst.awk: 用于提取 RPM 后安装脚本的 AWK 脚本。
- rpm2cpio.sh: 将 RPM 包转换为 CPIO 存档的脚本。
- runqemu: 启动 QEMU 虚拟机。
- runqemu-addptable2image: 将表添加到 QEMU 镜像的脚本。
- runqemu-export-rootfs: 导出 QEMU 根文件系统的脚本。
- runqemu-extract-sdk: 提取 QEMU SDK 的脚本。
- runqemu-gen-tapdevs: 生成 QEMU TAP 设备的脚本。
- runqemu-ifdown: 停用 QEMU 网络接口的脚本。
- runqemu-ifup: 启用 QEMU 网络接口的脚本。
- runqemu.README: runqemu 相关的 README 文件。
- send-error-report: 发送错误报告。
- send-pull-request: 发送拉取请求(Pull Request)。
- sstate-cache-management.sh: 管理 sstate 缓存的脚本。
- sstate-diff-machines.sh: 比较不同机器的 sstate 缓存。
- sstate-sysroot-cruft.sh: 清理系统根目录中的冗余文件。
- sysroot-relativelinks.py: 处理系统根目录中的相对链接。
- task-time: 记录和分析任务执行时间。
- test-reexec: 测试重新执行的脚本。
- test-remote-image: 测试远程镜像的脚本。
- tiny: 可能涉及小型构建或测试的工具。
- verify-bashisms: 验证 Bash 脚本中的不兼容语法。
- wic: 与 WIC(Yocto 的图像创建工具)相关的脚本。
- yocto-check-layer: 检查 Yocto 层的工具。
- yocto-check-layer-wrapper: 可能是 yocto-check-layer 的包装器或附加功能。
其中oe-setup-builddir、devtool、recipetool、runqemu、oe-selftest、oe-check-sstate、oe-pkgdata-browser、buildstats-diff、sstate-cache-management.sh、yocto-check-layer、create-pull-request较为常用
1.2.构建环境结构
OpenEmbedded构建系统在构建环境内执行它的全部工作。构建环境也有特定的布局和结构。布局以及其中的所有目录和文件是由构建系统自动化地创建的。构建环境的目录和文件结构是深度嵌套的。可以通过以下命令获取在构建已经运行后的前1级构建环境结构
tree -L 1 build
新创建的构建环境仅仅包含conf子目录和2个文件:bblayers.conf和local.conf。前面在快速构建环境时,我们遇到过也提到过local.conf。这个文件包含用于构建环境的所有配置设置。你也可以向其增加本地覆盖来自被包含层中的设置的变量设置。
文件bblayers.conf包含用于构建环境的层设置。通过以下命令可以看到一个经典的bblayers.conf文件内容
cat build/conf/bblayers.conf
在这个文件中最重要的变量是BBLAYERS,它是空格分隔的、到所有该构建环境包含的层的路径列表。这是你可以增加在构建环境中被包含的额外层的地方。这个文件也设置BBPATH成构建环境的顶级目录,并且用空字符串来初始化菜谱文件列表BBFILES。
其他目录和文件是在构建过程中被创建的。所有的构建输出被放进tmp子目录。你可以通过设置在环境的conf/local.conf文件中的TMPDIR变量来配置这个目录。在tmp目录中的构建输出被组织进大量子目录中:
- buildstats:这个子目录存储根据构建目标和目标被构建时的日期/时间戳组织的构建统计。
- cache:当BitBake初步解析元数据时,它分析依赖性和表达式。解析过程的结果被写入缓存。只要元数据没有改变,BitBake在后续运行时直接从这个缓存获取元数据信息。
- deploy(进入部署阶段将会产生): 针对部署的构建输出,例如目标文件系统镜像、包仓库和许可信息,被包含在deploy子目录中。
- log:你可以在此找到由cooker进程创建的BitBake日志信息。
- sstate-control:这个子目录包含用于根据架构/目标和任务组织的共享状态缓存的程序清单文件。
- stamps:BitBake把针对每个任务、根据架构/目标组织的完成标签和签名数据以及包名放在这个子目录中。
- sysroots:这个子目录包含根据架构/目标组织的根文件系统。内容包括针对构建主机的,包含交叉工具链、QEMU和在构建过程中用到的很多工具的根文件系统。
- work:在这个目录中,BitBake创建根据架构/目标组织的、它在这里构建实际软件包的子目录。
- work-shared:这个子目录和work类似,但是针对共享的软件包。
- 也有2个在tmp目录中值得解释的文件:abi_version和saved_tmpdir。前者包含针对tmp目录布局的版本号码。当布局变化时,这个号码增加。这个号码允许验证构建环境是否和构建系统是兼容的。后者包含tmp目录的绝对文件系统路径。很多在tmp目录下的文件包含绝对文件路径。很不幸,这种安排使得目录不可迁移。虽然这种限制可能不够方便,但是saved_tmpdir文件使得你可以方便地检查目录是否已经被从原始位置移出来了。
尽管构建环境,特别是通常位于其中的tmp目录,不可以被简单地迁移,但是BitBake本质上可以从共享状态缓存重新创建tmp目录的所有内容。共享状态缓存存储自其输入元数据(例如任务代码、变量等)创建的签名所识别的任务的中间输出。只要输入不变,那么签名就不变,使得BitBake使用来自共享状态缓存的输出而不是运行任务。这极大地缩短了构建时间,特别是针对那些花费长时间运行的任务,例如配置或者编译。
1.3.元数据层结构
元数据层是分组和组织菜谱、类、配置文件和其他元数据到逻辑实体中的容器。层通常构建在其他层之上并且彼此互相扩展。OpenEmbedded核心层形成了针对Poky构建系统的层架构的基础。它提供了针对大部分Linux操作系统栈需要的软件包的核心集合的菜谱,当然包括Linux内核、引导加载程序、图形、网络和许多其他包。OpenEmbedded核心也提供基础类以构建软件包、用包管理系统打包软件、创建文件系统和扩展BitBake功能性。
OpenEmbedded核心层本身,由BitBake、便利性和集成脚本所补充,针对构建用于模拟的设备的Linux操作系统栈来说是足够的。OpenEmbedded核心包含基础镜像目标和针对用于ARM、MIPS、PowerPC、x86和x86_64架构的QEMU模拟器的机器定义。
针对设备操作系统栈的构建环境通常包含其他层,例如用于实际硬件的板支持包层;指定用于用户账号、系统启动等操作系统配置的发行版层;用户界面层和用于用户空间应用、提供设备功能性的应用层。整体的架构如下:
1.3.1.层布局
所有层,不管它们服务的目的是什么,都有如程序清单3-4所示的相同的基本结构(目录名字是斜体化的以把它们和文件名区分开)。
常规的层布局如下
元数据层本质上是由目录和文件组成的结构。层顶级目录的名字并不严格;但是,按照惯例,所有的层目录名字以词meta开头、接下来是连字符(-)和层的名字。
每个层必须包含含有该层配置文件layer.conf的conf子目录。BitBake需要这个文件来设置针对元数据文件的路径和搜索模式。layer.conf内容如下:
- BBPATH变量:增加层的目录。变量LAYERDIR被BitBake扩展成层的权威路径名字
- BBFILES增加层的菜谱,使用通配符表达式匹配在这层的菜谱目录的布局。用于菜谱和菜谱追加文件的通配符都需要被增加到BBFILES。
- BBFILE_COLLECTIONS:由空格分隔的层名字的列表。每一层向列表增加它的名字
- BBFILE_PATTERN:包含正则表达式来匹配在BBFILES变量中的这一层的菜谱。这个变量是以层为条件的,因此变量名需要以层的名字作为前缀
- BBFILE_PRIORITY:优先级。因为层互相依赖和扩展,所以处理顺序是重要的。因此,每层通过设置BBFILE_PRIORITY来赋予优先级。层优先级处于1到10之间,1是最低优先级、10是最高优先级。如果2个层使用相同的优先级,那么它们在文件bblayers.conf中的变量BBLAYERS的顺序决定优先级。
- LAYERVERSION:可选,层也可以通过设置变量LAYERVERSION来定义版本号。层版本可以和LAYERDEPENDS变量一起使用以避免包含层的不兼容版本。
- LAYERDEPENDS:如果层依赖于另外的层,那么这些依赖性可以通过增加这些层到LAYERDEPENDS变量来设置,LAYERDEPENDS包含了空格分隔的层名字的列表。如果依赖性是基于层的特定版本,那么版本号可以通过增加冒号(:)和版本号来指定。
子目录conf可能包含其他文件和目录,特别是distro和machine子目录。这些是可选的。典型的是,仅仅发行版层将包含distro子目录,并且仅仅板支持包层通常包含machine子目录。如果存在,那么这2个子目录的每一个都包含用于发行版和机器配置的文件 。
如果层定义自己的类,那么它们位于classes子目录中。
层的菜谱由类别和包分组。类别是一组逻辑上属于一起的包。例如,OpenEmbedded核心元数据层的recipes-connectivity类别包含构建用于网络、电话和其他连接功能软件的包的菜谱。在每个类别子目录中,有针对不同软件包的子目录。这些包子目录包含菜谱、补丁和其他构建软件包所需要的文件。通常,包子目录包含用于构建不同版本的特定包的菜谱。
1.3.2.创建层
使用针对板支持包、应用软件、发行版策略等的层对你自己的项目产生了很多好处。大部分项目从小处开始,但随后越来越多的功能性被增加。即使你一开始仅仅有1个或者2个菜谱,把菜谱放在你自己的层而不是增加它们进OpenEmbedded核心层或者任何Yocto项目层依然是好的实践。
你自己的层从常用菜谱中分离你自己的菜谱,使得对你来说容易从OpenEmbedded构建系统的一个版本迁移到下一个。你仅仅需要用新的构建系统创建新的构建环境,并且在这个构建环境中包含进层。
通过在层中使用bbappend文件,你可以从常用层中调整菜谱而不是复制或者重写它们。考虑下在meta/recipes-kernel/linux中用于构建Linux内核的菜谱。就绝大部分而言,它们已经提供了用于构建内核的所有必要的东西。对你自己的板支持包层,你通常需要仅仅改进几个设置来完全支持目标硬件。不必复制内核菜谱,你可以使用bbappend文件或者包含文件来按照需求定制基础菜谱。
用yocto-layer脚本创建层是简单和直接的。在引用了构建环境后,这个脚本在命令行搜索路径下是直接可用的。简单调用这个脚本如下:
bitbake-layers create-layer <layername>
如果你想创建样例菜谱或样例bbappend文件,那么脚本提示你输入新层的优先级,然后在当前目录中创建包含conf/layer.conf文件和样例文件的层。所有你需要做的是,向你构建环境的conf/bblayers.conf文件中的BBLAYERS变量增加层以在构建过程中包含它。当然,你也需要根据你项目需求而修改样例菜谱或新菜谱、配置文件和类。