在編譯LINUX內(nèi)核時(shí),首先要修改內(nèi)核源碼頂層目錄下的makefile文件,將其中ARCH ?= $(SUBARCH)修改為ARCH ?= arm,將CROSS_COMPILE ?= 修改為CROSS_COMPILE ?= arm-linux-gcc,或者不修改,而是將ARCH和CROSS_COMPILE的值通過命令行傳入。然后在linux內(nèi)核源碼目錄下,執(zhí)行make menuconfig,那之后發(fā)生了什么?
make命令在未指定文件的情況下,默認(rèn)尋找名為Makefile或GNUMakefile的文件(文件名不區(qū)分大小寫,無后綴名)。make menuconfig命令沒有指定文件,因此默認(rèn)執(zhí)行的是 make –f Makefile menuconfig,即執(zhí)行$(srctree)/Makefile文件中目標(biāo)menuconfig的相關(guān)規(guī)則。我使用的源碼頂層目錄名為linux2.6.30.4,因此$(srctree)/Makefile即linux2.6.30.4/Makefile。
下邊只列出了執(zhí)行make menuconfig后,linux2.6.30.4/Makefile文件中與該命令執(zhí)行相關(guān)的目標(biāo)規(guī)則(此處將makefile文件中include的文件也包含進(jìn)去并展開了,在include關(guān)鍵字下邊,都用{ }括起來,用來表示include進(jìn)來的文件展開后的內(nèi)容):
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: 因?yàn)閙ake menuconfig命令沒有定義M、O、也沒有定義skip-makefile,且沒有SUBDIRS、KBUILD_EXTMOD、KBUILD_SRC這三個(gè)環(huán)境變量(除非自己去設(shè)置,否則linux系統(tǒng)不會(huì)有這三個(gè)個(gè)環(huán)境變量),因此KBUILD_OUTPUT為空,skip-makefile也為空,KBUILD_EXTMOD為空,KBUILD_SRC為空。
ifeq ($(skip-makefile),)
… …
srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
NOTE: KBUILD_SRC為空,所以if函數(shù)的返回值是$(CURDIR),CURDIR是make的內(nèi)嵌變量,其值是make命令執(zhí)行所在目錄,對(duì)于我這個(gè)例子,是進(jìn)入到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)/的值不被返回,即上句實(shí)際為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命令行參數(shù)中傳遞過來的目標(biāo)字符串,make menuconfig命令對(duì)應(yīng)的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后,經(jīng)過一些邏輯判斷(就是前面ifeq ($(config-targets),1)之類的),最終來執(zhí)行%config目標(biāo)的規(guī)則,%config目標(biāo)的規(guī)則中有兩條命令,第一條創(chuàng)建兩個(gè)目錄,第二條執(zhí)行make命令,Q的值被定義為@或者為空,而@……的意思是不將此行規(guī)則在執(zhí)行時(shí)顯示在屏幕上,因此$(Q)無關(guān)緊要。MAKE是內(nèi)嵌變量,其值為make,$@表示當(dāng)前目標(biāo),即menuconfig,故第二條規(guī)則實(shí)際就是make -f scripts/Makefile.build obj=scripts/kconfig menuconfig,進(jìn)入到scripts/makefile.build文件中,make的目標(biāo)是menuconfg。menuconfg目標(biāo)有三個(gè)依賴scripts_basic outputmakefile FORCE 。scripts_basic目標(biāo)的規(guī)則的命令為$(Q)$(MAKE) $(build)=scripts/basic,展開即make -f scripts/Makefile.build obj=scripts/basic。查看Makefile.build文件,可以看到,該文件的默認(rèn)目標(biāo)是__build,__build目標(biāo)有兩條規(guī)則,第一條是空規(guī)則,第二條規(guī)則有命令。按照makefile規(guī)則,當(dāng)一個(gè)目標(biāo)有多條規(guī)則時(shí),只能有一條規(guī)則有生成目標(biāo)的命令,多條規(guī)則的中的命令和依賴在makefile文件被讀取時(shí)合并,因此說,這里__build目標(biāo)的實(shí)質(zhì)性規(guī)則是
__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關(guān)鍵字定義,使在makefile遞歸進(jìn)行時(shí),這兩個(gè)變量被傳遞進(jìn)子makefile。KBUILD_BUILTIN和KBUILD_MODULES在頂層makefile文件中定義賦為1后,就沒有被改變過。所以此處__build目標(biāo)的依賴就是$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)。命令“:”在bash中表示什么都不干,只是單純的返回true. 經(jīng)過分析,發(fā)現(xiàn)builtin-target、lib-target、extra-y、subdir-ym都為空串,只有always有值,always在scripts/kconfig/Makefile中定義為dochecklxdialog,而dochecklxdialog目標(biāo)所在規(guī)則的注釋寫著# Check that we have the required ncurses stuff installed for lxdialog (menuconfig),也就是說,__build目標(biāo)的依賴dochecklxdialog是用來檢查生成配置對(duì)話框所需的ncurses庫是不是已經(jīng)安裝在本機(jī)了,如果沒有安裝,make過程會(huì)報(bào)錯(cuò)退出。因此在make menuconfig前,我們要保證該庫已經(jīng)被安裝在本地。
outputmakefile 目標(biāo)所在規(guī)則實(shí)際上什么也不做。FORCE所在規(guī)則為空,也是什么都不做。FORCE被定義為一個(gè)偽目標(biāo),所以它作為依賴時(shí)總是被認(rèn)為是最新的(比目標(biāo)新),故有FORCE作為依賴的目標(biāo)每次make時(shí)必然會(huì)重新生成,在這里FORCE偽目標(biāo)的規(guī)則命令為空,故FORCE在Kbuild體系中,就是相當(dāng)于是一個(gè)關(guān)鍵字,如果我們想要某個(gè)目標(biāo)每次make的時(shí)候都一定會(huì)被重新生成,就把FORCE寫為該目標(biāo)的依賴。
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文件,因此函數(shù)$(wildcard $(kbuild-dir)/Kbuild)查找失敗,返回為空,從而kbuild-file值被賦為$(kbuild-dir)/Makefile,也即scripts/kconfig/Makefile。接著include $(kbuild-file),目標(biāo)menuconfig就被找到了。menuconfig目標(biāo)的規(guī)則的命令是$< $(Kconfig),展開為$(obj)/mconf $(Kconfig), obj的值為scripts/kconfig,因?yàn)闆]有定義KBUILD_KCONFIG,而且SRCARCH之前已被賦值為$(ARCH),即SRCARCH為arm,因此Kconfig的值為arch/arm/Kconfig。故menuconfig目標(biāo)的規(guī)則的命令為scripts/kconfig/mconf arch/arm/Kconfig。mconf在這里實(shí)際上是scripts/kconfig目錄下的一個(gè)可執(zhí)行文件,此條命令里arch/arm/Kconfig字符串作為命令行參數(shù)傳入該可執(zhí)行文件運(yùn)行,該可執(zhí)行文件實(shí)際上就是依據(jù)arch/arm/Kconfig文件提供的菜單配置,生成配置界面。NOTE: 這里為什么說scripts/kconfig/mconf就是一個(gè)可執(zhí)行文件呢?繼續(xù)往下看scripts/kconfig/Makefile中的內(nèi)容:
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: 如果在編譯內(nèi)核的過程中,需要現(xiàn)編譯出一些可執(zhí)行文件供內(nèi)核編譯階段使用,就需要借助Kbuild框架的本機(jī)程序支持的特性。Kbuild 框架中,專門使用hostprogs-y變量來指示在內(nèi)核編譯階段需要使用的一些可執(zhí)行文件,通過hostprogs-y += mconf,就向make程序指明mconf是一個(gè)編譯階段需要使用的可執(zhí)行文件。另外,Kbuild框架使用-objs后綴來指明相應(yīng)的可執(zhí)行文件需要通過多個(gè)目標(biāo)文件來鏈接生成,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鏈接生成的。再有,未明確寫明生成規(guī)則時(shí),KBuild框架默認(rèn).o文件是由同名.c或.S文件編譯生成的。我們?cè)趕cripts/kconfig以及scripts/kconfig/lxdialog目錄下可以找到前邊8個(gè).o文件的同名.c文件。
新聞熱點(diǎn)
疑難解答
圖片精選