配置Linux Kernel時make menuconfig執行流程分析

   在編譯內核前,通常是根據已有的配置文件(通常在內核根目錄下的arch/arm/configs/文件夾下,把該目錄下的xxx_defconfig文件拷貝到內核根目錄下,並重命名爲.config)來進行編譯; 或者須要先配置裁剪內核。linux

    假設咱們要基於一塊ARM芯片的開發板配置裁剪內核時,在內核的根目錄下運行:make ARCH=arm menuconfig命令後,會彈出以下配置界面:bash

clip_image002

    當咱們在內核的根目錄下運行make ARM=arm menuconfig這條命令時,內核根目錄下的Makefile是怎樣被執行的呢?框架

    回答這個問題以前,咱們要先了解make這個工具的執行過程。GNU make找尋默認的Makefile規則是在當前目錄下按順序依次找三個文件 —「GNUmakefile」「makefile」和「Makefile」,一旦找到就開始讀取這個文件並執行。make menuconfig命令沒有指定makefile文件,所以默認執行的是 make –f Makefile menuconfig,即執行$(srctree)/Makefile文件中目標menuconfig的相關規則。通常來講,make的最終目標是makefile中的第一個目標,而其它目標通常是由這個目標連帶出來的。這是make的默認行爲。less

若是你的makefile中的第一個目標是由許多個目標組成,你能夠指示make,讓其完成你所指定的目標。要達到這一目的很簡單,需在make命令後直接跟目標的名字就能夠完成(如make clean)。任何在makefile中的目標均可以被指定成終極目標,可是 除了以「-」打頭,或是包含了「=」的目標,由於有這些字符的目標,會被解析成命令行參數或是變量。甚至沒有被咱們明確寫出來的目標也能夠成爲make的終極目標,也就是說,只要make能夠找到其隱含規則推導規則,那麼這個隱含目標一樣能夠被指定成終極目標。ide

    當在Linux內核(內核版本爲3.18.42)頂層目錄執行make  ARCH=arm  menuconfig」時,命令行對內核根目錄下Makefile文件ARCH這個變量賦值爲arm ,而且指定了make的目標是menuconfig「menuconfig」這個目標在根目錄下的Makefile中找到的匹配的目標是「%config」,所以會執行以下的規則:函數

%config: scripts_basic outputmakefile FORCE工具

    $(Q)$(MAKE) $(build)=scripts/kconfig $@ui

上面的規則等價於:this

menuconfig: scripts_basic outputmakefile FORCEspa

    $(Q)$(MAKE) $(build)=scripts/kconfig menuconfig

「menuconfig」這個目標有三個依賴:scripts_basicoutputmakefileFORCE。先來分析下「menuconfig」這個目標下的命令:$(Q) $(MAKE) $(build)=scripts/kconfig $@

    1$(Q)

看下變量QMakefile的定義:

# Beautify output

# ---------------------------------------------------------------------------

#

# Normally, we echo the whole command before executing it. By making

# that echo $($(quiet)$(cmd)), we now have the possibility to set

# $(quiet) to choose other forms of output instead, e.g.

#

#         quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@

#         cmd_cc_o_c       = $(CC) $(c_flags) -c -o $@ $<

#

# If $(quiet) is empty, the whole command will be printed.

# If it is set to "quiet_", only the short version will be printed.

# If it is set to "silent_", nothing will be printed at all, since

# the variable $(silent_cmd_cc_o_c) doesn't exist.

#

# A simple variant is to prefix commands with $(Q) - that's useful

# for commands that shall be hidden in non-verbose mode.

#

#   $(Q)ln $@ :<

#

# If KBUILD_VERBOSE equals 0 then the above command will be hidden.

# If KBUILD_VERBOSE equals 1 then the above command is displayed.

#

# To put more focus on warnings, be less verbose as default

# Use 'make V=1' to see the full commands

 

ifeq ("$(origin V)", "command line")

  KBUILD_VERBOSE = $(V)

endif

ifndef KBUILD_VERBOSE

  KBUILD_VERBOSE = 0

endif

 

ifeq ($(KBUILD_VERBOSE),1)

  quiet =

  Q =

else

  quiet=quiet_

  Q = @

endif

從上面的註釋和Makefile語句能夠看到,當在命令行傳人V這個變量的值爲1V=1)時,就會使能quietQ變量的值爲空,make在執行Makefile命令時就會向屏幕輸出所執行的命令;當在命令行不傳入V這個變量或者V的值爲0V=0)時,就會使能quiet=quiet_Q= @make在執行Makefile命令時就不會向屏幕輸出所執行的命令。

    2$(MAKE)

MAKE是內嵌變量,其值爲make

    3$(build)

    build這個變量是一個通用的變量,它定義在$(srctree)/scripts/Kbuild.include文件中:

###

# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=

# Usage:

# $(Q)$(MAKE) $(build)=dir

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

在內核的根目錄下的Makefile包含了$(srctree)/scripts/Kbuild.include這個文件:

# We need some generic definitions (do not try to remake the file).

$(srctree)/scripts/Kbuild.include: ;

include $(srctree)/scripts/Kbuild.include

分析$(srctree)/scripts/Kbuild.include: ; 這條語句前咱們先了解下make書寫規則。規則的命令部分有兩種書寫方式:

    a、目標、依賴描述和命令放在同一行,目標和依賴描述使用冒號(:)分隔開,在依賴文件列表後使用分號(;)把依賴文件列表和命令分開。

    b、目標和依賴描述放在同一行,目標和依賴描述使用冒號(:)分隔開;命令行在目標、依賴描述的下一行。看成爲獨立的命令行時此行必須以[Tab]字符開始。在Makefile中,在第一個規則以後出現的全部以[Tab]字符開始的行都會被看成命令來處理。

   $(srctree)/scripts/Kbuild.include: ; 這條語句使用的是第一種make書寫規則,這條規則只有目標,沒有依賴和命令。???所以make在執行這條規則的時候怎麼執行???

include $(srctree)/scripts/Kbuild.include這條規則把$(srctree)/scripts/Kbuild.include這個文件包含到了內核根目錄下的Makefile文件中。

    從上面的分析能夠知道build這個變量的值爲-f $(srctree)/scripts/Makefile.build obj

    4$@

    $@make的自動環變量,表示當前目標,即menuconfig

   

下面來分析下scripts_basicoutputmakefileFORCE這三個依賴:

1FORCE

    FORCE的定義爲:

PHONY += FORCE

FORCE:

 

# Declare the contents of the .PHONY variable as phony.  We keep that

# information in a variable so we can use it in if_changed and friends.

.PHONY: $(PHONY)

從上面看到,FORCE 既沒有依賴的規則,其底下也沒有可執行的命令。若是一個規則沒有命令或者依賴,而且它的目標不是一個存在的文件名。在執行此規則時,目標總會被認爲是最新的。就是說:這個規則一旦被執行,make就認爲它的目標已經被更新過。這樣的目標在做爲一個規則的依賴時,由於依賴總被認爲被更新過,所以做爲依賴所在的規則中定義的命令總會被執行。FORCE所在規則爲空,也是什麼都不作。FORCE被定義爲一個僞目標,因此它做爲依賴時老是被認爲是最新的(比目標新),故有FORCE做爲依賴的目標每次make時必然會從新生成,在這裏FORCE僞目標的規則命令爲空,故FORCEKbuild體系中,就是至關因而一個關鍵字,若是咱們想要某個目標每次make的時候都必定會被從新生成,就把FORCE寫爲該目標的依賴。

2scripts_basic

    scripts_basic的定義爲:

# Basic helpers built in scripts/

PHONY += scripts_basic

scripts_basic:

    $(Q)$(MAKE) $(build)=scripts/basic

    $(Q)rm -f .tmp_quiet_recordmcount

scripts_basic這個目標沒有依賴,且scripts_basic也不是一個存在的文件,所以scripts_basic所定義的命令總會被執行。上述scripts_basic的定義等價爲:

# Basic helpers built in scripts/

PHONY += scripts_basic

scripts_basic:

    $(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/basic

    $(Q) rm -f .tmp_quiet_recordmcount

$(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/basic這條命令指定了執行的是$(srctree)/scripts/Makefile.build這個Makefile,傳遞的參數是obj=scripts/basic

接下來咱們來分析下$(srctree)/scripts/Makefile.build這個Makefile文件。

    obj這個變量傳遞進$(srctree)/scripts/Makefile.build中的src這個變量:

src := $(obj)

src := scripts/basic

$(srctree)/scripts/Makefile.buildsrc (scripts/basic)目錄下的Makefile包含進來(若是有Kbuild則包含Kbuild

# 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)

$(srctree)/scripts/Makefile.build也包含了scripts/Makefile.lib這個文件:

# If the save-* variables changed error out

ifeq ($(KBUILD_NOPEDANTIC),)

        ifneq ("$(save-cflags)","$(CFLAGS)")

                $(error CFLAGS was changed in "$(kbuild-file)". Fix it to use ccflags-y)

        endif

endif

 

include scripts/Makefile.lib

$(srctree)/scripts/Makefile.build這個Makefile文件中的第一個目標是:

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \

     $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \

     $(subdir-ym) $(always)

     @:

KBUILD_BUILTINKBUILD_MODULES在頂層Makefile中定義:

# Decide whether to build built-in, modular, or both.

# Normally, just do built-in.

 

KBUILD_MODULES :=

KBUILD_BUILTIN := 1

 

# If we have only "make modules", don't compile built-in objects.

# When we're building modules with modversions, we need to consider

# the built-in objects during the descend as well, in order to

# make sure the checksums are up to date before we record them.

 

ifeq ($(MAKECMDGOALS),modules)

  KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)

endif

 

# If we have "make <whatever> modules", compile modules

# in addition to whatever we do anyway.

# Just "make" or "make all" shall build modules as well

 

ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)

  KBUILD_MODULES := 1

endif

 

ifeq ($(MAKECMDGOALS),)

  KBUILD_MODULES := 1

endif

 

export KBUILD_MODULES KBUILD_BUILTIN

export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD

經過export關鍵字定義,使在makefile遞歸進行時,這兩個變量被傳遞進子makefile

這裏:

KBUILD_MODULES :=

KBUILD_BUILTIN := 1

KBUILD_BUILTINKBUILD_MODULES在頂層makefile文件中定義賦值後,就沒有被改變過。因此此處__build目標的依賴就是$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)

__build規則展開爲:

__build: $(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)

     @:

規則的命令是一個冒號命令」:」,冒號(:)命令是bash的內建命令,一般把它看做true命令。bashhelp解釋(help :)爲:No effect; the command does nothing. A zero exit code is returned.(沒有效果,該命令是空操做,退出狀態老是0)。

__build的依賴除了$(always)$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym)這些變量在$(srctree)/scripts/basic/Makefile中沒有定義,所以builtin-targetlib-targetextra-ysubdir-ym都爲空串,只有always有值。alwaysscripts/kconfig/Makefile中定義爲dochecklxdialog,而dochecklxdialog目標所在規則的註釋寫着# Check that we have the required ncurses stuff installed for lxdialog (menuconfig)。也就是說,__build目標的依賴dochecklxdialog是用來檢查生成配置對話框所需的ncurses庫是否是已經安裝在本機了,若是沒有安裝,make過程會報錯退出。所以在make menuconfig前,咱們要保證該庫已經被安裝在本地。

 

3outputmakefile

    outputmakefile在內核根目錄下的Makefile中的定義爲:

PHONY += outputmakefile

# outputmakefile generates a Makefile in the output directory, if using a

# separate output directory. This allows convenient use of make in the

# output directory.

outputmakefile:

ifneq ($(KBUILD_SRC),)

    $(Q)ln -fsn $(srctree) source

    $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \

    $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)

endif

因爲這裏KBUILD_SRC爲空,因此這個腳本並不會被執行。

    到這裏咱們分析完了menuconfig的依賴,在處理完這些依賴後就開始執行規則的命令:$(Q)$(MAKE) $(build)=scripts/kconfig $@這條命令展開:

$(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig

這條命令指定要執行scripts/Makefile.build這個makefile文件。

    $(srctree)/scripts/Makefile.buildsrc變量的定義爲:

src := $(obj)

 

PHONY := __build

__build:

 

# Init all relevant variables used in kbuild files so

# 1) they have correct type

# 2) they do not inherit any value from the environment

obj-y :=

obj-m :=

lib-y :=

lib-m :=

always :=

targets :=

subdir-y :=

subdir-m :=

EXTRA_AFLAGS   :=

EXTRA_CFLAGS   :=

EXTRA_CPPFLAGS :=

EXTRA_LDFLAGS  :=

asflags-y  :=

ccflags-y  :=

cppflags-y :=

ldflags-y  :=

 

subdir-asflags-y :=

subdir-ccflags-y :=

 

# Read auto.conf if it exists, otherwise ignore

-include include/config/auto.conf

 

include scripts/Kbuild.include

 

# For backward compatibility check that these variables do not change

save-cflags := $(CFLAGS)

 

# 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)

make -f scripts/Makefile.build obj=scripts/kconfig menuconfig可知,src值爲scripts/kconfig,與/%的字串模式相符,所以$(filter /%,$(src))就是scripts/kconfig,故kbuild-dir就被賦值爲$(src),即kbuild-dirscripts/kconfig。因爲scripts/kconfig目錄下並無Kbuild文件,所以函數$(wildcard $(kbuild-dir)/Kbuild)查找失敗,返回爲空,從而kbuild-file值被賦爲$(kbuild-dir)/Makefile,也即scripts/kconfig/Makefile。接着,

scripts/Makefile.build包含scripts/kconfig/Makefile文件(include $(kbuild-file))。目標menuconfig定義在scripts/kconfig/Makefile中,找到menuconfig目標後,而後執行以menuconfig爲目標的規則:

PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config \

    localmodconfig localyesconfig

 

ifdef KBUILD_KCONFIG

Kconfig := $(KBUILD_KCONFIG)

else

Kconfig := Kconfig

endif

 

# We need this, in case the user has it in its environment

unexport CONFIG_

... ...

 menuconfig: $(obj)/mconf

    $< $(Kconfig)

... ...

menuconfig目標的規則的命令是$< $(Kconfig),展開爲$(obj)/mconf $(Kconfig), obj的值爲scripts/kconfig,由於沒有定義KBUILD_KCONFIG,並且SRCARCH以前已被賦值爲$(ARCH),即SRCARCHarm,所以Kconfig的值爲arch/arm/Kconfig。故menuconfig目標的規則的命令爲scripts/kconfig/mconf  arch/arm/Kconfigmconf在這裏其實是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

 

conf-objs   := conf.o  zconf.tab.o

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

nconf-objs     := nconf.o zconf.tab.o nconf.gui.o

kxgettext-objs  := kxgettext.o zconf.tab.o

qconf-cxxobjs   := qconf.o

qconf-objs  := zconf.tab.o

gconf-objs  := gconf.o zconf.tab.o

 

hostprogs-y := conf nconf mconf kxgettext qconf gconf

 

clean-files := qconf.moc .tmp_qtcheck .tmp_gtkcheck

clean-files += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h

clean-files     += config.pot linux.pot

 

# 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) $(HOSTLOADLIBES_mconf)

 

always := dochecklxdialog

若是在編譯內核的過程當中,須要現編譯出一些可執行文件供內核編譯階段使用,就須要藉助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文件。

    保存配置信息後會在內核根目錄下生成一個.config文件,該文件保存了所作的內核配置信息。

相關文章
相關標籤/搜索