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 源码结构关系图:
从这张图你能看出来,configs/ 是入口,package/ 是软件仓库,board/ 是板子专属配置,arch/ 和 toolchain/ 是底层支撑。它们各司其职,互不干扰。
3.6 迁移时要注意什么
当你从旧版本迁移到新版本时,这几个目录是你重点关注的对象:
configs/:检查你的defconfig是否还兼容。新版本可能新增或废弃了一些配置项。board/:这是你的板子专属资产,通常可以直接复制过去。但要注意内核或 U-Boot 版本变化可能导致的设备树不兼容。package/:如果你有自定义包,检查.mk文件里的 API 是否变了。比如 Buildroot 2023.02 之后,generic-package的某些钩子函数名有调整。
board/ 目录拷过来,结果新版本的内核升级了设备树编译器(DTC),旧的 .dts 文件编译报错。后来我养成了习惯:迁移时先 make clean,然后逐个检查 board/ 下的文件是否还适用。