Uboot 中make menuconfig 做了什麽?
Make menuconfig 到底做了什麽?
(寫在前面: 本文是本人分析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
$(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) $(
$(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 ? 這就是下一篇博客需要做的了。
Uboot 中make menuconfig 做了什麽?