学习目标:
uboot顶层Makefile分析。
学习内容:
学习使用了正点原子的I.MX6ULL教程及开发平台。
uboot的make mx6ull_14x14_ddr512_emmc_defconfig配置过程
学习时间:
2022-06-05
学习产出:
1、make xxx_defconfig的过程
在顶层Makefile下有如下代码,
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
no-dot-config-targets := clean clobber mrproper distclean \
help %docs check% coccicheck \
ubootversion backup
$(info #########################1#############################)
$(info no-dot-config-targets=$(no-dot-config-targets))
$(info MAKECMDGOALS=$(MAKECMDGOALS))
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
$(info dot-config=$(dot-config))
$(info KBUILD_EXTMOD=$(KBUILD_EXTMOD))
ifeq ($(KBUILD_EXTMOD),)
ifneq ($(filter config %config,$(MAKECMDGOALS)),)
config-targets := 1
ifneq ($(words $(MAKECMDGOALS)),1)
mixed-targets := 1
endif
endif
endif
$(info config-targets=$(config-targets))
$(info mixed-targets=$(mixed-targets))
ifeq ($(mixed-targets),1)
# ===========================================================================
# We're called with mixed targets (*config and build targets).
# Handle them one by one.
PHONY += $(MAKECMDGOALS) __build_one_by_one
$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
@:
__build_one_by_one:
$(Q)set -e; \
for i in $(MAKECMDGOALS); do \
$(MAKE) -f $(srctree)/Makefile $$i; \
done
else
ifeq ($(config-targets),1)
# ===========================================================================
# *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 $@
else
# ===========================================================================
# Build targets only - this includes vmlinux, arch specific targets, clean
# targets and others. In general all targets except *config targets.
ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf
# Read in dependencies to all Kconfig* files, make sure to run
# oldconfig if changes are detected.
-include include/config/auto.conf.cmd
# To avoid any implicit rule to kick in, define an empty command
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
@# If the following part fails, include/config/auto.conf should be
@# deleted so "make silentoldconfig" will be re-run on the next build.
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
@# include/config.h has been updated after "make silentoldconfig".
@# We need to touch include/config/auto.conf so it gets newer
@# than include/config.h.
@# Otherwise, 'make silentoldconfig' would be invoked twice.
$(Q)touch include/config/auto.conf
-include include/autoconf.mk
-include include/autoconf.mk.dep
# We want to include arch/$(ARCH)/config.mk only when include/config/auto.conf
# is up-to-date. When we switch to a different board configuration, old CONFIG
# macros are still remaining in include/config/auto.conf. Without the following
# gimmick, wrong config.mk would be included leading nasty warnings/errors.
ifneq ($(wildcard $(KCONFIG_CONFIG)),)
ifneq ($(wildcard include/config/auto.conf),)
autoconf_is_old := $(shell find . -path ./$(KCONFIG_CONFIG) -newer \
include/config/auto.conf)
$(info autoconf_is_old=$(autoconf_is_old))
ifeq ($(autoconf_is_old),)
$(info ##########################################2################################################)
include config.mk
include arch/$(ARCH)/Makefile
endif
endif
endif
首先定义了两个变量,
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
version_h 变量是保存uboot的版本号文件,此文件在make时才能自动生成。
timestamp_h 变量是保存ubot的时间戳文件,此文件在make时才能自动生成。
接下来定义了变量no-dot-config-targets := clean clobber mrproper distclean
help %docs check% coccicheck ubootversion backup
定义变量config-targets初值为0;
定义变量mixed-targets初值为0;
定义变量dot-config初值为1。
然后判断MAKECMDGOALS中是否有符合no-dot-config-targets的,此时的MAKECMDFLASG的值为mx6ull_14x14_ddr512_emmc_defconfig,不满足条件判断,dot-config的值仍然为1。
之后判断KBUILD_EXTMOD是否为空,从前面章节的分析中可知,KBUILD_EXTMOD是关于模块编译的,未用模块编译,其值为空,满足判断条件,后面再判断,MAKECMDGOALS中是否有符合config和%config的部分,很明显条件成立,此时config-targets的值变为1,然后继续判断MAKECMDGOALS变量中的单词数是否为1,此处单词数为1,判断条件不成立。mixed-targets的值仍为0。
下面根据config-targets、mixed-targets、dot-config的值进入ifeq…else ifeq判断,这里进入else ifeq ($(config-targets),1)之中。
首先给变量KBUILD_DEFCONFIG赋值为sandbox_defconfig,然后导出变量,KBUILD_DEFCONFIG值为sandbox_defconfig,KBUILD_KCONFIG值为空。
因为我们make的目标为mx6ull_14x14_ddr512_emmc_defconfig,所以不会执行下面的语句:
config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
直接匹配进入下面的语句执行,后面的else ifeq将不再执行。
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
可以看出匹配的目标中有三个依赖分别为scripts_basic 、outputmakefile 和FORCE,一条规则为$(Q)$(MAKE) $(build)=scripts/kconfig $@
。
1.1 FORCE
在顶层Makefile中有如下代码:
PHONY += FORCE
FORCE:
可以看出FORCE是没有依赖和规则的,所以每此都会执行FORCE。当FORCE作为其他目标的依赖时,由于FORCE总是被更新的,因此依赖所在的规则总是会执行的。
1.2 outputmakefile
在顶层Makefile中规则outputmakefile作为目标有如下代码:
PHONY += outputmakefile
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
outputakefile作为目标时没有依赖,因此也总是会被更新的,首先判断变量KBUILD_SRC是否为空,没有设置KBUILD_SRC的值,因此KBUILD_SRC的值空,所以判断条件不成立,依赖outputmakefile直接结束。
1.3 scripts_basic
在顶层makefile下有如下代码,
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
scripts_basic作为目标时,也没有依赖,一次总是被更新的。其中包含两条规则。从前面章节的分析可知,$(Q)
为空。$(MAKE)
就是make。而build在文件
scripts/Kbuild.include中有定义,顶层Makefile中也包含了此文件include scripts/Kbuild.include
。而在scripts/Kbuild.include文件中对build有如下定义:
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj
而srctree为顶层Makefile导出的变量值为.,所以,build := -f ./scripts/Makefile.build obj
,回到scripts_basic的规则中,其规则经过展开为:
scripts_basic:
make -f ./scripts/Makefile.build obj=scripts/basic
rm -f . tmp_quiet_recordmcount
scripts_basic会调用./scripts/Makefile.build文件,且传入的参数值为obj=scripts/basic。
进入./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
首先,定义了变量prefix 的值为tpl,然后定义变量src,这里用到了函数patsubst。此函数的格式如下:
$(patsubst <pattern>,<replacement>,<text>)
此函数用于在text中查找符合patttern的部分,如果匹配的话就用replacement替换掉。pattern是可以包含通配符"%“,如果在replacement中也包含通配符”%“,那么replacement中的这个”%“,将是pattern中的那个”%"所代表的字符串。函数的返回值就是替换后的字符串。因此,$(patsubst $(prefix)/%,%,$(obj))
的意思就是在scripts/basic中查找符合tpl/%
的部分然后替换掉,但是scripts/basic中没有tpl/%,所以src=scripts/basic。
接下来判断obj的值和src的值是否相等,很明显此处条件成立,继续执行。prefix的值变为了spl。继续执行src := $(patsubst $(prefix)/%,%,$(obj))
,src的值仍为src=scripts/basic。
再继续obj和src的值相等,所以,prefix=.。
继续向下有如下代码:
# 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)
将kbuild-dir展开后的结果为:
$(if $(filter /%,scripts/basic),scripts/basic,./scripts/basic)
因为没有以/%开头的单词,所以$(filter /%,scripts/basic)
的值为空,kbuild-dir的值为./scripts/basic,kbuild-dir:=./scripts/basic
。
将kbuild-file展开后为:
$(if $(wildcard ./scripts/basic/Kbuild),./scripts/basic/Kbuild,./scripts/basic/Makefile)
因为./scripts/basic/下没有Kbuild这个文件,所以kbuild-file的值为./scripts/basic/Makefile,kbuild-file:=./scripts/basic/Makefile
。
下面的include $(kbuild-file)
,变为了include ./scripts/basic/Makefile
,也就是读取./scripts/basic下面的Makefile文件。
继续向下会有如下代码:
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:
因为在顶层Makefile执行make -f ./scripts/Makefile.build obj=scripts/basic
时,没有指明目标,所以会使用__build作为默认目标,在顶层Makefile导出的变量中,KBUILD_BUILTIN的值为1,KBUILD_MODULES为空。因此将目标展开后为:
__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
@:
可以看出有五个依赖,$(builtin-target),$(lib-target),$(extra-y)),$(subdir-ym),$(always)
,将这五个依赖的值直接打印出来,如图所示:
__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)
其结果如图所示:
可以看出只有always有值。
由此可见,__build最终变为:
__build: scripts/basic/fixdep
@:
__build的最终依赖为scripts/basic/fixdep。因此就会编译scripts/basic/fixdep.c这个文件,最终生成scripts/basic/fixdep这个软件,用于再构建过程中生成依赖项信息。
1.4、%config的规则
回到顶层Makefile中,%config的三个依赖已经分析完了,下面看规则,内容如下。
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
将规则展开就为:
make -f ./scripts/Makefile.build obj=scripts/kconfig mx6ull_14x14_ddr512_emmc_defconfig
同样进入./scripts/Makefile.build文件中,此时:
src= scripts/kconfig
kbuild-dir = ./scripts/kconfig
kbuild-file = ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile
可以看出,Makefile.build会读取./scripts/kconfig/Makefile中的内容,此Makefile中有如下代码:
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
目标%_defconfig刚好和我们输入的mx6ull_14x14_ddr512_emmc_defconfig相匹配,所以会执行这条规则。其依赖为$(obj)/conf
,展开后就是scripts/kconfig/conf
。下面就是检查并生成依赖 scripts/kconfig/conf。conf 是主机软件这里先到此为止,conf工具从根目录开始树状读取默认的Kconfig文件,分析其配置保存在内存里,分析完默认的Kconfig文件后再读取指定的xxx_defconfig文件,更新得到最终的符号表,并输出到.config文件中。
得到 scripts/kconfig/conf 以后就要执行目标%_defconfig 的命令:
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
相关变量的值如下:
silent=
SRCARCH=…
Kconfig=Kconfig
将其展开就是:
scripts/kconfig/conf --defconfig=arch/../configs/mx6ull_14x14_ddr512_emmc_defconfig Kconfig
这里会将mx6ull_alientek_emmc_defconfig 中的配置输出到.config 文件中,最终生成 uboot 根目录下的.config 文件。