
OpenBMC 的活真不是说拿来就能开工的光是把编译环境拉起来就有不少人卡在头三天。玩过 Yocto 的朋友都知道那套东西和你平时 nginx 直接 make 完全不是一个量级——依赖多、下载量大、编译时间长动不动就是二三十 GB 的磁盘空间、一晚上的全量构建。这还是在网络畅通、工具链熟悉的前提下。要是再把“硬件移植”这件事加进来比如给一块新板子加 OpenBMC 支持那开发环境构建就更像是一场需要提前排雷的工程而不是单纯的搭环境。这篇文章聚焦 OpenBMC 开发里的第二步构建一套能正常编译、能输出固件镜像、能直接用于后续硬件移植的开发环境。说清楚目录结构怎么摆、bitbake 怎么跑、新板子怎么加、踩坑怎么排。既有命令也有思路配着实操记录讲尽量让看的人少走点弯路。1. 先把 OpenBMC 的工程构成和构建原理理清楚1.1 它不是一个仓库而是一堆仓库OpenBMC 是 Linux Foundation 托管的开源项目但这玩意儿不是“clone 一个仓库改一行就能跑”的项目。从代码组织方式上看它是由一个主仓openbmc/openbmc加无数个下一级子模块组成的多层次结构。主仓里包含的更多是元数据、构建策略和各子模块的版本指针真正的板级代码、C 服务比如 phosphor 系列、Kernel 补丁、U-Boot 源码、BIOS/BIOS 相关的 fw 接口都是以 Git 子模块的方式嵌进来的。这一点和 Android 的 repo 管理方式有点像。最初玩的时候如果你只浅层 clone 主仓进去会发现meta-phosphor、meta-aspeed、qemu这些目录内容是空的。必须先git submodule update --init --recursive把全部子模块拉下来构建系统才看到真正的源码。从构建系统角度OpenBMC 用的是 Yocto Project底层是 BitBake 和 OpenEmbedded。BitBake 是任务执行引擎OpenEmbedded 的 meta 层负责描述“从源码到镜像”的每一个 step。OpenBMC 的本质就是若干 OE 层叠加比如meta-phosphor放通用 BMC 功能meta-aspeed放 ASpeed 芯片相关meta-google、meta-facebook、meta-ibm等对应各家的板子。理解这个结构构建环境才不会瞎。你处理的问题分几类主仓构建逻辑调整、某个 meta 层的新增改动、某个 C service 的独立编译、kernel 补丁的修改。它们处理的手段不太一样但都在同一套 bitbake 工作流里。1.2 为什么构建环境比代码本身更考验人说句实在话OpenBMC 的代码修改多数是模块化的改一个 D-Bus 方法或者调一个 phosphor-state-manager 的行为很多工程师能搞定。但构建环境这一关卡住的概率反而最高。原因有三点第一是依赖链太长。从 host 系统的 gcc/make 到 Python 模块到chrpath、diffstat、texi2html这些工具任何一环缺了或者版本不对都可能在某次 parse 阶段报错。第二是下载量大。OpenBMC 构建会拉取 Linux Kernel、U-Boot、OpenSSL、glibc 等大批量的源码包动辄四五个 GB网络稍不稳定就失败。第三是环境的一致性要求高。Yocto 官方支持 Ubuntu 和 Fedora 系列但即便是 Ubuntu不同版本对 Python、make 的版本要求也不一样直接用错宿主环境编译到一半才暴露问题是常有的事。所以构建开发环境的本质不光是“能把命令跑通”而是搭建一个稳定、可复现、利于调试的宿主系统。这块值得花时间认真对待省得后面移植新板子时每次改一个 dts 都要重新痛苦挣扎一遍。2. 搭建 OpenBMC 构建环境的前期准备2.1 主机系统怎么选官方一贯推荐 Ubuntu LTS我自己在 Ubuntu 20.04 和 22.04 上都跑过20.04 配老版本 OpenBMC 更顺滑22.04 在编译速度上略好。如果你是纯新手我建议直接用 Ubuntu 22.04遇到问题社区匹配度高依赖安装也容易。CPU 没硬性限制但核心数直接决定编译时间。我在自己的机器上实测过8 核 16 线程编译witherspoon这类完整镜像全量大概需要 3 个小时左右16 核 32 线程能压到 1.5 到 2 小时。内存建议至少 16GB低于这个数并行编译任务稍微开多点就会爆内存bitbake 直接显示MemoryError或者被 OOM-killer 杀掉。磁盘空间是容易被低估的点。OpenBMC 构建时build/downloads里保存所有源码包build/tmp里放编译产物和 sstate 缓存。首次构建一个基本镜像容易摸到 20GB如果多编译几个目标板型翻倍也正常。所以分区至少留 100GB 才玩得开。我当时犯过错误根目录只有 60GB编到一半磁盘满整个构建目录都坏了只能删掉tmp重新来。2.2 系统依赖安装清单不同 Linux 发行版装依赖的命令不同Ubuntu/Debian 系我整理了一份比较完整的最小集照着装就行sudo apt update sudo apt install -y \ git python3 python3-dev python3-venv python3-setuptools \ gcc g make cmake ninja-build \ gawk wget diffstat texinfo chrpath \ rsync cpio file socat iputils-ping \ libssl-dev liblz4-tool zstd \ bison flex autoconf automake libtool \ libncurses-dev libxml2-utils \ unzip zip xz-utils \ bc u-boot-tools注意不同 OpenBMC 版本对 host 包的要求略有差异但上面这套基本覆盖 90% 的场景。如果 build 过程中 BitBake 提示缺了某个 host 工具按提示补装即可。还有个小细节Ubuntu 20.04 自带 Python 3.8而 22.04 是 3.10都不影响编译 OpenBMC 的源码包因为 Yocto 构建时用的是它自己下载到tmp/hosttools里的一套工具链宿主机的大多数工具只用于“启动构建环境”。只有少数如git、tar、gzip这类底层工具会直接调用宿主版本所以宿主里别装些奇奇怪怪的 alias避免干扰。2.3 拉取代码与子模块初始化OpenBMC 主仓现在体积也不小直接普通 clone 没问题但建议加--recursive一次性把所有子模块拉下来git clone https://github.com/openbmc/openbmc.git --recursive如果之前只 clone 了主仓而忘了递归子模块可以在主仓根目录里补git submodule update --init --recursive --depth1推荐加--depth1的原因很实际OpenBMC 的历史提交非常多完整拉取所有历史会占用巨大空间而绝大多数人只需要最新状态。尤其子模块里包含 Linux Kernel 这类仓库全历史动辄几个 GBdepth 限制能省很多时间和磁盘。有一点要提醒OpenBMC 子模块的分支切换和主仓版本是联动的不要乱 checkout 某个子模块的 master 分支。如果主仓指向某个特定 commit子模块也要保持对应 commit否则构建时大概率会出现“recipe 找不到 source”或者编译到一半失败的情况。这也是 Yocto 体系下“可复现构建”的基础。3. 首次构建 OpenBMC 镜像的完整实操3.1 初始化构建目录与选择目标机型先看代码根目录的结构。拉完代码后你会看到一个setup脚本它是构建环境的入口。用法很简单source setup machine比如要构建 IBM Witherspoon 机型的镜像source setup witherspoon执行完这步后项目根目录下会生成build/witherspoon目录并且环境变量也被设置好了这之后才能直接跑bitbake。这个目录就是你的工作区所有编译中间产物都在里面换板型只需要重新执行source setup machine互不影响。如果想看当前支持哪些机器可以直接ls meta-*/*/conf/machine/*.conf或者查看setup脚本里列出的 machine 列表。这里有个新手容易懵的点source setup之后 Bash 提示符前面会多出一串类似openbmc-env的前缀这是正常的说明 BitBake 环境已经激活。如果你开了多个终端每个终端在跑 bitbake 之前都必须重新source setup不然找不到配置环境变量。3.2 目标镜像概念与生成固件OpenBMC 框架里bitbake的默认目标通常不是“镜像文件”而是一个 image 配方。比如bitbake obmc-phosphor-image这个obmc-phosphor-image是 OpenBMC 的基础系统镜像里面包含了 Phosphor 基础服务、BMC 系统管理功能、WebUI 等。如果是硬件移植阶段一般先编这个镜像确保最基础的系统能跑起来再按需追加 layer 和其他功能包。编译完成后镜像产物在build/witherspoon/tmp/deploy/images/witherspoon/目录下常见几个文件obmc-phosphor-image-witherspoon.static.mtd这是最终刷入 BMC 的镜像对应的静态布局 mtd 格式。obmc-phosphor-image-witherspoon.ubiUBI 文件系统镜像。u-boot.bin、u-boot.elfU-Boot 相关。fitimage-*或kernel-fit打包了 kernel 的 FIT 镜像。以*.mtd结尾的是刷机常用固件很多工程师直接拿这个文件通过 ASpeed 的flashrom或者 BMC 内部分区烧写工具写入 Flash。3.3 加速构建的几招实操首次构建全量可能耗时很久这里有几个实测有效的加速方法。最有效的是开启共享状态缓存sstate cache。OpenBMC 默认在每个板型的 build 目录里有自己的tmp/sstate-cache但你可以让多个板型共享一个缓存减少重复编译。修改conf/local.conf设置SSTATE_DIR ? /path/to/shared/sstate-cache DL_DIR ? /path/to/shared/downloadsDL_DIR是所有板型共享的源码包下载目录SSTATE_DIR是编译状态缓存目录。这样当你在不同机器之间切换或者给相同架构的新板子做构建时大量编译任务可以直接命中缓存不用重新编译。第二个加速是增加并行度。在conf/local.conf里加BB_NUMBER_THREADS 16 PARALLEL_MAKE -j16数值按 CPU 核心数调整一般是物理核心数或线程总数。不是越大越好我在 8 核机器上设置 16 后反而因为内存交换导致变慢12GB 内存下建议最多等于核心数。第三招是配置本地镜像源。如果你在国内网络环境GitHub 和 kernel.org 下载源可能不稳定可以换用镜像源站点比如部分大厂内网镜像或高校镜像站。这个根据自己的环境灵活处理核心是把DL_DIR先准备好的源码包填满离线构建也能成功。我自己的习惯是先跑一次bitbake obmc-phosphor-image什么都不改让它老老实实下载所有依赖确认能完整编出镜像。这一步跑通就证明宿主环境和依赖链没问题。之后再开始改代码、加板型遇到问题就能确定是代码改动造成的而不是环境导致的。4. 硬件移植场景下的开发环境配置4.1 新板子从哪里入手OpenBMC 所谓“硬件移植”本质上就是让 OpenBMC 的软件栈支持你手里那块新硬件。相比通用 Linux 移植OpenBMC 的硬件抽象层次更多有 U-Boot、有 Linux Kernel、有设备树dts、有各类传感器监控、有 D-Bus 服务。构建开发环境时就要把这些都纳入到一个可编译的体系里。最常见的起点是“叠一个自己的 meta 层”。OpenBMC 已经提供了非常多 OpenBMC 兼容板型的模板你新做板卡时不一定从零写所有配方可以基于一个 CPU/SOC 最接近的已有机器来扩展。比如你的板子用 AST2600 的 BMC 芯片那就先找到基于 ASpeed AST2600 的现有机器配置复制一份改成自己的机器名逐步替换设备树和外围设备信息。大体流程是在某个 layer 下新建meta-company/meta-board/conf/machine/board.conf。设置机器相关变量PREFERRED_PROVIDER_virtual/kernel、KERNEL_DEVICETREE、UBOOT_MACHINE等。添加 u-boot 的 defconfig 和 Linux kernel 的 defconfig。修改 dts 设备树源文件匹配新产品硬件。继承并调整 image 配方去掉不需要的驱动加上需要的应用。这套改动不是在 OpenBMC 主仓里乱来的而是通过新 layer 去覆盖原 layer 的配方。BitBake 的 layer 优先级机制允许你用高优先级 layer 覆盖低优先级 layer 里的相同配方所以保留 OpenBMC 官方代码不动你的移植改动都放自己的 meta 层里是最安全、最可维护的做法。4.2 layer 配置与依赖关系一个全新的层要有自己的配置文件meta-myboard/conf/layer.conf里面大概长这样BBPATH . :${LAYERDIR} BBFILES ${LAYERDIR}/recipes-*/*/*.bb ${LAYERDIR}/recipes-*/*/*.bbappend BBFILE_COLLECTIONS myboard BBFILE_PATTERN_myboard : ^${LAYERDIR}/ BBFILE_PRIORITY_myboard 9BBFILE_PRIORITY越高遇到同名配方时优先使用你这层的版本。OpenBMC 官方层的优先级一般在 5 到 7 之间你用 9 就能覆盖绝大多数东西。然后在你的 machine conf 里面引用include conf/machine/include/ast2600.inc官方提供的ast2600.inc里已经定义好了很多通用项PREFERRED_VERSION 等。你只需要补上自己机器特有参数比如 Flash 大小、U-Boot config、DTS 文件名。开发环境这一步要特别留意TEMPLATECONF和 meta 层的组合关系。不同板型可能依赖不同的 layer比如用到 AST2600 就必须把meta-aspeed加进bblayers.conf。如果你的板子不需要某些功能比如依赖 BCM 或 Intel 平台的 layer可以从bblayers.conf里删掉减少不必要的解析和编译构建速度也会快一点。4.3 内核和设备树在构建环境中的配置要点OpenBMC 里 Linux Kernel 的构建不是单独 make而是通过 BitBake 配方触发。OpenBMC 默认的内核配方在meta-phosphor/common/recipes-kernel/linux/下面通常叫linux-aspeed.bb或者类似名字。硬件移植时多半要针对新板子生成新的内核 config 或 patch。通常的操作是在自定义 layer 里添加一个 linux-aspeed 的 append 文件比如meta-myboard/recipes-kernel/linux/linux-aspeed_%.bbappend内容里指定你板子的设备树和 defconfigFILESEXTRAPATHS:prepend : ${THISDIR}/files: SRC_URI file://defconfig SRC_URI file://myboard.dts KERNEL_DEVICETREE_myboard aspeed-bmc-myboard.dtb把myboard.dts放到该目录的files子目录下然后用 bitbake 构建内核。BitBake 会把这个 dts 编译成 dtb并打包进 FIT 镜像和最终 BMC 固件里。这里有个常见坑内核设备树的编译是在内核源码树里进行的OpenBMC 又常常使用 prebuilt kernel source 或者多层叠加的补丁如果你改了 dts 但没有强制重新编译内核bitbake 可能认为文件没变化、还用旧缓存。排错时用bitbake -c cleanall linux-aspeed bitbake obmc-phosphor-image强制重新获取、重新展开、重新编译。注意cleanall会删除下载目录里的内核源码缓存下次构建又要重新拉取所以一般用cleansstate比较多bitbake -c cleansstate linux-aspeed这条只会清编译缓存源码包保留重编速度更快。另外OpenBMC 里很多机器直接把 kernel defconfig 内嵌在 machine conf 里比如KBUILD_DEFCONFIG_myboard aspeed_g5_defconfig如果你要用自己的 config 文件可以新增一个.config文件放到内核源码目录或者在 append 文件里执行额外脚本。方法很多但核心思想一致你在自定义层里提供文件其他行为交给 BitBake 去完成。5. OpenBMC 构建环境排错实录5.1 依赖与宿主环境相关报错这类问题最多我整理几个实录过的报错 1Cannot fetch URL ...或者 download 阶段网络超时。最常见的是下载源码包失败。检查网络、DNS、镜像源。如果用的是国内网络GitHub 上的部分大文件容易超时可以配置 git 低限速、或用替代源但最稳妥的是提前把所有依赖包下载到 DL_DIR。另外子模块下载失败可以多试几次git submodule sync git submodule update --init --recursive报错 2Please install the following missing dependencies。当 OpenBMC 要求宿主有gawk、chrpath等而你没有装全时setup 脚本会在开头直接提示。按提示补装sudo apt install gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping报错 3bitbake: command not found。激活环境后bitbake应该可用。如果提示不存在多半是没source setup或者 setup 过程中某个依赖报错导致环境变量没有完整注入。重新执行 source确认开头没有 error。5.2 BitBake 解析阶段的常见问题解析阶段失败很让人崩溃但大部分是语法或配置错误。比如 machine 名字拼写错误bitbake 无法识别或者 layer 依赖缺失、BBFILE_COLLECTIONS名字冲突。这类问题可以通过bitbake-layers show-recipes bitbake-layers show-layers导出当前的解析状态排查是不是某个 meta 层没加载。还有一类典型错误是Missing metadata for machine ...。这通常是因为你写的 machine conf 文件没有落到conf/machine目录或者 layer 没加入bblayers.conf。检查自定义层路径是否被正确添加。5.3 编译阶段典型异常编译阶段最常见的错误是磁盘空间不足和内存不足。磁盘不足在编译大型软件包时非常明显报错类似于No space left on device而且往往在tmp/work/...目录。解决方法是清理旧构建产物bitbake -c cleanall target或者把build/tmp整个删掉重新来前提是你有 sstate 缓存。平时建立目录软链到大分区也是个实用策略比如mkdir -p /data/openbmc-build ln -s /data/openbmc-build build将build目录放到数据盘避开系统盘空间瓶颈。内存不足多见于并行编译过多bitbake 进程直接被 kill。解决降低BB_NUMBER_THREADS和PARALLEL_MAKE或者在local.conf限制任务内存。如果你用 8 核 16GB 内存BB_NUMBER_THREADS 4是稳妥的。5.4 缓存命中与失败重编技巧你改了自定义 layer 里的东西但 bitbake 认为没变化这种问题隐蔽。原因不外乎 sstate 缓存未失效或者本机文件时间戳问题。稳妥做法是使用-c compile/-c install强制编译某个具体阶段而不是每次全量重新构建。比如单独重编内核bitbake -c compile -f linux-aspeed bitbake -c deploy -f linux-aspeed-f强制忽略缓存状态。硬件移植调 dts 和补丁时这个技巧可以说每天都要用。我把踩过的坑按类型整理成了下面这张表方便遇到问题时快速定位。错误类型典型提示排查重点解决方案依赖缺失missing dependency宿主包按依赖列表补装下载失败Cannot fetch网络/仓库配置镜像重试子模块磁盘不足No space left on devicebuild 目录分区清理或迁移 build 目录内存不足OOM killedBB_NUMBER_THREADS降低并行度解析错误ParseErrormachine/layer 配置检查 conf运行 bitbake-layerssstate 缓存不刷新改动未生效sstate 状态使用 cleansstate 或 -f 强制编译6. 关于构建环境稳定复用的几个忠告开发环境搭好以后我强烈建议维护好两个目录downloads和sstate-cache。前者是源码包归档后者是编译状态缓存。在你做硬件移植、反复调整代码的那几个月这两个目录的价值仅次于代码仓库本身。可以把DL_DIR设成一个共享目录比如/opt/openbmc-archive之后所有板型、所有分支都用它SSTATE_DIR可以每套代码对应一份避免不同提交间的缓存污染但同一套代码多次构建之间就可以复用。这个策略帮我节省的编译时间非常可观至少三次以上全量构建改拼接缓存大量公共库和工具链都能直接跳过。最后一个忠告不要把生产用的构建环境折腾成“测试环境”更不要在同一个目录里既跑构建又频繁清缓存。给 OpenBMC 单独一块干净文件夹所有 bitbake 操作都发生在激活环境里所有临时文件都在build/tmp里解决。即使出了问题删掉整个tmp重来也比反复排查环境变量靠谱。我从 OpenBMC 源码构建到第一次完整刷板前后折腾了一周多。回头再看构建环境这一关其实没有太多捷径就是踏踏实实把宿主环境配好、把目录规划好、把第一次全量构建跑通。后续一切硬件移植、功能裁剪、服务开发都建立在这个“能编、能跑、能复现”的基础上。把这个地基打牢了后面的活会顺很多。