LV105-xxx-defconfig过程分析
在编译 uboot 之前要使用“make xxx_defconfig”命令来配置 uboot,那么这个配置过程是如何运行的呢?接下来就来了解一下吧。这个我们从这里开始:Makefile
一、版本与时间
1. version_h
先来看一些这个变量,它在 Makefile 中定义如下:
version_h := include/generated/version_autogenerated.h这个变量保存版本号文件,此文件是自动生成的。文件 include/generated/version_autogenerated.h (编译后才会生成)内容如下:
#define PLAIN_VERSION "2019.04-gf32bcf5b"
#define U_BOOT_VERSION "U-Boot " PLAIN_VERSION
#define CC_VERSION_STRING "arm-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 8.3.0"
#define LD_VERSION_STRING "GNU ld (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 2.32.0.20190321"2. timestamp_h
变量 timestamp_h 在 Makefile 中定义如下:
timestamp_h := include/generated/timestamp_autogenerated.h这个变量保存时间戳文件,此文件也是编译自动生成的。文件 include/generated/timestamp_autogenerated.h 内容如下:
#define U_BOOT_DATE "Nov 19 2024"
#define U_BOOT_TIME "21:45:10"
#define U_BOOT_TZ "+0800"
#define U_BOOT_DMI_DATE "11/19/2024"
#define U_BOOT_BUILD_DATE 0x20241119二、三个变量
接下来看几个变量,定义在 Makefile:
no-dot-config-targets := clean clobber mrproper distclean \
help %docs check% coccicheck \
ubootversion backup tests check qcheck
config-targets := 0
mixed-targets := 0
dot-config := 1
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
dot-config := 0
endif
endif
ifeq ($(KBUILD_EXTMOD),)
ifneq ($(filter config %config,$(MAKECMDGOALS)),)
config-targets := 1
ifneq ($(words $(MAKECMDGOALS)),1)
mixed-targets := 1
endif
endif
endif第 1 行定义了变量 no-dot-config-targets。
第 5 行定义了变量 config-targets,初始值为 0。
第 6 行定义了变量 mixed-targets,初始值为 0。
第 7 行定义了变量 dot-config,初始值为 1。
第 9 行将 MAKECMDGOALS 中不符合 no-dot-config-targets 的部分过滤掉,剩下的如果不为空的话条件就成立。MAKECMDGOALS 是 make 的一个环境变量,这个变量会保存我们所指 定的终极目标列表,比如执行“make mx6ull_14x14_evk_defconfig”,那么 MAKECMDGOALS 就为 mx6ull_14x14_evk_defconfig。很明显过滤后为空,所以条件不成立,变量 dot-config 依 旧为 1。
第 15 行判断 KBUILD_EXTMOD 是否为空,如果 KBUILD_EXTMOD 为空的话条件成立, 经过前面的分析,我们知道 KBUILD_EXTMOD 为空,所以条件成立。
第 16 行将 MAKECMDGOALS 中不符合“config”和“%config”的部分过滤掉,如果剩 下的部分不为空条件就成立,MAKECMDGOALS = mx6ull_14x14_evk_defconfig,所以此处条件成立,变量 config-targets = 1。
第 18 行统计 MAKECMDGOALS 中的单词个数,如果不为 1 的话条件成立。此处调用 Makefile 中的 words 函数来统计单词个数,words 函数格式如下:
$(words <text>)MAKECMDGOALS 的单词个数是 1 个,所以条件不成立,mixed-targets 继续为 0。
综上所述,这些变量值如下:
config-targets = 1
mixed-targets = 0
dot-config = 1三、三个变量的使用
上面确定了那三个变量的值,我们继续往下看 Makefile :
ifeq ($(mixed-targets),1)
# (1)......
else
# (2)......
endif如果变量 mixed-targets 为 1 的话条件成立, 这里显然不成立了,所以会走 2)这里,(2)这里在 Makefile 中是:
ifeq ($(config-targets),1)
# (2.1)......
else
# (2.2)......
endif如果变量 config-targets 为 1 的话条件成立,很明显,条件成立,执行这个(2.1)分支。这个(2.1)在 Makefile 中的代码,我们下面接着分析。
四、%config
我们来看一下上面(2.1)在 Makefile 中的代码:
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target
KBUILD_DEFCONFIG := sandbox_defconfig
export KBUILD_DEFCONFIG KBUILD_KCONFIG
config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@第 7 行,没有目标与之匹配,所以不执行。
第 10 行,有目标与之匹配,当输入“make xxx_defconfig”的时候就会匹配到%config 目标,目标“%config”依赖于 scripts_basic、 outputmakefile 和 FORCE。
1. FORCE
FORCE 在顶层 Makefile 中定义如下:
PHONY += FORCE
FORCE:
# Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)可以看出 FORCE 是没有规则和依赖的,所以每次都会重新生成 FORCE。当 FORCE 作为 其他目标的依赖时,由于 FORCE 总是被更新过的,因此依赖所在的规则总是会执行的。
2. scripts_basic
依赖 scripts_basic 在 Makefile 中定义如下:
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
# To avoid any implicit rule to kick in, define an empty command.
scripts/basic/%: scripts_basic ;这是 scripts_basic 的规则,其对应的命令用到了变量 Q、MAKE 和 build,其中:
Q=@或为空
MAKE=make变量 build 是在 scripts/Kbuild.include 文件中有定义,定义如下:
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj经过前面的分析 可知,变量 srctree(Makefile · srctree) 为”.”,所以有:
build=-f ./scripts/Makefile.build objscripts_basic 展开以后如下:
scripts_basic:
@make -f ./scripts/Makefile.build obj=scripts/basic # 也可以没有@,视配置而定
@rm -f . tmp_quiet_recordmcount # 也可以没有@scripts_basic 会调用文件./scripts/Makefile.build,这个我们后面在分析。
3. %config 展开
上面的几个依赖我们都找到了,接下来吧%config 展开:
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@把 build 的值替换展开后是:
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig发现同样也跟文件./scripts/Makefile.build 有关,我们后面再分析此文件。
五、编译信息
我们在 uboot 源码目录下执行以下命令:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_defconfig主要是看第二条命令的打印信息:

可以看到里面有两条 make -f 开头的命令:
- scripts_basic 目标对应的命令
make -f ./scripts/Makefile.build obj=scripts/basic- %config 目标对应的命令
make -f ./scripts/Makefile.build obj=scripts/kconfig mx6ull_14x14_evk_defconfig六、Makefile.build 脚本
接下来我们就来看一下 Makefile.build 脚本里面都有什么。从前边知道“ make xxx_defconfig“配置 uboot 的时候如下两行命令会执行脚本 scripts/Makefile.build:
@make -f ./scripts/Makefile.build obj=scripts/basic
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig1. scripts_basic 目标对应的命令
scripts_basic 目标对应的命令为:
@make -f ./scripts/Makefile.build obj=scripts/basic- src 与 prefix 变量
先看一下 scripts/Makefile.build 最开始的两个变量:
# Modified for U-Boot
prefix := tpl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := spl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := .
endif
endif第 2 行,变量 prefix 值为 tpl。
第 3 行定义了变量 src,这里用到了函数 patsubst,此行代码展开后为:
$(patsubst tpl/%,%, scripts/basic)patsubst 是替换函数,格式如下:
$(patsubst <pattern>,<replacement>,<text>)此函数用于在 text 中查找符合 pattern 的部分,如果匹配的话就用 replacement 替换掉。 pattern 是可以包含通配符“%”,如果 replacement 中也包含通配符“%”,那么 replacement 中的 这个“%”将是 pattern 中的那个“%”所代表的字符串。函数的返回值为替换后的字符串。因 此,第 3 行就是在“scripts/basic”中查找符合“tpl/%”的部分,然后将“tpl/”取消掉,但是 “scripts/basic”没有“tpl/”,所以 src = scripts/basic。
第 4 行判断变量 obj 和 src 是否相等,相等的话条件成立,很明显,此处条件成立。
第 6 行和第 3 行一样,只是这里处理的是“spl”,“scripts/basic”里面也没有“spl/”,所以 src 继续为 scripts/basic。
第 8 行因为变量 obj 和 src 相等,所以 prefix =. 。
- kbuild-dir 与 kbuild-file
我们继续往下看 scripts/Makefile.build,这里有两个变量:
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)我们前面分析过 srctree =.,src = scripts/basic, 将 kbuild-dir 展开后为:
kbuild-dir := $(if $(filter /%, scripts/basic), scripts/basic, ./scripts/basic)因为没有以“/”为开头的单词,所以$(filter /%, scripts/basic)的结果为空,kbuild-dir =./scripts/basic。得到这个变量后,我们将 kbuild-file 展开后为:
kbuild-file := $(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild, ./scripts/basic/Makefile)因为 scrpts/basic 目录中没有 Kbuild 这个文件,所以 kbuild-file = ./scripts/basic/Makefile。最后将第 4 行展开,即:
include ./scripts/basic/Makefile也就是读取 scripts/basic 下面的 Makefile 文件。
- __build
我们继续向下看 scripts/Makefile.build,会看到这个__build:
# We keep a list of all modules in $(MODVERDIR)
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:__build 是默认目标,因为下面这条命令
@make -f ./scripts/Makefile.build obj=scripts/basic没有指定目标,所以会使用到默认目标:__build。在顶层 Makefile 中,KBUILD_BUILTIN 为 1,KBUILD_MODULES 为 0,因此展开后目标__build 为:
__build:$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)
@:可以看出目标__build 有 5 个依赖:builtin-target、lib-target、extra-y、subdir-ym 和 always。 这 5 个依赖的具体内容我们就不通过源码来分析了,直接在 scripts/Makefile.build 中输入以下代码:
# We keep a list of all modules in $(MODVERDIR)
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:
@echo "builtin-target="$(builtin-target)
@echo "lib-target="$(lib-target)
@echo "extra-y="$(extra-y)
@echo "subdir-ym="$(subdir-ym)
@echo "always="$(always)然后我重新执行以下命令:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_defconfig会看到有如下打印信息出现:
builtin-target=
lib-target=
extra-y=
subdir-ym=
always=scripts/basic/fixdep可以看出,只有 always 有效,因此__build 最终为:
__build:scripts/basic/fixdep
@:__build 依赖于 scripts/basic/fixdep,所以要先编译 scripts/basic/fixdep.c,生成 fixdep,前面已经读取了 scripts/basic/Makefile 文件。
总的来说 scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 这个软件。
2. %config 目标对应的命令
%config 目标对应的命令为:
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig按照前面的分析,相关的变量值如下所示:
src= scripts/kconfig
kbuild-dir = ./scripts/kconfig
kbuild-file = ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile可以看出,Makefilke.build 会读取 scripts/kconfig/Makefile 中的内容,此文件中有如下所示内容:
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
# Added for U-Boot (backward compatibility)
%_config: %_defconfig
@:这几行在 scripts/kconfig/Makefile · %_defconfig 附近。目标%_defconfig 刚好和我们输入的 xxx_defconfig 匹配,所以会执行这条规则。依赖为 $(obj)/conf,展开后就是 scripts/kconfig/conf。
接下来就是检查并生成依赖 scripts/kconfig/conf。 conf 是主机软件,到这里我们就停下来,不要纠结 conf 是怎么编译出来的,否则就越陷越深,太绕了,像 conf 这种主机所使用的工具类软件我们一般不关心它是如何编译产生的。如果一定要看是 conf 是怎么生成的,可以输入如下命令重新配置 uboot,在重新配置 uboot 的过程中就会 输出 conf 编译信息。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_defconfig会有下面的打印信息:
# ......
cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_GNU_SOURCE -D_DEFAULT_SOURCE -I/usr/include/ncursesw -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -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/mx6ull_14x14_evk_defconfig Kconfig
#
# configuration written to .config
#这里的 cc -o scripts/kconfig/conf 这行就是在编译生成 conf 软件。得到 scripts/kconfig/conf 以后就要执行目标%_defconfig 的命令:
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)相关的变量值如下:
silent=-s 或为空
SRCARCH=..
Kconfig=Kconfig将其展开就是:
@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig上述命令用到了 xxx_defconfig 文件,比如 mx6ull_14x14_evk_defconfig 。这里会将 mx6ull_14x14_evk_defconfig 中的配置输出到.config 文件中,最终生成 uboot 根目录下的.config 文件。
七、总结一下
