在之前的博文 Linux 之八 完整嵌入式 Linux 环境介绍及搭建过程详解 中我们说了要一步步搭建整个嵌入式 Linux 运行环境,今天继续介绍 U-Boot 相关的内容。我所使用的硬件平台及整个要搭建的嵌入式 Linux 环境见博文 Linux 之八 完整嵌入式 Linux 环境介绍及搭建过程详解,没有特殊说明都是在以上环境中进行验证的,就不过多说明了。
这篇博文我们仅仅关注 U-Boot 构建过程本身,想要吃透 U-Boot,有太多东西需要学习!最开始我想放到一篇文章中,写着写着内容越来越多,最终超过了 CSDN 编辑器的限制。。。最终决定把内容拆分成多篇文章。你可能需要:
- U-Boot 之一 零基础编译 U-Boot 过程详解 及 编译后的使用说明
- U-Boot 之二 详解使用 eclipse + J-Link 进行编译及在线调试
- U-Boot 之三 U-Boot 源码文件解析及移植过程详解
- U-Boot 之五 详解 U-Boot 及 SPL 的启动流程
Kbuild && Kconfig
Kbuild && Kconfig 隶属于 Linux Kernel Build System。在宏观上,Kbuild && Kconfig 可以统称为 Kbuild,从微观上来说,Kbuild 指的是编译的过程,而 Kconfig 指的在编译之前对内核进行配置的过程(该过程中会编译一些工具来实现配置过程)。关于详细的可以查看 Linux Kernel 的官方文档 The Linux Kernel Documentation 中的 Kernel Build System 章节的介绍。
- 本文约定俗成的称呼:构建 指的 Kbuild + Kconfig;配置 指的 Kconfig;编译 指的 Kbuild。
- https://www.kernel.org/doc/
Kbuild && Kconfig 这套构建系统一个显著的特点就是每一级目录都会有单独的相关文件,然后会被上一级相同的文件引用。这样就保证了每一级目录都是相互独立的。尤其是对于源码的维护者来说,这个是至关重要。每个维护者可以只关心他所负责的代码部分,任何更改只需要变更它自己的 Kbuild && Kconfig 相关文件即可。它本身主要包含以下几类文件:
-
Makefile: Kbuild && Kconfig 这套构建系统本身属于 make 功能的扩展,因此,整个工作过程就是一些列 Makefile 文件的调用。其中入口就是根目录下的 Makefile 文件,Makefile 中会调用各种工具以实现不同的功能。
注意,为了区分不同的功能,在源码中对于 Makefile 的命名有时候会加一个后缀。例如,config.mk
、Makefile.build
、Makefile.clean
等这些都属于 Makefile 文件。
Makefile 文件无法在线调试,对于理解一个复杂的 Makefile 很不友好。一般我们可以使用--debug
参数让 make 打印详细的信息来协助理解。或者是在 Makefile 中添加一些打印信息,常用打印方式有两种:- 使用
$(info, xxxx$(xxx))
、$(warning, xxxx$(xxx))
、$(error, xxxx$(xxx))
,其中,$(xxx)
表示某个变量。这三个命令可以加到 Makefile 的任意地方,注意$(error, xxxx$(xxx))
会终止 Make 过程。 - 使用
@echo "xxxx $xx xxxx"
,其中,$(xxx)
表示某个变量。这个命令只能用在目标后边,且前面是个TAB。这个就是标准 Makefile 语法中的一个命令。
再顺带说一句
make
的工作的一些机制:- 如果给出了参数,则
make
优先去找匹配的规则(匹配规则:完整匹配 > 通配符半匹配 > 完全通配符匹配)去执行;如果没有给出参数,make
会自动找到 Makefile 中第一个目标中没有通配符的规则执行。 - 如果中间遇到 include 其他文件,就会紧接着执行 include 的文件,完成后再继续执行本文件。
-
make
总是从 Makefile 的开头开始解析,并不是找到匹配目标之后仅执行匹配目标的命令。也就是说,在匹配之前,Make 可能已经解析了很多判断条件。 - 对于匹配的规则如果有依赖,优先解析依赖。注意,依赖的匹配也符合 1 中所说的规则。
- 命令前面加了
@
字符,则不显示命令本身而只显示它的结果。命令前面加了-
号,即使这条命令出错,make
也会继续执行后续命令。 - 如果 Makefile 中存在多条同名规则,则
make
程序会尝试将他们合并。但是如果这些同名规则都有命令的话,make
会给出警告,并用后面的命令代替前面的命令。
- 使用
-
Kconfig 文件: 这个文件就是 Kconfig 系统的菜单项,当我们使用命令:
make menuconfig
时,Kconfig 系统读取该文件,根据该文件的内容生成各级菜单。U-Boot 源码根目录下的 Kconfig 就是*的配置菜单,其中会在引入其他目录下的 Kconfig 作为二级菜单,依次类推。 -
.config 文件: 这个文件记录了我们菜单项中的配置的具体值,我们所有对于构建的配置的存放在这个文件中,我们在,Kconfig 系统菜单中的更改,最终都会改写该文件。注意:该文件默认是个隐藏文件,可以使用
ls -al
查看。 -
xxxx_defconfig 文件: xxxx_defconfig 文件就为 Kconfig 系统的菜单项提供一个默认值。不过,Kconfig 系统不会直接读取 xxxx_defconfig 文件,而是需要使用方式是通过
make xxx_deconfig
生成带默认值的.config
。这样,在加载 Kconfig 时,就可以同时加载.config
以提供默认值。
简单来说,xxxx_defconfig 就是为了方便支持更多个性化配置,从而可以尽可能少的改动源代码。 -
Kbuild 文件: 这个是 Kbuild 系统使用的文件,该文件用于定义一些源码使用的需要根据编译环境产生的中间文件。
-
config.mk 文件: 用来处理一些编译过程中的环境变量。Linux Kernel 没有这个文件,U-Boot 需要使用它。
U-Boot 从 v2014.10 版本开始也引入 Kbuild && Kconfig 这套构建系统,相比于原来应该是复杂了不少。但是对于属性 Linux Kernel 的人来说确实是一个好消息!今天我们就来学习一下 U-Boot 中这套系统的具体工作流程。
Kbuild && Kconfig 这套构建系统中定义了很多命令,我们可以使用 make help
来进行查看(就在根目录的 Makefile 文件中),其中经常用到命令如下图所示:
构建过程
在 Kbuild && Kconfig 这套构建系统中,源码中使用的有些文件是要靠 Kbuild && Kconfig 这套系统来生成的,直接在源码中是找不到。这就要求我们必须要了解 Kbuild && Kconfig 是如何工作的,更重要的是要知道 Kbuild && Kconfig 会产生哪些源码使用的文件。
整个构建过程的这个入口就是源码根目录下的 Makefile 文件。下面我们先来整体看一下这个文件的总体结构,并且对其中的规则进行一下说明。其大体可以分为三部分:
整个文件的前半部分就是定义一堆符号,检测工作环境、处理各种参数,基本没有实际与编译源码相关的命令。下面重点介绍一些(注意,由于我在其中添加了一些打印信息,导致行号与原文件有区别):
- 给
HOST_ARCH
赋值。HOST_ARCH
代表我们当前指定的 主机 的类型。当我们没有指定的时候,它就是我们的 PC 架构(例如我的 HOST_ARCH_X86_64),指定 交叉编译器 后(例如,我们编译时CROSS_COMPILE=arm-none-eabi- ARCH=arm make -j4
),他就是我们指定的架构(HOST_ARCH_ARM)。
- 处理 make 参数:
make V=1
、make -s
等。这里导出了quiet
、Q
、KBUILD_VERBOSE
,用以指示编译过程的显示方式。
- 处理编译的当前目录以及编译输出目录。其中,我们可以使用环境变量
KBUILD_OUTPUT
或者 指定make O=xxx
的方式指定输出目录。而编译的源目录KBUILD_SRC
根据注释仅用于在 OBJ 目录时,而且一般不给使用者使用。代码可以简化为以下逻辑:
这里需要注意的就是,如果指定了输出目录,将转到 输出目录中去执行 Makefile,本 Makefile 的执行就结束了。if `KBUILD_SRC` 为空 检查 O=xxx 参数,如果有指定,则赋值给 `KBUILD_OUTPUT` 定义伪目标_all ,且 _all 为空目标,避免后面include其余文件的时候寻找隐含的第一个目标。后面肯定还会定义 _all, 而且肯定不为空 if `KBUILD_OUTPUT` 不为空 `KBUILD_OUTPUT` = 创建并指定的目录 到 `KBUILD_OUTPUT` 继续执行 make (其中需要过滤掉一些内容,防止无限嵌套)。因为 在 `KBUILD_OUTPUT` 中执行的 Makefile 就是当前这个 Makefile skip-makefile 赋值为 1 endif endif if skip-makefile 为空。不空表示上面已经在 `KBUILD_OUTPUT` 中执行过了 执行其他各种操作 endif 整个makefie 结束
skip-makefile
被赋值为 1,此后只有在skip-makefile
不为 1 时才会继续执行。 - 再接下来就是判断
make C=x
参数以及处理是编译整个 U-Boot 还是编译的模块(由环境变量SUBDIRS
或make M=xxx
执行),如果是模块,赋值给KBUILD_EXTMOD
(赋值厚就不空)。
前面有说过,开始定义了一个空的目标 _all,这里再次定义目标 _all。这里的_all
根据KBUILD_EXTMOD
的值依赖于all
或者是modules
,但是命令还是为空。这里我们不关心模块,重点关注all
。all
又依赖.binman_stmp
和inputs
,如下图所示:
关于all
后文会有详细的介绍。 - 继续下来就是检查编译工具链。并做了一些重命名以统一编译时的显示。
- 再继续就是 kconfig 和 kbuild 共用的一些规则
scripts_basic
、outputmakefile
的定义。然后检查要执行的操作是否需要.config
文件,最终出现以下逻辑:
也就是在此之前的内容,无论给出啥命令,都会被 make 解析,然后从这里开始分道扬镳!if 是多个目标(mixed-targets=1) 一个一个处理 else if 配置(config-targets=1),即我们执行的 make xxxconfig 时的处理 config: scripts_basic outputmakefile FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@ %config: scripts_basic outputmakefile FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@ else 这里就是 编译过程以及 非 config(例如 clean) 的处理了 scripts: scripts_basic scripts_dtc include/config/auto.conf $(Q)$(MAKE) $(build)=$(@) if 需要 .confg 检查 .confg 文件 else include/config/auto.conf: ; endif all 的定义(实际编译文件等等) 非 config(例如 clean、check)等的定义 endif Makefile 结束
至此,就开始根据给出的目标的不同开始区分具体操作类型了,后续我们单独章节来介绍!***注意,由于我在 Makefile 中添加了一些打印信息,导致行号与原文件有区别!***下面是我大体整理的一个流程图:
Kconfig
在 Kbuild && Kconfig 这套构建系统中,当我们使用 make xxxconfg
类似的命令时,就会执行 Kconfig 流程。例如,当执行 make menuconfig
时会出现一个配置界面,允许开发者通过类似于 UI 的方式来对内核进行配置,之所为我们可以看到这个类似于 UI 的界面,就是因为 Kconfig 从中产生了多个文件和工具来实现的。
Kconfig 语法可以从 https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html 里来学习。
当我们在 U-Boot 根目录执行 make menuconfig
或者 make xxx_deconfig
时,make
命令便会读取 U-Boot 根目录下的 Makefile 文件,然后解析并匹配 Makefile 文件中的规则 。而 xxxconfig 就会匹配根目录下 Makefile 文件中的如下图示的规则(%
可以匹配任意非空字符串,所以 menuconfig、xxx_deconfig 都匹配):
-
Q 是否显示信息信息,在 Makefile 前面有赋值为
@
或空
- MAKE: 就是指的可执行程序 make
-
$(build): 定义在
./scripts/Kbuild.include
中:181行
那么将以上命令展开之后就是:make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig
或者$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
-
$(srctree): 在 Makefile 前面有赋值为
.
或$(KBUILD_SRC)
-
scripts_basic: 就定义在 U-Boot 根目录的 Makefile 中
那么将以上命令第一句规则展开之后就是:make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
,第二句规则没啥可说的,就是移除个文件。 -
outputmakefile: 就定义在 U-Boot 根目录的 Makefile 中,如下所示:
从规则代码可以看出只有当 KBUILD_SRC 不为空时才有效,而对于 KBUILD_SRC 一般都是空,非空的情况下一般也不使用。因此,这里的依赖 outputmakefile 这里就是空。对于具体见如下说明:
-
FORCE 是没有规则和依赖的,所以每次都会重新生成 FORCE。当 FORCE 作为其他目标的依赖时,由于 FORCE 总是被更新过的,因此依赖所在的规则总是会执行的。
经过上面的分析,最终由两条语句 make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
和 make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig
或者 make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
是我们需要进一步来解析的。接下来就要进入 /scripts/Makefile.build
这个文件了。
Makefile.build
对于 Makefile.build
文件,目前我们只需要关注下图所示的两部分(说明见注释):
接下就是进一步处理 scripts/basic/Makefile
或者 scripts/kconfig/Makefile
了。
make xxx_deconfig
经过上面的分析,当我执行 make xxx_defconfig
时(例如,我这里的 make stm32f769-disco_defconfig
),最终会执行以下两句:make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
和 make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
。
-
make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
展开后是cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -o scripts/basic/fixdep scripts/basic/fixdep.c
最终在scripts/basic/
目录下生成了可执行工具fixdep
。 -
make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
展开后是:
最终在cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c bison -oscripts/kconfig/zconf.tab.c -t -l scripts/kconfig/zconf.y flex -oscripts/kconfig/zconf.lex.c -L scripts/kconfig/zconf.l cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c cc -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o scripts/kconfig/conf --defconfig=arch/../configs/stm32f769-eval_defconfig Kconfig
scripts/kconfig/
目录下生成了可执行工具conf
,紧接着使用刚生成的scripts/kconfig/conf
工具根据我们指定的stm32f769-eval_defconfig
生成.config
文件。
make menuconfig
经过上面的分析,当我执行 make menuconfig
时,最终会执行以下两句:make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
和 make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig
。
-
make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
与make xxx_deconfig
时的语句是一样的。如果在执行make menuconfig
之前没有使用make xxx_deconfig
,那么这句就会和make xxx_deconfig
时一样去执行来生成scripts/basic/fixdep
。 -
make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig
展开后是:
最终生成make -f ./scripts/Makefile.build obj=scripts/basic rm -f .tmp_quiet_recordmcount make -f ./scripts/Makefile.build obj=scripts/kconfig menuconfig set -e; mkdir -p scripts/kconfig/; /bin/bash scripts/kconfig/mconf-cfg.sh < scripts/kconfig/mconf-cfg.sh > scripts/kconfig/.mconf-cfg.tmp; if [ -r scripts/kconfig/.mconf-cfg ] && cmp -s scripts/kconfig/.mconf-cfg scripts/kconfig/.mconf-cfg.tmp; then rm -f scripts/kconfig/.mconf-cfg.tmp; else : ' UPD scripts/kconfig/.mconf-cfg'; mv -f scripts/kconfig/.mconf-cfg.tmp scripts/kconfig/.mconf-cfg; fi cc -Wp,-MD,scripts/kconfig/.mconf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -c -o scripts/kconfig/mconf.o scripts/kconfig/mconf.c cc -Wp,-MD,scripts/kconfig/lxdialog/.checklist.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -c -o scripts/kconfig/lxdialog/checklist.o scripts/kconfig/lxdialog/checklist.c cc -Wp,-MD,scripts/kconfig/lxdialog/.inputbox.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -c -o scripts/kconfig/lxdialog/inputbox.o scripts/kconfig/lxdialog/inputbox.c cc -Wp,-MD,scripts/kconfig/lxdialog/.menubox.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -c -o scripts/kconfig/lxdialog/menubox.o scripts/kconfig/lxdialog/menubox.c cc -Wp,-MD,scripts/kconfig/lxdialog/.textbox.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -c -o scripts/kconfig/lxdialog/textbox.o scripts/kconfig/lxdialog/textbox.c cc -Wp,-MD,scripts/kconfig/lxdialog/.util.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -c -o scripts/kconfig/lxdialog/util.o scripts/kconfig/lxdialog/util.c cc -Wp,-MD,scripts/kconfig/lxdialog/.yesno.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -c -o scripts/kconfig/lxdialog/yesno.o scripts/kconfig/lxdialog/yesno.c cc -o scripts/kconfig/mconf scripts/kconfig/mconf.o scripts/kconfig/zconf.tab.o scripts/kconfig/lxdialog/checklist.o scripts/kconfig/lxdialog/inputbox.o scripts/kconfig/lxdialog/menubox.o scripts/kconfig/lxdialog/textbox.o scripts/kconfig/lxdialog/util.o scripts/kconfig/lxdialog/yesno.o -Wl,-Bsymbolic-functions -lncursesw -ltinfo scripts/kconfig/mconf Kconfig
scripts/kconfig/mconf
,紧接着使用刚生成的scripts/kconfig/mconf
工具读取根目录的 Kconfig 文件,随之出现配置界面。
make xxx_deconfig
之后,Kconfig 系统会在 U-Boot 源码根目录下生成 .config
文件,当我们使用 make menuconfig
修改了相关配置之后,Kconfig 系统最终也是修改根目录下的 .config
文件(注意,该文件默认是个隐藏文件,可使用 ls -al
查看),而 .config
文件就记录了我们当前对于 U-Boot 的配置,后续构建时便会读取该文件。
Kbuild
这里说的 Kbuild 是微观上的 Kbuild,指的是编译的过程。在经过上面 Kconfig 之后,接下来就是真正的编译过程,这个过程采用的就是 Kbuild 系统。Linux 官方文档地址:https://www.kernel.org/doc/html/latest/kbuild/index.html。
编译使用的命令是 CROSS_COMPILE=arm-none-eabi- ARCH=arm make -j8
,当我们使用该命令之后,make
程序就会读取 U-Boot 根目录的 Makefile 文件,然后解析并匹配 Makefile 文件中的规则。由于这里没有指明目标,Make 会自动找到 makefile 中第一个目标中没有通配符的规则执行,这里就是 _all
。
注意,这里的 _all
并不是第一个定义的 _all
。第一个定义的 _all
是个空命令目标,空命令行可以防止 make 在执行时试图为重建这个目标去查找隐含命令(包括了使用隐含规则中的命令和“ .DEFAULT”指定的命令)。后面重新定义的 _all
才是最终生效的 _all
如果 KBUILD_EXTMOD
为空的话 _all
依赖于 all
。我们不编译模块(没有指定 KBUILD_EXTMOD
),所以 KBUILD_EXTMOD
就是空 ,_all
依赖于 all
的。 all
又依赖 .binman_stmp
和 inputs
,如下图所示:
注意,如果使用的命令是 CROSS_COMPILE=arm-none-eabi- ARCH=arm make all -j8
,则直接就会匹配到 all
这条规则,就不会有 _all
啥事了!
.binman_stamp
这个比较简单,就是通过使用 touch
命令更新 .binman_stamp
文件(不存在时会新建)的时间戳保证 binman
总是会被执行。然后检查一些定义,给出提示。
重点就在 inputs
,而 inputs
又是依赖于 $(INPUTS-y)
。通过将 $(INPUTS-y)
展开后就是 checkarmreloc u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check spl/u-boot-spl.bin u-boot.img u-boot.dtb u-boot-dtb.img
再进一步就是展开及处理各个依赖项了,下面我们重点来介绍一下 checkarmreloc
和 u-boot.srec
,其他的大家自行解析。
-
checkarmreloc
将 u-boot 的各项依赖展开就是:u-boot: arch/arm/cpu/armv7m/start.o arch/arm/cpu/built-in.o arch/arm/cpu/armv7m/built-in.o arch/arm/lib/built-in.o arch/arm/mach-stm32/built-in.o board/st/common/built-in.o board/st/stm32f769-eval/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/domain/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/cdns3/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/host/built-in.o drivers/usb/mtu3/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o env/built-in.o fs/built-in.o lib/built-in.o net/built-in.o u-boot.lds FORCE
,其中:-
$(u-boot-init)
:arch/arm/cpu/armv7m/start.o -
$(u-boot-main)
:后面一大串 built-in.o -
$(u-boot-keep-syms-lto)
:空,不包含任何内容
因此,我们需要重点来关注 u-boot.lds 这个依赖:
其中,$(LDSCRIPT)
就是我们使用的链接脚本文件,这里就是./arch/arm/cpu/u-boot.lds
。而 prepare 的内容就比价多了,如下图所示:
中间各种依赖,最终会到include/config/%.conf:
这条规则。 -
-
u-boot.srec
这个比较简单,至于它依赖的 u-boot 在上面已经说了。
其实到这里基本就可以了,解析来就不继续深入了!下面来一张图:
clean、mrproper、distclean
介绍了以上的工作过程之后,我们再介绍一下三个与清理相关的命令:clean
、mrproper
、distclean
。至于支持的其他命令基本都差不多,大家感兴趣可以自己分析即可。这三个命令的清理程度是逐渐增加,后者包含前者,如下图所示:
我们就以 distclean
为例来说明。当我们执行 make distclean
这条命令时,make 读取根目录的 Makefile,然后开始解析,最终匹配到规则 distclean: mrproper
,如下所示:
-
命令部分:第一个 find 命令查找上面指定的文件并删除。其中的 “|” 表示管道,即左边的输出作为右边的输入。rm 命令删除 boards.cfg 和 CHANGELOG。
关于 find 命令:https://www.cnblogs.com/shenqidu/p/10615593.html
关于 xargs 命令:https://www.runoob.com/linux/linux-comm-xargs.html -
依赖
mrproper
:接下来重点看他的依赖mrproper
,由于mrproper
也有依赖,我把它们整理到一个图中,如下所示:
从中可以看出,mrproper
先执行了clean
,然后执行$(mrproper-dirs)
。综合来看,clean
和mrproper
结构基本一致,我们将里面各变量展开,其中: -
先看
clean
:- 依赖
rm-dirs
:展开后就是.tmp_versions
、spl/*
- 依赖
rm-files
:展开后就是include/bmp_logo.h include/bmp_logo_data.h tools/version.h boot* u-boot* MLO* SPL System.map fit-dtb.blob* u-boot-ivt.img.log u-boot-dtb.imx.log SPL.log u-boot.imx.log lpc32xx-* bl31.c bl31.elf bl31_*.bin image.map tispl.bin* idbloader.img flash.bin flash.log defconfig keep-syms-lto.c
- 依赖
$(clean-dirs)
:-
$(clean)
:定义在Kbuild.include
文件中:clean := -f $(srctree)/scripts/Makefile.clean obj
-
cmd
:定义在Kbuild.include
文件中:cmd = @$(echo-cmd) $(cmd_$(1))
,进一步$(echo-cmd)
就在之上:echo-cmd = $(if $($(quiet)cmd_$(1)),echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)
-
- 命令部分:
-
$(call cmd,rmdirs)
: -
$(call cmd,rmfiles)
: -
find xxx | xargs rm -f
:在以上清理的基础上再清理一些符号文件,临时文件、脚本文件等
-
- 依赖
-
mrproper
:- 依赖
rm-dirs
:include/config
、include/generated spl
- 依赖
rm-files
:.config
、.config.old
、include/autoconf.mk
、include/autoconf.mk.dep
、include/config.h
- 依赖
clean
:就是上面的clean
- 依赖
$(mrproper-dirs)
: - 命令部分:
-
$(clean)
:同上 -
cmd
:同上
-
- 依赖
参考
- https://loee.xyz/2021/04/27/uboot-%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90/
- https://www.cnblogs.com/humaoxiao/p/4189411.html
- https://github.com/zhaojh329/U-boot-1/blob/master/%E7%AC%AC9%E7%AB%A0-U-boot%E7%BC%96%E8%AF%91%E4%B9%8B%E4%B8%80%E9%85%8D%E7%BD%AE%E8%BF%87%E7%A8%8B.md
- https://blog.csdn.net/guyongqiangx/article/details/52558087
- https://yunyanan.github.io/exploring_kbuild/
- https://opensource.com/article/18/10/kbuild-and-kconfig
- https://www.cnblogs.com/syyxy/p/9350999.html
- https://blog.csdn.net/q_z_r_s/article/details/80783631
- https://blog.csdn.net/linuxweiyh/article/details/100528718