我最近花了一些时间为一个基于 OpenEmbedded 的 linux 项目重构构建环境。我对 Buildroot 没有直接经验,但我希望 OpenEmbedded 与您使用的足够相似。我将描述我的设置,如果幸运的话,您会在这里找到一些有用的东西......
问题
三个软件组件可以单独安装(即相互独立):bootloader(u-boot);内核(Linux);和文件系统映像。我们的最终产品随附这三个组件的打包版本。也就是说,一个版本的 u-boot、linux 和文件系统映像已经过 QA 测试并且已知可以一起工作。但是,可以独立升级任何一个组件(例如安装新的内核映像)以创建尚未一起测试的软件组件的组合。
用户空间应用程序也存在这个问题。一旦文件系统映像安装到目标中,就可以独立于其他文件系统对象更新一个或多个用户空间二进制文件(假设您的文件系统不是只读的)。你怎么知道现在安装的用户空间应用程序的具体组合可以一起工作?我如何确定在这个特定单元中运行的二进制文件组合与经过 QA 认证的二进制文件组合相同?我怎么知道软件的“版本”是什么?
我需要解决的另一个问题,与您在问题中概述的问题相同,是如何让开发人员在软件堆栈的不同部分(内核、根文件系统、用户空间 Qt 应用程序等)上协同工作?
一个办法
我通过以下方式解决了这个问题和“版本”问题:
- 将 rootfs 和 sysroot 存储在 git 存储库中。
- 自由使用 git 子模块。
将目标的根文件系统和系统根文件存储在 git 存储库中最初让我犯了错误(将输出文件存储在版本控制中,什么!?!)但它提供了以下优点:
- 一个 JFFS2 文件系统映像(rootfs + 我们的自定义用户空间应用程序)可以构建,只要它需要构建用户空间应用程序(即数十秒)。不再需要开发人员首先从头开始构建 rootfs(使用 OpenEmbedded 需要几个小时)。
- 版本控制的所有其他优点(随着时间的推移可以轻松跟踪对 rootfs 的更改、发布标签、分支等)。
- 我最初考虑将 rootfs 和 sysroot 存储为 tarball,但我喜欢 git 在每个文件的基础上跟踪更改的想法。
目录结构看起来像这样(一些名称已更改以保护无辜者):
\---proj [*] # Name of your project
+---u-boot [*]
+---linux [*]
+---toolchain [*]
\---fs [*] # Filesystem stuff.
+---oe [*] # OpenEmbedded.
+---qt [*] # Qt framework.
+---apps [*] # Custom user-space applications.
\---bin [*] # Revision controlled binaries
+---rootfs # Target root filesystem, output of OpenEmbedded.
\---sysroot # System root, output of OpenEmbedded (headers, etc).
每个星号目录 [*] 是一个 git 存储库,每个 git 存储库是其父级的子模块。
构建环境是从一个顶级 Makefile 初始化的,该 Makefile 基本上执行递归git submodule init
和git submodule update
. 所有开发人员都会这样做:
$ git clone git@your.url:proj proj
$ cd proj
$ make git-init
然后,用户空间开发人员可以立即构建:
$ make --directory proj/fs/apps all # Build apps
$ make --directory proj/fs install # Create JFFS2 image
文件系统维护者可以更新 rootfs:
$ cd proj/fs/oe
$ # Modify build recipes and other OpenEmbedded black magic stuff.
$ make
$ # Go make coffee while oe builds every package on the planet.
$ cd proj/bin # OE writes output files here.
$ git commit # Commit latest rootfs and sysroot.
软件版本控制
从顶层 makefile ( proj/Makefile
) 可以构建所有软件组件(内核、u-boot、文件系统映像)。使用以下 git 命令,makefile 向所有子 make 进程导出一个VER_TAG
描述当前软件版本的单个环境变量(例如 )。版本是来自 git 存储库的标签或 SHA(例如v1.0
,471087ec254e8e353bb46c533823fc4ece3485b4
或471087ec254e8e353bb46c533823fc4ece3485b4-modified
)。
git rev-parse HEAD # Get current SHA
git status --porcelain | wc -c # Is working copy modified?
git describe --exact-match HEAD # Is the working copy a tag?
如果任何项目子目录中的单个文件已被修改,那么VER_TAG
将始终为xxxx-modified
. 然后,这个单一VER_TAG
变量作为编译时常量传递给所有构建(u-boot、内核、用户空间应用程序等)。
在运行时,自定义用户空间应用程序会累积VER_TAG
来自所有组件的值,如果它们都报告相同的值,则该字符串将成为产品报告的正式版本。如果即使一个VER_TAG
值与其他值不同,那么软件堆栈也不是从相同的顶级 SHA 构建的,并且不能发布到野外(用于测试的 QA,用于制造的生产等)。
如果软件组件不是从顶级 makefile(例如make --directory proj/fs/apps all
)构建的,则VER_TAG
该组件的 将未定义,并且生成的软件堆栈“仅供内部使用”。也就是说,所有软件组件的“发布”只能通过从顶级makefile构建。
作为参考,linuxVER_TAG
通过 procfs 中的自定义文件报告,u-boot 通过 linux 命令行 ( /proc/cmdline
) 报告,每个用户空间应用程序通过进程间通信进行报告。
概括
一个警告。我一个月前才开发了这个构建环境,所以不能声称它的健壮性,但现在它似乎保持在一起......
如果您有需要澄清的具体问题或要点,我很乐意更新我的答案。