3. 初识Buildroot目录结构:顶层目录解析

说实话,我第一次打开Buildroot源码目录时,也有点懵。一堆文件夹,不知道从哪下手。但别急,我带你捋一遍,你会发现这个结构其实很清晰。

Buildroot的顶层目录,说白了就是一套「生产嵌入式Linux系统的流水线」。每个文件夹都有自己的职责。我习惯把它们分成三类:配置类、源码类、构建类。下面我们逐个看。

3.1 顶层目录一览

先看整体结构。你解压Buildroot后,会看到这些核心目录:

buildroot/
├── board/          # 板级支持文件
├── configs/        # 默认配置文件
├── package/        # 软件包定义
├── dl/             # 下载的源码包
├── output/         # 构建输出目录
├── Config.in       # 顶层配置入口
├── Makefile        # 顶层构建脚本
├── external.mk     # 外部扩展机制
└── ...

嗯,这里要注意:dl/output/是构建过程中自动生成的。你刚下载的Buildroot里可能没有,或者只有空目录。别慌,这是正常的。

3.2 board/ —— 板级支持的家

board/目录存放的是板级相关的文件。比如设备树、分区表、启动脚本、根文件系统覆盖文件等。

我个人习惯把board目录想象成「每个板子的专属文件夹」。举个例子:

board/
├── raspberrypi/
│   ├── config.txt
│   ├── cmdline.txt
│   └── post-build.sh
├── beaglebone/
│   ├── uEnv.txt
│   └── S99custom
└── mycompany/
    └── myboard/
        ├── linux.config
        ├── rootfs_overlay/
        └── post-image.sh

我在项目中遇到过一个问题:多个板子共用同一个board目录,结果设备树文件搞混了。后来我强制要求:一个板子一个子目录,命名规则用「厂商/板型」。这样清晰多了。

我的建议:board目录里最好放三类文件——设备树(dts)、根文件系统覆盖(rootfs_overlay)、后处理脚本(post-build.sh/post-image.sh)。别把源码放进去,那是package的事。

3.3 configs/ —— 一键配置的钥匙

configs/目录里全是defconfig文件。每个文件对应一个板子的完整配置。说白了,这就是Buildroot的「一键配置」入口。

你运行make raspberrypi3_defconfig时,Buildroot就会读取configs/raspberrypi3_defconfig,然后自动配置好内核、工具链、软件包等所有选项。

我建议你养成一个习惯:每次调通一个新板子,第一时间把配置保存到configs目录。命令很简单:

make savedefconfig
cp defconfig configs/myboard_defconfig

这样下次重构建,一行命令就搞定。我曾经因为没保存配置,折腾了三天才找回原来的设置……嗯,血的教训。

3.4 package/ —— 软件包的大本营

package/是Buildroot最核心的目录。每个软件包(比如busybox、openssl、qt5)都在这里有一个子目录。

每个软件包子目录的结构通常是:

package/hello/
├── Config.in          # 配置选项
├── hello.mk           # 构建规则
└── S50hello           # 启动脚本(可选)

你想想看,Buildroot怎么知道怎么编译hello这个程序?就是靠hello.mk里的规则。而Config.in决定了这个包在make menuconfig里怎么显示、有哪些依赖。

我刚开始写package时,总搞混Config.in.mk文件的作用。后来我总结了一句话:Config.in管「要不要」,.mk管「怎么要」。前者是配置界面,后者是构建逻辑。

重要:如果你要添加自己的软件包,千万别直接改Buildroot自带的package。正确的做法是使用external.mk机制,或者放在package/下新建一个目录。我后面会详细讲。

3.5 dl/ —— 源码缓存仓库

dl/目录是Buildroot自动创建的。所有从网上下载的源码包、补丁文件,都会缓存到这里。

为什么要有这个目录?你想想看,每次构建都去网上下载,多慢啊。而且万一网络断了,整个构建就废了。dl/就是用来缓存源码的。第一次下载后,后续构建直接读本地缓存。

我有个小技巧:把dl/目录做成软链接,指向一个共享的下载缓存。比如在公司服务器上放一个共享目录,所有开发人员的Buildroot都指向它。这样大家下载一次,所有人都能用。省带宽,省时间。

注意:dl/目录不要提交到git里。它太大了,而且每次构建都会变化。记得在.gitignore里加上dl/

3.6 output/ —— 构建成果的集散地

output/是Buildroot的构建输出目录。所有编译产物都在这里。默认情况下,它的结构是这样的:

output/
├── build/        # 每个软件包的编译目录
├── host/         # 主机工具(交叉编译器、工具链)
├── images/       # 最终镜像文件(zImage、rootfs.ext4等)
├── staging/      # 目标系统的头文件/库(用于交叉编译)
├── target/       # 目标根文件系统(挂载点)
└── .config       # 当前使用的配置

我个人最常看的是output/images/output/target/。前者是最终产物,后者是调试用的根文件系统。

有一个坑我要提醒你:output/target/里的文件不要直接修改。因为每次make都会重新生成它。如果你想定制根文件系统,应该用board/目录里的rootfs_overlay机制。

3.7 关键文件:Config.in、Makefile、external.mk

这三个文件是Buildroot的「骨架」。理解了它们,你就理解了Buildroot的运作方式。

Config.in —— 配置菜单的入口

顶层Config.inmake menuconfig的入口文件。它通过source指令,把各个子目录的Config.in文件包含进来。

举个例子,顶层Config.in里会有类似这样的内容:

source "package/Config.in"
source "board/Config.in"
source "configs/Config.in"

然后package/Config.in又会包含所有软件包的配置:

source "package/busybox/Config.in"
source "package/openssl/Config.in"
source "package/hello/Config.in"

这样一层层嵌套,就形成了完整的配置菜单树。你想想看,如果没有这个机制,所有配置都写在一个文件里,那得多乱?

Makefile —— 构建的总指挥

顶层Makefile是Buildroot的构建入口。它定义了所有构建目标:allmenuconfiglinuxbusybox等等。

你运行make时,实际上就是在执行这个Makefile。它会依次完成:下载源码 → 配置 → 编译 → 打包 → 生成镜像。

我建议你花点时间读一读这个Makefile的开头部分。不用全看懂,但至少要知道BR2_开头的变量是怎么来的。这些变量贯穿整个构建过程。

external.mk —— 外部扩展的桥梁

external.mk是Buildroot的一个强大特性。它允许你在不修改Buildroot源码的情况下,添加自己的软件包、板子配置、工具链等。

使用方式很简单:创建一个外部目录,里面放你自己的Config.inexternal.mkpackage/等。然后通过BR2_EXTERNAL变量指向它。

make BR2_EXTERNAL=/path/to/my-external/ raspberrypi3_defconfig

我在实际项目中,几乎都是用BR2_EXTERNAL来管理公司内部的私有软件包。这样Buildroot升级时,我的外部目录完全不受影响。你想想看,如果直接改Buildroot源码,升级时得多痛苦?

核心总结:
  • board/:放板子专属文件(设备树、脚本、覆盖文件)
  • configs/:放一键配置的defconfig文件
  • package/:放所有软件包的定义和构建规则
  • dl/:源码缓存,避免重复下载
  • output/:构建产物,包括镜像、工具链、根文件系统
  • Config.in:配置菜单的入口,层层嵌套
  • Makefile:构建总指挥,定义所有目标
  • external.mk:外部扩展机制,不修改源码也能加东西

3.8 知识体系图

下面这张SVG图,帮你理清Buildroot顶层目录之间的关系:

Buildroot 顶层目录结构 Buildroot 根目录 board/ configs/ package/ dl/ output/ external.mk 关键文件:Config.in / Makefile / external.mk 板级支持文件 (设备树、脚本) 一键配置入口 (defconfig文件) 软件包定义 (Config.in + .mk) 源码缓存 构建产物 外部扩展

这张图里,board/configs/package/输入dl/缓存output/输出。而Config.inMakefileexternal.mk则是贯穿整个流程的骨架

好了,这一章的内容就到这里。你只要记住每个目录的职责,后续章节我们会深入每个细节。到时候你会发现,Buildroot的设计其实非常优雅——每个东西都在它该在的位置。


专注资料整理