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),$(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 ? 這就是下一篇博客需要做的了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM