Make ARCH=arm CROSS_COMPILE=arm-linux-gcc menuconfig 分析


在編譯LINUX內核時,首先要修改內核源碼頂層目錄下的makefile文件,將其中ARCH ?= $(SUBARCH)修改為ARCH ?= arm,將CROSS_COMPILE    ?= 修改為CROSS_COMPILE    ?= arm-linux-gcc,或者不修改,而是將ARCH和CROSS_COMPILE的值通過命令行傳入。然后在linux內核源碼目錄下,執行make menuconfig,那之后發生了什么?

make命令在未指定文件的情況下,默認尋找名為Makefile或GNUMakefile的文件(文件名不區分大小寫,無后綴名)。make menuconfig命令沒有指定文件,因此默認執行的是 make –f Makefile menuconfig,即執行$(srctree)/Makefile文件中目標menuconfig的相關規則。我使用的源碼頂層目錄名為linux2.6.30.4,因此$(srctree)/Makefile即linux2.6.30.4/Makefile。

下邊只列出了執行make menuconfig后,linux2.6.30.4/Makefile文件中與該命令執行相關的目標規則(此處將makefile文件中include的文件也包含進去並展開了,在include關鍵字下邊,都用{ }括起來,用來表示include進來的文件展開后的內容):

linux2.6.30.4/Makefile


... ...

# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
ifdef SUBDIRS
    KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifdef M
    ifeq ("$(origin M)", "command line")
        KBUILD_EXTMOD := $(M)
    endif
endif

ifeq ($(KBUILD_SRC),)

ifdef O
    ifeq ("$(origin O)", "command line")
        KBUILD_OUTPUT := $(O)
    endif
endif

… …

ifneq ($(KBUILD_OUTPUT),)

… …

sub-make: FORCE
    $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \
    KBUILD_SRC=$(CURDIR) \
    KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \
    $(filter-out _all sub-make,$(MAKECMDGOALS))

… …

skip-makefile := 1

endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)

NOTE:  因為make menuconfig命令沒有定義M、O、也沒有定義skip-makefile,且沒有SUBDIRS、KBUILD_EXTMOD、KBUILD_SRC這三個環境變量(除非自己去設置,否則linux系統不會有這三個個環境變量),因此KBUILD_OUTPUT為空,skip-makefile也為空,KBUILD_EXTMOD為空,KBUILD_SRC為空。

ifeq ($(skip-makefile),)

… …

srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))

NOTE:  KBUILD_SRC為空,所以if函數的返回值是$(CURDIR),CURDIR是make的內嵌變量,其值是make命令執行所在目錄,對於我這個例子,是進入到D:\linux2.6.30.4后make menuconfig的,因此CURDIR就是D:\linux2.6.30.4

… …

SRCARCH     := $(ARCH)

… …

include $(srctree)/scripts/Kbuild.include

{

… …

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

NOTE:KBUILD_SRC為空,所以$(srctree)/的值不被返回,即上句實際為build := -f scripts/Makefile.build obj,

… …

}

… …

PHONY += scripts_basic
scripts_basic:
    $(Q)$(MAKE) $(build)=scripts/basic

… …

PHONY += outputmakefile
outputmakefile:
ifneq ($(KBUILD_SRC),)
    $(Q)ln -fsn $(srctree) source
    $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
        $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

… …

config-targets := 0
mixed-targets  := 0

... ...

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
                        mixed-targets := 1
                endif
        endif
endif

NOTE: MAKECMDGOALS變量指的是從make命令行參數中傳遞過來的目標字符串,make menuconfig命令對應的MAKECMDGOALS就是menuconfig。KBUILD_EXTMOD為空,MAKECMDGOALS字串與%config模式符合,故config-targets := 1,而MAKECMDGOALS中除了%config模式外,沒有其他模式的字串了,因此$(filter-out config %config,$(MAKECMDGOALS))為空,故mixed-targets仍然為0


ifeq ($(mixed-targets),1)

… …

else

ifeq ($(config-targets),1)

… …

%config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

else

… …

endif #ifeq ($(config-targets),1)
endif #ifeq ($(mixed-targets),1)

… …

endif    # skip-makefile

… …

PHONY += FORCE
FORCE:

.PHONY: $(PHONY)

NOTE: make menuconfig后,經過一些邏輯判斷(就是前面ifeq ($(config-targets),1)之類的),最終來執行%config目標的規則,%config目標的規則中有兩條命令,第一條創建兩個目錄,第二條執行make命令,Q的值被定義為@或者為空,而@……的意思是不將此行規則在執行時顯示在屏幕上,因此$(Q)無關緊要。MAKE是內嵌變量,其值為make,$@表示當前目標,即menuconfig,故第二條規則實際就是make -f scripts/Makefile.build obj=scripts/kconfig menuconfig,進入到scripts/makefile.build文件中,make的目標是menuconfg。menuconfg目標有三個依賴scripts_basic outputmakefile FORCE 。scripts_basic目標的規則的命令為$(Q)$(MAKE) $(build)=scripts/basic,展開即make -f scripts/Makefile.build obj=scripts/basic。查看Makefile.build文件,可以看到,該文件的默認目標是__build,__build目標有兩條規則,第一條是空規則,第二條規則有命令。按照makefile規則,當一個目標有多條規則時,只能有一條規則有生成目標的命令,多條規則的中的命令和依賴在makefile文件被讀取時合並,因此說,這里__build目標的實質性規則是

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
     $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
     $(subdir-ym) $(always)
    @:

KBUILD_BUILTIN、KBUILD_MODULES在頂層makefile文件中定義,並通過export關鍵字定義,使在makefile遞歸進行時,這兩個變量被傳遞進子makefile。KBUILD_BUILTIN和KBUILD_MODULES在頂層makefile文件中定義賦為1后,就沒有被改變過。所以此處__build目標的依賴就是$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)。命令“:”在bash中表示什么都不干,只是單純的返回true. 經過分析,發現builtin-target、lib-target、extra-y、subdir-ym都為空串,只有always有值,always在scripts/kconfig/Makefile中定義為dochecklxdialog,而dochecklxdialog目標所在規則的注釋寫着# Check that we have the required ncurses stuff installed for lxdialog (menuconfig),也就是說,__build目標的依賴dochecklxdialog是用來檢查生成配置對話框所需的ncurses庫是不是已經安裝在本機了,如果沒有安裝,make過程會報錯退出。因此在make menuconfig前,我們要保證該庫已經被安裝在本地。

outputmakefile 目標所在規則實際上什么也不做。FORCE所在規則為空,也是什么都不做。FORCE被定義為一個偽目標,所以它作為依賴時總是被認為是最新的(比目標新),故有FORCE作為依賴的目標每次make時必然會重新生成,在這里FORCE偽目標的規則命令為空,故FORCE在Kbuild體系中,就是相當於是一個關鍵字,如果我們想要某個目標每次make的時候都一定會被重新生成,就把FORCE寫為該目標的依賴。

linux2.6.30.4/scripts/Makefile.build


src := $(obj)

PHONY := __build
__build:

… …

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

{

… …

ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := arch/$(SRCARCH)/Kconfig
endif

… …

menuconfig: $(obj)/mconf
    $< $(Kconfig)

… …

NOTE: 由make -f scripts/Makefile.build obj=scripts/kconfig menuconfig可知,src值為scripts/kconfig,與/%的字串模式相符,因此$(filter /%,$(src))就是scripts/kconfig,故kbuild-dir就被賦值為$(src),即kbuild-dir為scripts/kconfig。由於scripts/kconfig目錄下並沒有Kbuild文件,因此函數$(wildcard $(kbuild-dir)/Kbuild)查找失敗,返回為空,從而kbuild-file值被賦為$(kbuild-dir)/Makefile,也即scripts/kconfig/Makefile。接着include $(kbuild-file),目標menuconfig就被找到了。menuconfig目標的規則的命令是$< $(Kconfig),展開為$(obj)/mconf $(Kconfig), obj的值為scripts/kconfig,因為沒有定義KBUILD_KCONFIG,而且SRCARCH之前已被賦值為$(ARCH),即SRCARCH為arm,因此Kconfig的值為arch/arm/Kconfig。故menuconfig目標的規則的命令為scripts/kconfig/mconf arch/arm/Kconfig。mconf在這里實際上是scripts/kconfig目錄下的一個可執行文件,此條命令里arch/arm/Kconfig字符串作為命令行參數傳入該可執行文件運行,該可執行文件實際上就是依據arch/arm/Kconfig文件提供的菜單配置,生成配置界面。NOTE: 這里為什么說scripts/kconfig/mconf就是一個可執行文件呢?繼續往下看scripts/kconfig/Makefile中的內容:

lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o
lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o

… …

mconf-objs    := mconf.o zconf.tab.o $(lxdialog)

… …

ifeq ($(MAKECMDGOALS),menuconfig)
    hostprogs-y += mconf
endif

… …

# Check that we have the required ncurses stuff installed for lxdialog (menuconfig)
PHONY += $(obj)/dochecklxdialog
$(addprefix $(obj)/,$(lxdialog)): $(obj)/dochecklxdialog
$(obj)/dochecklxdialog:
    $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES)

always := dochecklxdialog

… …

}
NOTE: 如果在編譯內核的過程中,需要現編譯出一些可執行文件供內核編譯階段使用,就需要借助Kbuild框架的本機程序支持的特性。Kbuild 框架中,專門使用hostprogs-y變量來指示在內核編譯階段需要使用的一些可執行文件,通過hostprogs-y += mconf,就向make程序指明mconf是一個編譯階段需要使用的可執行文件。另外,Kbuild框架使用-objs后綴來指明相應的可執行文件需要通過多個目標文件來鏈接生成,mconf-objs    := mconf.o zconf.tab.o $(lxdialog)就是向make指明,mconf文件是由mconf.o zconf.tab.o lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o鏈接生成的。再有,未明確寫明生成規則時,KBuild框架默認.o文件是由同名.c或.S文件編譯生成的。我們在scripts\kconfig以及scripts\kconfig\lxdialog目錄下可以找到前邊8個.o文件的同名.c文件。


免責聲明!

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



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