3、源码结构解析:顶层目录结构、package/目录详解、configs/目录作用、board/目录与板级支持

说实话,我第一次打开 Buildroot 源码目录时,也有点懵。一堆文件夹,不知道从哪下手。但用久了你会发现,它的目录结构设计得其实很清晰——每个目录都有明确的分工。这一章,我就带你把这些目录挨个捋一遍。

3.1 顶层目录结构:一眼看穿 Buildroot 的家底

先看顶层。你执行 ls -1 后,会看到类似这样的东西:

arch/        Config.in        docs/         Makefile        package/
board/       Config.in.legacy fs/           Makefile.legacy support/
boot/        configs/         linux/        output/          system/
CHANGES      COPYING          dl/           README           toolchain/

我个人的习惯是,先记住几个核心目录:

目录/文件 一句话说明
arch/ CPU 架构相关的配置,比如 ARM、x86、RISC-V
board/ 板级支持包,放你板子的专属文件
configs/ 默认配置文件,类似 make xxx_defconfig 的入口
package/ 所有软件包的构建脚本,这是核心中的核心
support/ 辅助脚本和工具,比如 kconfig 前端
toolchain/ 工具链相关的配置和辅助文件
output/ 编译产物目录(默认生成,不在源码里)
dl/ 下载的源码包缓存目录

嗯,这里要注意:output/ 和 dl/ 是编译过程中自动生成的,不在 Git 仓库里。如果你要清理环境,直接删 output 就行,别手滑把 package/ 删了。

小技巧: 我习惯在 output/ 里建个软链接指向 dl/,这样多个项目可以共享源码缓存,省带宽也省时间。

3.2 package/ 目录详解:软件包的核心工厂

package/ 是 Buildroot 的灵魂。每个子目录对应一个软件包,比如 package/busybox/、package/openssh/。每个包目录里通常包含:

  • Config.in:Kconfig 菜单配置,决定这个包是否被选中
  • <package>.mk:Makefile 片段,定义下载、编译、安装规则
  • <package>.hash:源码包的哈希校验文件(可选但推荐)
  • S50<package>:SysV init 脚本(可选)

举个例子,看看 package/hello/ 这个演示包:

package/hello/
├── Config.in
├── hello.mk
└── S50hello

hello.mk 的内容大致是这样:

HELLO_VERSION = 1.0
HELLO_SITE = http://example.com/downloads
HELLO_LICENSE = GPL-2.0+

define HELLO_BUILD_CMDS
    $(MAKE) CC="$(TARGET_CC)" -C $(@D)
endef

define HELLO_INSTALL_TARGET_CMDS
    $(INSTALL) -D -m 0755 $(@D)/hello $(TARGET_DIR)/usr/bin/hello
endef

$(eval $(generic-package))

这里有个关键点:$(eval $(generic-package)) 是 Buildroot 的包基础设施。它会自动帮你处理下载、解压、打补丁等通用步骤。你只需要定义 _BUILD_CMDS 和 _INSTALL_TARGET_CMDS 就行。

避坑指南: 我曾经在迁移时忘记更新 .hash 文件,结果 Buildroot 死活不让我编译,提示哈希不匹配。后来养成了习惯:每次升级包版本,第一时间更新 .hash。

包的类型有几种:

类型 适用场景 示例
generic-package 自定义构建流程的包 hello, dropbear
autotools-package 使用 autotools 的项目 libpng, zlib
cmake-package 使用 CMake 的项目 json-c, libwebsockets
meson-package 使用 Meson 的项目 systemd, glib
python-package Python 模块 python-flask, python-numpy

说白了,你只要选对包类型,Buildroot 会帮你搞定 80% 的脏活累活。

3.3 configs/ 目录作用:一键配置的入口

configs/ 目录里放的是各种开发板的默认配置文件。文件名格式通常是 <board_name>_defconfig。比如:

configs/
├── raspberrypi4_64_defconfig
├── beaglebone_defconfig
├── qemu_x86_64_defconfig
└── my_custom_board_defconfig

使用方式很简单:

make raspberrypi4_64_defconfig

这条命令会把 configs/raspberrypi4_64_defconfig 的内容复制到 .config,然后你就可以 make 了。

我建议你养成一个习惯:每次调通一块新板子,第一时间把最终配置保存回去:

make savedefconfig

这会生成一个精简版的 defconfig,只包含你修改过的选项。然后把它复制到 configs/ 目录下,命名成你的板子名。这样下次重编译或者同事要用,直接 make xxx_defconfig 就行。

注意: 不要手动编辑 .config 文件!它是由 Kconfig 自动生成的,格式复杂且容易出错。改配置请用 make menuconfig 或 make nconfig。

3.4 board/ 目录与板级支持:你的板子专属文件夹

board/ 目录是放板级支持文件的地方。每个板子一个子目录,里面可以放:

  • 设备树文件(.dts / .dtsi)
  • U-Boot 补丁或配置
  • Linux 内核补丁
  • 根文件系统覆盖层(rootfs overlay)
  • 生成镜像的脚本(post-image.sh)
  • 生成文件系统的脚本(post-build.sh)

举个例子,树莓派 4 的板级支持目录结构:

board/raspberrypi/
├── rpi4_64/
│   ├── config_4_64.txt      # 固件配置文件
│   ├── cmdline.txt           # 内核启动参数
│   ├── rootfs_overlay/       # 根文件系统覆盖层
│   │   └── etc/
│   │       └── inittab
│   ├── post-image.sh         # 生成镜像后处理脚本
│   └── genimage-raspberrypi4-64.cfg  # 镜像布局配置
└── common/
    ├── firmware/             # 树莓派专有固件
    └── ...

这里有个概念叫 rootfs overlay,说白了就是:编译完成后,把 board/xxx/rootfs_overlay/ 里的文件直接覆盖到根文件系统里。我经常用它来放板子特有的配置文件,比如 /etc/network/interfaces、/etc/fstab 这些。

在 Config.in 或 .config 里,你需要指定:

BR2_ROOTFS_OVERLAY="board/raspberrypi/rpi4_64/rootfs_overlay"

这样 Buildroot 就会在打包文件系统前,自动把覆盖层的内容合并进去。

我的经验: 把板子特有的补丁和配置文件都放在 board/ 下,而不是直接改内核或 U-Boot 源码。这样升级 Buildroot 版本时,你只需要迁移 board/ 目录,省心很多。

3.5 一张图看懂整体结构

说了这么多,不如一张图来得直观。下面是我画的 Buildroot 源码结构关系图:

Buildroot 源码结构关系图 Buildroot 源码结构关系图 Buildroot 顶层 configs/ package/ board/ arch/ toolchain/ Config.in <pkg>.mk <pkg>.hash S50<pkg> 设备树 (.dts) rootfs_overlay/ post-image.sh 核心工作流: 1. 在 configs/ 中选择或创建板级 defconfig 2. 在 board/ 下放置板子专属文件(设备树、覆盖层、脚本) 3. 在 package/ 中添加或配置需要的软件包 4. 执行 make,Buildroot 自动组装出完整固件

从这张图你能看出来,configs/ 是入口,package/ 是软件仓库,board/ 是板子专属配置,arch/ 和 toolchain/ 是底层支撑。它们各司其职,互不干扰。

3.6 迁移时要注意什么

当你从旧版本迁移到新版本时,这几个目录是你重点关注的对象:

  1. configs/:检查你的 defconfig 是否还兼容。新版本可能新增或废弃了一些配置项。
  2. board/:这是你的板子专属资产,通常可以直接复制过去。但要注意内核或 U-Boot 版本变化可能导致的设备树不兼容。
  3. package/:如果你有自定义包,检查 .mk 文件里的 API 是否变了。比如 Buildroot 2023.02 之后,generic-package 的某些钩子函数名有调整。
踩坑记录: 我曾经在迁移时直接把旧版的 board/ 目录拷过来,结果新版本的内核升级了设备树编译器(DTC),旧的 .dts 文件编译报错。后来我养成了习惯:迁移时先 make clean,然后逐个检查 board/ 下的文件是否还适用。