View on GitHub

富乎 · 地问


avatar
辗转探寻为富乎?《天问》无解向地问!

<<< 返回主页

编译Linux内核时使用较高版本make
导致每次均全量编译的问题

1、前言

标题有点长,但没办法,现象就是这么个现象,少几个字都不足以完整描述问题。

2、背景

最近想转嵌入式驱动,搞了块板子,并在B站看了两个星期的配套教程视频后, 开始转入实战阶段。开头没什么大障碍,解决几个编译报错之后, 内核镜像就编译出来了。唯一令人困惑的是,每次都是全量编译,即使一个文件都没改, make xx_defcongmake menuconfigmake clean之类的更是没执行过。 在网上搜索一番无果之后,本着无谓的时间就算一秒也不能浪费的心态, 以及难以抑制的好奇心,自行动手开启了抽丝剥茧之旅。

3、过程

3.1 首先怀疑Makefile

首先是怀疑开发板商家是不是后来修改了Makefile,或自行添加了某些脚本, 以至于每次都在初始化阶段更新了某些文件。虽然大改Makefile的概率很小, 但确认一下还是好的,于是下了一份纯净版的Linux内核源码, 与开发板内核源码分别解压到两个目录,再用命令把所有Makefile都找出来:

for i in original custom
do
    find $i/ -iname "*Makefile*" -o -iname "*.mk" | sort > $i/mks.txt
done

再对比一下两份Makefile清单有哪些差异:

diff original/mks.txt custom/mks.txt

一方比另一方多出的Makefile只有寥寥几个,可手动打开肉眼观察, 都是简单的添加*.o文件,肯定不会对编译产生影响。

接下来,逐个对比Makefile的差异——当然不可能手动打开肉眼对比啦。 可以用Beyond Compare,设置成只显示差异的模式以及只过滤出Makefile来对比, 不过我还是嫌麻烦,就用了最擅长的命令行来解决:

while read i
do
    printf ">>> ${i}\n";
    diff original/$i custom/$i || printf "\n\n\n";
done < original/mks.txt

果然发现商家版的一个Makefile里面调用了它自家的一个小程序做了一些初始化, 以为找到了问题根源。谁知把这个操作屏蔽之后,没有任何效果,每次依旧全量编译, 我不禁开始思考人生……

3.2 大胆点,怀疑make程序

既然Makefile找不出问题,再结合Sherlock Holmes的一句名言:

When you have eliminated the impossibles,
whatever remains, however improbable,
must be the truth.

虽然尚未穷尽所有的“不可能”,但实操可不必像理论那般严谨,更多依靠的是经验、直觉, 向着概率最大的方向探索。何况,之前看教程视频时注意到里面用的是Ubuntu 16.04, 商家提供的资料链接里则为18.04,而我平时老旧笔记本用的是20.04,台式机用22.04, 之前还觉得这不会造成什么问题,现在看来倒也未必。 因为,不同版本操作系统的软件包管理器提供的make程序版本不一样, 进而推知其解析Makefile目标依赖关系的逻辑也可能不一样, 何况Linux内核Makefile又包含复杂的逻辑,两者交织一起, 导致高低版本分别出现全量编译和增量编译的差异,不是没有可能。综上所述, 怀疑make版本是问题根源的猜想,很合情合理,只剩下验证而已。

3.3 有怀疑就验证,能想更要能做

根据前述猜想,很容易得出解决思路:确定Ubuntu 16.04或18.04的make版本, 再下载其源码进行编译安装,最后利用新make来编译内核即可。但我不想把个人环境搞乱, 何况现在又有docker之类的容器环境,安全又好用,所以不必多想, 直接用docker分别构造了16.04和18.04的容器,使用之前相同的一套操作, 反复多次进行了编译,最终确实验证了是make版本导致了全量或增量编译的问题根源。 以下是多个环境的make版本:

16.04: 4.1-6
18.04: 4.1-9.1build1
20.04: 4.2.1-1.2
22.04: 4.3-4.1build1

注意,以上结果是使用apt list make得到的,若使用make -v只会得到4.x大版本号。

另外,在较低版本Ubuntu编译内核时,会因为32位库兼容的问题, 会报缺失一些组件缺失或XX程序XX库找不到文件或目录的错误,不在本文的讨论范围, 仅给出解决问题的命令以供参考:

sudo apt install lib32ncurses5 lib32z1 lib32stdc++6

4、总结