3、基础配置:make menuconfig 入门
Buildroot 的配置,说白了就是通过一个菜单界面,告诉系统你要给谁编译、用什么工具链、最终生成什么。这个菜单界面就是 make menuconfig。我第一次用的时候也觉得不就是个配置界面嘛,但后来发现,这里面的门道,直接决定了你根文件系统能不能跑起来。
核心思路:配置的本质是定义「目标平台 + 构建策略 + 工具链来源」。这三者定下来,剩下的就是选包。
3.1 make menuconfig 操作与结构
在 Buildroot 根目录下执行:
$ make menuconfig
你会看到一个基于 kconfig 的蓝白界面。上下键移动,回车进入子菜单,空格或 Y 键选中,N 键排除,M 键编译为模块(如果支持)。
我个人习惯先按 / 键搜索某个配置项,比如搜 busybox,能直接定位到它在哪个菜单下。这个功能在包多的时候特别有用。
小技巧:按 Z 键可以显示所有隐藏选项(那些被依赖关系隐藏的)。调试时经常用到。
整个配置界面分为几个顶层菜单:
- Target options — 目标架构、CPU、浮点等
- Build options — 编译目录、下载缓存、并行数等
- Toolchain — 工具链类型、C库、内核头文件等
- System configuration — 主机名、密码、初始化系统等
- Kernel — Linux 内核配置
- Target packages — 你要装进根文件系统的所有软件包
- Filesystem images — 最终输出什么格式(ext4、squashfs、tar 等)
嗯,这里要注意:Target options 和 Toolchain 是基础中的基础。这两项配错了,后面全白搭。
3.2 Target options 配置
这个菜单决定了你的根文件系统跑在什么硬件上。我见过有人拿 ARM 配置编译出来的镜像烧到 x86 板子上,折腾了两天才发现是架构选错了。
| 配置项 | 说明 | 常见值 |
|---|---|---|
| Target Architecture | 目标 CPU 架构 | ARM (little endian)、ARM64、x86_64、RISC-V 64 |
| Target Architecture Variant | CPU 具体型号或内核变体 | cortex-a7、cortex-a53、nehalem、rocket |
| Target ABI | 应用程序二进制接口 | EABI、EABIhf(硬浮点) |
| Floating point strategy | 浮点运算策略 | VFPv4-D16、NEON、软件浮点 |
| Target Binary Format | 可执行文件格式 | ELF(几乎都是这个) |
举个例子,如果你用的是全志 H3 芯片(Cortex-A7),配置应该是:
Target Architecture: ARM (little endian)
Target Architecture Variant: cortex-a7
Target ABI: EABIhf
Floating point strategy: VFPv4-D16
我曾经在一个项目里,板子是 Cortex-A7,但默认选了 generic ARM,结果编译出来的系统性能差得离谱。后来发现是编译器没生成针对 cortex-a7 的指令调度。你想想看,这种坑多冤枉。
注意:浮点策略选错了,轻则性能下降,重则程序跑飞。硬浮点(EABIhf)和软浮点(EABI)的二进制不兼容。如果你的板子有硬件 FPU,一定要选硬浮点。
3.3 Build options 配置
这个菜单控制的是「怎么编译」,而不是「编译什么」。我刚开始学的时候经常忽略这里,直到磁盘被占满才回头来看。
| 配置项 | 说明 | 建议值 |
|---|---|---|
| Build output directory | 编译输出目录 | 默认 output/,可改为 build/ 或绝对路径 |
| Download directory | 源码包缓存目录 | 建议统一为 ~/buildroot_dl,多个项目共享 |
| Number of jobs to run simultaneously | 并行编译线程数 | CPU 核心数 + 1(比如 4 核设 5) |
| Enable compiler cache | 启用 ccache 加速 | 强烈建议开启 |
| Enable package remaking | 允许重新编译单个包 | 建议开启,调试时有用 |
我个人习惯把 Download directory 设成一个共享目录。这样我同时维护三四个项目时,不用每个项目都下载一遍 Linux 内核源码包,省了几十 G 的磁盘空间。
还有 Enable compiler cache,说白了就是 ccache。第一次编译慢,第二次改了一行代码再编译,只重新编译改动的文件,其他用缓存。我试过,全量编译从 40 分钟降到 5 分钟。
建议:把 Number of jobs 设成 CPU 核心数 + 1。设太大反而会因为上下文切换变慢。我一般 8 核机器设 9。
3.4 Toolchain 配置
工具链是编译器的集合。Buildroot 支持四种方式:
- Buildroot 内部工具链 — 自己编译 gcc、binutils、glibc。灵活但慢。
- 外部工具链(预编译) — 下载现成的,比如 Linaro、ARM 官方、Bootlin。快,推荐。
- 系统工具链(本地) — 用你 PC 上的 gcc。只能编译同架构,不常用。
- 自定义工具链 — 自己用 crosstool-NG 生成的。高级玩法。
我大部分项目都用 外部预编译工具链。为什么呢?省时间。Buildroot 自己编译一个完整的 gcc 工具链,在性能一般的机器上要半小时以上。而下载 Linaro 的 ARM 工具链,解压即用。
| 配置项 | 说明 | 推荐值 |
|---|---|---|
| Toolchain type | 工具链来源 | External toolchain |
| Toolchain | 具体工具链名称 | Linaro ARM 2021.07 或 Bootlin 2022.02 |
| Kernel Headers | 内核头文件版本 | 与你目标内核版本匹配或略低 |
| C library | C 运行库 | glibc(通用)、musl(嵌入式)、uclibc(极小) |
关键:Kernel Headers 版本必须 ≤ 你实际运行的内核版本。比如你的板子跑 Linux 5.10,内核头文件可以选 5.10 或 5.4,但不能选 6.1。否则编译出来的程序调用新内核才有的系统调用,在老内核上会报错。
关于 C 库的选择,我个人的经验是:
- glibc — 功能最全,兼容性最好。适合存储大、性能要求高的场景。
- musl — 体积小、安全。我最近几个 IoT 项目都用它。静态链接时特别香。
- uclibc — 极简,但维护不太活跃了。除非你 flash 只有 4MB,否则不建议。
我曾经在一个 32MB flash 的板子上,用 glibc 编译出来的 busybox 占 1.2MB,换成 musl 后只有 400KB。你想想看,省下来的空间能多放一个 Web 服务器。
3.5 配置流程与依赖关系
下面这张图展示了配置的先后顺序和依赖关系。我每次新建项目都按这个流程走一遍,基本不会漏。
这张图里,Target options 是起点。架构定了,工具链才能选。工具链定了,C 库和 ABI 就定了。然后才是系统配置和选包。我见过有人先选了一堆包,回头发现架构不对,全部重来。嗯,按这个顺序走,省心。
3.6 保存与验证配置
配置完成后,按 F6 保存,默认写入 .config 文件。你也可以用 make savedefconfig 生成一个精简版的 defconfig,只包含非默认值的选项。这个文件适合放到版本控制里。
$ make savedefconfig
$ cat defconfig
BR2_arm=y
BR2_cortex_a7=y
BR2_ARM_EABIHF=y
BR2_TOOLCHAIN_EXTERNAL=y
BR2_TOOLCHAIN_EXTERNAL_LINARO_ARM=y
我个人习惯把 defconfig 提交到 Git,每次编译前执行 make defconfig 恢复配置。这样团队协作时,每个人拿到的配置完全一致。
验证方法:执行 make show-targets 可以列出所有将要编译的包。如果列表里出现了你不认识的包,说明某个依赖被自动选中了。这时候回头检查一下配置。