转载地址:https://www.cnblogs.com/syyxy/p/9350999.html
在Uboot的主Makefile中496行可以看到如下定义:
%config: scripts_basic outputmakefile FORCE
$(MAKE)$(build)=scripts/kconfig $@
在Makefile 415行中可以看到如下定义:
scripts_basic:
$(MAKE) $(build)=scripts/basic
rm -f .tmp_quiet_recordmcount
$(build) 在script/Kbuild.include文件中下定义如下:
则将其展开得到:
Make -f scripts/Makefile.build obj=srcipts/basic
由于没有指定目标,因此__build为默认目标
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:
解析:
$(KBUILD_BUILTIN): 在根目录的makefile中, 将其置为了1,且被export了下来
那么$(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)),
返回的结果就是$(builtin-target) $(lib-target) $(extra-y)。
$(KBUILD_MODULES):在根目录中设置了为0, 且被export了下来.因此:
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target))的返回值就是空。则__build可以写作:
__build:$(builtin-target) $(lib-target) $(extra-y)$(subdir-ym)$(always)
接下来分析依赖文件:
$(builtin-target):在Makefile.build 文件中找到其定义如下:
和:
首先看:
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif
如果没有定义 $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target),则
builtin-target := $(obj)/built-in.o, 否则不定义该变量。
那么, 如何查看有没有定义这几个变量?
在我看来, 需要在3个地方查看变量的定义:
1)本makefile中
2)是否是上层makefile 通过export的方式传递下来
3)有没有通过include 其他文件
首先查看$(obj-y):
在当前makefile.build 中只有一处定义:
但是我们仍然还要看此Makefileinclude了那几个文件,如下:
通过查看各个文件,在Makefile.lib 中发现如下定义:
obj-y := $(patsubst %/, %/built-in.o, $(obj-y))
和
obj-y := $(addprefix $(obj)/,$(obj-y))
通过以上可以看出,上面的使用仍然需要$(obj-y)。因此不是我们的目标(请注意,我们此阶段的目标是找到$(obj-y)的具体定义)。
在 Makefile.build中发现如下片段:
# Do not include host rules unless needed
ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host
endif
接下来就是确定有没有定义$(hostprogs-y)$(hostprogs-m),这两个变量。
通过$(error $(hostprogs-y))打印$(hostprogs-y)为dep
则说明确实定义了$(hostprogs-y),则会include scripts/Makefile.host,这个文件。
查看该文件并没有定义该变量。
由此看来,$(obj-y) 为空。
上述只是告诉了分析方法,其实可以通过打印的方式打印出来,如下:
PHONY += scripts_basic
scripts_basic:
$(Q) echo "obj-y:"$(obj-y)
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
可以看到:
同理,通过添加$(error xxxxx)可以轻松得到变量的值
_build 为scripts/basic/fixdep
接下来的依赖目标是:outputmakefile,我们没有定义$(KBUILD_SRC),因此outputmakefile为空。
接下来依赖的目标是:FORCE, 定义如下:
可以看到FORCE 为伪目标,强制执行。
到这里%config的三个依赖目标全部得到了:
scripts_basic: scripts/basic/fixdep
outputmakefile: 空
FORCE :代表强制执行
接下来就是:
$(MAKE) $(build)=scripts/kconfig $@
如果我们输入make menuconfig,
将其展开,得到:
make -f ./scripts/Makefile.build obj=scripts/kconfig menuconfig
这里做了什么?
首先我们要知道,在./scripts/Makefile.build中,
$(obj)= scripts/kconfig
分析该文件开头代码,
# 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
分析:
src := $(patsubst $(prefix)/%,%,$(obj))
意思是:如果,$(obj)中有单词符合tpl/* ,则将其替换为*(例如将 tpl/test替换为 test)。并将替换结果返回(注意,该结果包括不能替换的单词)。
因此得到src=scripts/kconfig
然后继续往下走:
# 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)
分析:
$(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
意思是:保留$(src)中以/开头的单词,如果为空(),则返回$(srctree)/$(src),否则返回$(src)
$(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
意思是:如果$(kbuild-dir)/Kbuild存在,则返回 $(kbuild-dir)/Kbuild,否则返回 $(kbuild-dir)/makefile.通过查看./scripts/kconfig目录下没有kbuild文件,那么kbuild-file 就是./scripts/kconfig /makefile
得到:
kbuild-dir = ./scripts/kconfig
kbuild-file =./scripts/kconfig/makefile
然后将其include 进来。
到这里其实不用往下看了, 因为我们需要分析的目标menuconfig, 就在./scripts/basic/makefile中。
menuconfig: $(obj)/mconf
$<$(silent)$(Kconfig)
Menuconfig 依赖 ./scripts/basic/mconf 目标
那么mconf是如何编译的呢?
我们在当前Makefile搜索,发现mconf赋给了变量hostprogs-y(205行):
hostprogs-y := conf nconf mconf kxgettext qconf gconf
而我们include 的 scripts/Makefile.host 中,可以看到hostprogs-y赋给了__hostprogs:
__hostprogs := $(sort $(hostprogs-y) $(hostprogs-m))
在scripts/Makefile.host的第42行,可以看到:
# Object (.o) files compiled from .c files
host-cobjs := $(sort $(foreach m,$(__hostprogs),$($(m)-objs)))
64行可以看到:
host-cobjs := $(addprefix $(obj)/,$(host-cobjs))
而在scripts/Makefile.host的118 行,可以看到如下定义:
$(host-cobjs): $(obj)/%.o: $(src)/%.c FORCE
$(call if_changed_dep,host-cobjs)
我们将 $(obj)和$(src)在这个目标的下方使用$(warning $(obj))$(warning $(src))打印出来:
将$(host-cobjs)打印出来:
因此我们这里就将scripts/kconfig下的所有的*.c文件编译成了.o。
$(if_change_dep) 的定义在scripts/Kbuild.include 256行:
# Execute command if command has changed or prerequisite(s) are updated.
#
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
@set -e; \
$(echo-cmd) $(cmd_$(1)); \
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)
我们将@set -e; 去掉@可以看到打印:
因此我们这里就间接解释了HOSTCC scripts/kconfig/mconf.o这样的整齐划一的打印从哪里来的。
目前我们知道了如何将*conf.c 编译为.o 那么如何链接,生成真正的工具的呢?
请看host-cmulti这个变量。
# C executables linked based on several .o files
host-cmulti := $(foreach m,$(__hostprogs),\
$(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m))))
我们来分析一下这个语句:
$(foreach m,$(__hostprogs),\
$(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m))))
首先找到make foreach 的定义:
https://www.cnblogs.com/rohens-hbg/p/6297495.html
从上面的解释中,我们可以将我们的语句翻译为:
遍历$(__hostprogs)中的变量,放到m中,然后执行$(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m)))。将得到的返回值赋给host-cmulti。
$(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m)))翻译为:
如果 $($(m)-cxxobjs)存在,则返回空,否则返回$(if $($(m)-objs),$(m))。
$(if $($(m)-objs),$(m))翻译为:
如果$($(m)-objs)存在则返回$(m)
目前我们已知$(__hostprogs)=conf gconf kxgettext mconf nconf qconf
那么根据上诉逻辑一步一步走,可以得到:
host-cmulti=conf gconf kxgettext mconf nconf
有人会问,为什么少了qconf?
因为在srcipts/kconfig/Makefile第201行,定义如下:
qconf-cxxobjs := qconf.o
因此被消除了。
目前我们得到了:
host-cmulti =conf gconf kxgettext mconf nconf
我们可以在srcipts/Makefile.host中查看host-cmulti的变化过程:
在第62行,被添加了前缀:
host-cmulti := $(addprefix $(obj)/,$(host-cmulti))
得到:
host-cmulti = scripts/kconfig/conf scripts/kconfig/gconf scripts/kconfig/kxgettext scripts/kconfig/mconf scripts/kconfig/nconf
在第107行, host-cmulti被当成目标强制编译,过程为:
$(host-cmulti): FORCE
$(call if_changed,host-cmulti)
也就是说,只要有目标是scripts/kconfig/conf scripts/kconfig/gconf scripts/kconfig/kxgettext scripts/kconfig/mconf scripts/kconfig/nconf
这些,就会走 $(call if_changed,host-cmulti)流程。
目前我们得到了
host-cmulti = scripts/kconfig/conf scripts/kconfig/gconf scripts/kconfig/kxgettext scripts/kconfig/mconf scripts/kconfig/nconf
在scripts/Makefile.host第105行有如下定义:
# Link an executable based on list of .o files, all plain c
# host-cmulti -> executable
quiet_cmd_host-cmulti = HOSTLD $@
cmd_host-cmulti = $(HOSTCC)$(HOSTLDFLAGS) -o $@ \
$(addprefix $(obj)/,$($(@F)-objs)) \
$(HOST_LOADLIBES)$(HOSTLOADLIBES_$(@F))
紧接着下一行有:
$(host-cmulti): FORCE
$(call if_changed,host-cmulti)
$(warning $(host-cmulti))
$(call multi_depend, $(host-cmulti), , -objs)
$(if_changed )的定义在scripts/Makefile.include 356行:
# Execute command if command has changed or prerequisite(s) are updated.
#
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
@set -e; \
$(echo-cmd) $(cmd_$(1)); \
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)
我们将@去掉得到如下打印:
可以看到编译过程cc -o scripts/kconfig/mconf. 至此, mconf 这个文件我们已经得到了。
使用:
scripts/kconfig/mconf Kconfig
就得到了我们的界面:
完成!
接下来还有我们需要做的是如何添加和删除kconfig ?这就是下一篇博客需要做的了。