4. Buildroot目录结构

理解 Buildroot 的目录结构,是掌握其构建机制的第一步。Buildroot 并不是一个“黑盒”构建系统,它把源码、配置、板级支持、下载缓存、编译产物和主机工具分门别类地放在不同目录中。只有清楚每个目录“负责什么、由谁生成、能否修改”,才能在定制 SDK、排查编译错误、复用下载缓存时做到心中有数。本节将围绕顶层目录、package、board、configs、output、dl、host 等核心目录展开,说明它们各自的职责与相互关系。

一、核心原理:源码树与构建输出分离

Buildroot 的目录设计遵循一个基本原则:源码树保持相对干净,构建产物集中输出。源码树中存放的是构建规则、包描述、板级配置和默认配置文件;而真正的编译结果、下载的压缩包、主机工具链等,则统一放在输出目录中。这样设计的好处是:源码树可以纳入版本管理,输出目录可以随时删除重建,不会污染原始工程。

默认情况下,Buildroot 使用 output/ 作为输出根目录。但通过 BR2_EXTERNAL 和 O= 参数,也可以把输出目录指定到源码树之外,实现“一份源码、多套配置并行构建”。理解这一点,是理解后续所有目录作用的前提。

Buildroot 目录结构与数据流向 源码树(只读/可版本管理) 顶层目录 Makefile / Config.in package/ 软件包构建规则 board/ 板级支持文件 configs/ 默认配置 defconfig toolchain/ 工具链构建逻辑 fs/ linux/ boot/ 文件系统/内核/引导 support/ system/ utils/ 辅助脚本与系统骨架 .config(由 defconfig 生成) 决定本次构建选择哪些包与选项 make 命令读取 .config,驱动整个构建流程 源码树本身不存放编译产物 输出目录 output/(可删除重建) host/ 主机工具与交叉工具链 build/ 各包编译工作目录 target/ 目标根文件系统 images/ 最终镜像与 SDK staging/ 头文件与库的暂存区 per-package/ 按包隔离的构建目录 dl/(下载缓存,可跨工程共享) 存放源码压缩包与补丁,避免重复下载 .config / build.log / legal-info 本次构建的配置副本、日志与许可证信息 output/ 可整体删除,重新 make 即可重建 dl/ 建议保留,节省下载时间 构建

二、各目录职责详解

顶层目录是 Buildroot 的入口,包含 Makefile、Config.in、Config.in.legacy 等文件。Makefile 定义了 make menuconfig、make、make clean 等目标;Config.in 是 Kconfig 配置系统的根入口,它会递归包含各子目录中的 Config.in,最终形成图形化配置菜单。

package/ 是软件包描述目录,每个子目录对应一个软件包,通常包含 Config.in、包名.mk 和可选的 包名.hash。.mk 文件定义包的版本、下载地址、依赖关系、编译与安装命令。Buildroot 内置了数百个包,从 busybox 到 python3,都通过这里的规则被纳入构建。

board/ 存放板级支持文件,例如设备树、内核配置片段、genimage.cfg、启动脚本和 post-build.sh。当某个开发板的启动方式、分区布局或根文件系统覆盖层比较特殊时,相关文件就放在这里,由 configs/ 中的 defconfig 引用。

configs/ 存放默认配置文件(defconfig),文件名通常形如 raspberrypi4_64_defconfig、qemu_aarch64_virt_defconfig。执行 make raspberrypi4_64_defconfig 时,Buildroot 会把这些配置复制为源码树根部的 .config,作为本次构建的输入。

output/ 是构建输出根目录,内部又细分为多个子目录:host/ 存放主机端工具和交叉工具链;build/ 是各软件包的编译工作目录;target/ 是目标根文件系统的暂存目录;staging/ 存放供其他包链接使用的头文件和库;images/ 存放最终生成的镜像、根文件系统压缩包和 SDK;per-package/ 则用于按包隔离构建,减少包之间的隐式依赖。

dl/ 是下载缓存目录,存放从网络获取的源码压缩包和补丁。它的价值在于:同一台机器上多个 Buildroot 工程可以共享 dl/,避免重复下载;在离线环境中,只要 dl/ 完整,也能完成构建。

host/ 位于 output/ 之下,存放为主机编译出来的工具,包括交叉编译器、pkg-config、cmake、autoconf 等。它和 staging/ 的区别在于:host/ 是给构建过程本身用的,staging/ 是给目标程序链接用的。

三、方法流程:一次构建中目录如何协作

  1. 执行 make <board>_defconfig,从 configs/ 复制配置到源码树根部的 .config。
  2. 执行 make,Buildroot 读取 .config,确定需要构建哪些包、使用哪种工具链。
  3. 下载阶段:源码包被放入 dl/,若已存在则直接复用。
  4. 工具链阶段:交叉编译器被安装到 output/host/。
  5. 包编译阶段:各包在 output/build/ 下解压、配置、编译,安装到 output/target/ 和 output/staging/。
  6. 镜像阶段:根文件系统、内核、引导程序被打包到 output/images/,SDK 也在此生成。
# 典型构建命令
make raspberrypi4_64_defconfig
make -j$(nproc)

# 查看输出目录
ls output/
# build  host  images  per-package  staging  target

# 清理构建产物但保留下载缓存
make clean

# 彻底清理(含 dl/ 与 host/)
make distclean

四、案例与场景:目录结构在实战中的价值

假设你正在为一块自定义板卡制作 SDK。板级设备树和分区脚本放在 board/vendor/myboard/,默认配置放在 configs/myboard_defconfig,自定义软件包放在 package/myapp/。构建完成后,交叉工具链在 output/host/bin/,目标根文件系统在 output/target/,最终镜像和 SDK 在 output/images/。把 SDK 交给应用开发同事时,他们只需要 output/images/ 中的工具链压缩包,而不需要整个源码树。

另一个常见场景是复用下载缓存。团队中多人构建同一块板卡时,可以把 dl/ 放到共享目录,通过 BR2_DL_DIR 指向它,从而显著减少网络等待时间。

五、常见误区与复盘

误区一:直接修改 output/target/ 中的文件。这些修改会在下次 make 或 make clean 后丢失。正确做法是通过 board/ 中的 rootfs_overlay 或 post-build.sh 注入。

误区二:把 output/ 纳入版本管理。output/ 体积大且可重建,应加入 .gitignore。真正需要版本管理的是 configs/、board/ 和自定义 package/。

误区三:混淆 host/ 与 staging/。host/ 是构建工具,staging/ 是目标库和头文件。链接目标程序时应使用 staging/ 中的库,而不是 host/。

目录 位置 主要作用 是否可删除
顶层目录 源码树根 Makefile、Kconfig 入口 否
package/ 源码树 软件包构建规则 否
board/ 源码树 板级设备树、脚本、覆盖层 否
configs/ 源码树 默认配置文件 defconfig 否
output/ 构建输出 host、build、target、images 等 是,可重建
dl/ 构建输出 源码包与补丁下载缓存 可删,但建议保留
host/ output/ 下 主机工具与交叉工具链 随 output/ 删除

记忆口诀:源码树管“怎么建”,output 管“建出来”,dl 管“下载过”,host 管“构建工具”,staging 管“目标库”,images 管“最终交付”。分清这六类职责,Buildroot 的目录结构就不会再混乱。