4、编译U-Boot:make命令详解、编译输出文件与多线程加速
好,咱们接着聊U-Boot编译。上一章我们把环境搭好了,配置也选了,现在终于到了最激动人心的环节——敲下make命令,看着屏幕刷刷刷地滚代码。
说实话,我第一次编译U-Boot的时候,心里还挺没底的。那时候刚入行,生怕敲错一个命令把电脑搞崩了。后来发现,其实没那么玄乎。今天我就把make命令的那些门道,还有编译出来的一堆文件都是干嘛的,一次性给你讲透。
4.1 make命令的几种常见用法
先看最基础的。配置完以后,直接敲:
make
这就开始编译了。但实际项目中,我很少这么干。为啥?因为默认是单线程编译,慢得让人抓狂。你想想看,一个完整的U-Boot工程,少说几百个源文件,单线程跑下来,喝杯咖啡回来还没完。
所以,我一般这么用:
make -j4
-j后面跟的数字,是并行编译的线程数。一般设成CPU核心数的1.5到2倍。比如你电脑是4核8线程,设-j8就挺合适。我自己的开发机是8核16线程,我习惯用-j12,既能跑满又不至于把系统搞卡。
nproc 命令,它会直接告诉你逻辑核心数。然后你就用 make -j$(nproc),自动适配。
还有一种情况,你只想编译某个特定的目标,比如只生成设备树:
make dtbs
或者只编译SPL部分:
make spl/u-boot-spl.bin
嗯,这里要注意,U-Boot的Makefile设计得挺巧妙的,它支持增量编译。你改了一个文件,再跑make,它只会重新编译改过的部分和依赖它的文件。我第一次用的时候还担心会不会每次都全量编译,后来发现完全多虑了。
4.2 编译输出文件详解
编译完了,你会在源码目录下看到一堆生成的文件。新手往往一脸懵:这么多文件,哪个才是我要烧录的?
我整理了一个表格,把最重要的几个文件列出来:
| 文件名 | 作用 | 典型大小 |
|---|---|---|
u-boot.bin |
纯二进制镜像,不含任何头部信息,直接烧录到存储介质起始位置 | 200KB~500KB |
u-boot.img |
带U-Boot头部信息的镜像,包含校验和、加载地址等元数据 | 比.bin大几十字节 |
u-boot.srec |
Motorola S-record格式,用于串口下载场景 | 比.bin大2~3倍 |
SPL / u-boot-spl.bin |
第一阶段引导程序,负责初始化DDR并加载完整的U-Boot | 16KB~64KB |
u-boot.dtb |
编译生成的设备树二进制文件 | 几KB到几十KB |
这里我重点说一下SPL。SPL是"Secondary Program Loader"的缩写,说白了就是"小号的U-Boot"。为什么需要它?因为有些芯片的片上SRAM太小了,装不下完整的U-Boot。所以先跑一个极简的SPL,把DDR初始化好,再把完整的U-Boot从Flash里搬到DDR里运行。
我曾经在一个项目里踩过坑:SPL编译出来只有32KB,但芯片的SRAM只有28KB可用。烧进去直接跑飞,查了两天才发现是SPL里开了太多调试打印,把代码段撑爆了。从那以后,我每次编译完都会先看一眼SPL的大小:
ls -lh spl/u-boot-spl.bin
如果超过芯片SRAM的限制,就得想办法裁剪了。
4.3 多线程编译加速的注意事项
多线程编译确实快,但也不是没坑。我遇到过几次编译报错,排查下来都是因为-j设得太高,导致依赖关系没处理好。特别是当你改了头文件的时候,多线程编译可能会漏掉某些依赖文件的重新编译。
我的建议是:
- 日常开发用
-j4或-j8,稳定第一 - 最终发布前做一次
make clean && make -j1,确保没有增量编译带来的隐患 - 如果编译中途报错,先别急着加
-j,用单线程跑一遍看看完整错误信息
-j编译。网络延迟会导致文件锁冲突,编译出来的镜像可能损坏。我吃过这个亏,烧了三块板子才发现问题。
还有一个容易被忽略的点:编译U-Boot时,make命令会检查环境变量CROSS_COMPILE。如果你忘了设,它会用本机的gcc来编译,生成的是x86架构的代码,烧到ARM板子上肯定跑不起来。所以每次编译前,我习惯先确认一下:
echo $CROSS_COMPILE
# 输出应该是类似 arm-linux-gnueabihf- 这样的前缀
4.4 知识体系梳理
说了这么多,我画了一张图,把U-Boot编译的核心流程和输出文件的关系串起来,方便你理解:
从这张图可以看得很清楚:配置、编译、输出,三步走。SPL和设备树是并行编译的,互不干扰。最终你拿到手的,就是u-boot.bin和u-boot-spl.bin这两个核心文件。
4.5 实战中的几个小建议
最后,分享几个我实际项目里总结出来的经验:
- 编译前先
make clean,特别是切换了配置之后。不清干净的话,旧的目标文件可能会和新配置冲突,报一些莫名其妙的错。 - 保留编译日志。我习惯用
make -j8 2>&1 | tee build.log,把标准输出和错误都存下来。出了问题翻日志,比重新编译一遍快多了。 - 检查编译器的版本。U-Boot对GCC版本有要求,太老的编译器可能不支持某些语法。我一般用Linaro提供的交叉编译器,比较稳定。
- 如果编译报错说某个头文件找不到,先别急着改代码。八成是配置里没打开对应的宏,去
menuconfig里搜一下就能找到。
好了,关于U-Boot编译这块,核心内容就这些。make命令其实还有很多高级用法,比如make V=1可以打印详细的编译命令,make O=build可以把输出文件放到单独目录。这些你用到的时候自然就记住了。
可可精品资料库