转载地址:https://www.cnblogs.com/syyxy/p/9350999.html
(写在前面: 本文是本人分析uboot中的makefile文件得到的粗浅的见解,写的越多越感觉其中的深奥复杂。本文是编辑在word中粘贴过来的, 排版可能有有点问题, 或者可以下载 https://files.cnblogs.com/files/syyxy/make_menuconfig%E5%88%B0%E5%BA%95%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F.zip 这个是我的原版,排版会好一些)
在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 中 只有一处定义:
但是我们仍然还要看此Makefile include了那几个文件,如下:
通过查看各个文件, 在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 ,这个文件。
查看该文件并没有定义该变量。
通过 $(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 ? 这就是下一篇博客需要做的了。