在編譯內核前,通常是根據已有的配置文件(通常在內核根目錄下的arch/arm/configs/文件夾下,把該目錄下的xxx_defconfig文件拷貝到內核根目錄下,並重命名爲.config)來進行編譯; 或者須要先配置裁剪內核。linux
假設咱們要基於一塊ARM芯片的開發板配置裁剪內核時,在內核的根目錄下運行:make ARCH=arm menuconfig命令後,會彈出以下配置界面:bash
當咱們在內核的根目錄下運行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_basic、outputmakefile、FORCE。先來分析下「menuconfig」這個目標下的命令:$(Q) $(MAKE) $(build)=scripts/kconfig $@
1、$(Q)
看下變量Q在Makefile的定義:
# 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這個變量的值爲1(V=1)時,就會使能quiet、Q變量的值爲空,make在執行Makefile命令時就會向屏幕輸出所執行的命令;當在命令行不傳入V這個變量或者V的值爲0(V=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_basic、outputmakefile、FORCE這三個依賴:
1、FORCE
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僞目標的規則命令爲空,故FORCE在Kbuild體系中,就是至關因而一個關鍵字,若是咱們想要某個目標每次make的時候都必定會被從新生成,就把FORCE寫爲該目標的依賴。
2、scripts_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.build把src (即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_BUILTIN、KBUILD_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_BUILTIN和KBUILD_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命令。bash的help解釋(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-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前,咱們要保證該庫已經被安裝在本地。
3、outputmakefile
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.build中src變量的定義爲:
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-dir爲scripts/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),即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
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文件,該文件保存了所作的內核配置信息。