make menuconfig makefile kconfig詳解
前面有一片文章分析make menuconfig執行過程:http://blog.csdn.net/xinyuan510214/article/details/50964808
今天,將一下make menuconfig makefile kconfig等幾個容易混淆的關鍵操作。
=== 1、核心Makefile概述(linux 2.6)
Linux核心的Makefile分為5個部分:
Makefile 最頂層Makefile
.config 核心當前配置檔案,編譯時成為頂層Makefile的一部分
arch/$(ARCH)/Makefile 和體系結構相關的具體架構的Makefile
scripts/Makefile.*
kbuildMakefiles 核心各級目錄原始碼中大約有500個這樣的檔案,編譯時根據上層Makefile傳下來的巨集定義和其他編譯規則,將原始碼編譯成模組或編入核心。
- 相互作用關係
頂層的Makefile文件讀取核心配置檔案.config文件的內容,確定核心配置情況,通過遞歸向下訪問子目錄的形式完成build核心和模組的工作。.config檔案的內容是在makemenuconfig的時候,通過Kconfig文件配置的結果;arch/$(ARCH)/Makefile則提供補充體系結構相關的資訊;每一個子目錄都有一個KbuildMakefile檔案,用來執行從其上層目錄傳遞下來的命令。KbuildMakefile從.config檔案中提取資訊,生成Kbuild完成核心編譯所需的檔案列表。scripts/Makefile.*文件包含了任何用來根據kbuildMakefile 構建核心所需的定義和規則。
2.6核心的原始碼樹目錄下一般都會有兩個檔案:Kconfig和Makefile。分佈在各目錄下的Kconfig構成了一個分散式的核心配置資料庫,每個Kconfig分別描述了所屬目錄原始檔相關的核心配置選單。在核心配置makemenuconfig(或xconfig等)時,從Kconfig中讀出配置選單,使用者配置完後儲存到.config(在頂層目錄下生成)中。在核心編譯時,主Makefile呼叫這個.config,就知道了使用者對核心的配置情況。
上面的內容說明:Kconfig就是對應著核心的配置選單。假如要想新增新的驅動到核心的原始碼中,可以通過修改Kconfig來增加對我們驅動的配置選單,這樣就有途徑選擇我們的驅動,假如想使這個驅動被編譯,還要修改該驅動所在目錄下的Makefile。(見第3小節舉例)
因此,一般新增新的驅動時需要修改的檔案有兩種(注意不只是兩個)
*Kconfig
*Makefile
要想知道怎麼修改這兩種檔案,就要知道兩種文件的語法結構。
- Kconfig 語法結構
每個選單項都有一個關鍵字標識,最常見的就是config。
語法:
config symbol
options
symbol就是新的選單項,options是在這個新的選單項下的屬性和選項
======================================================
其中options部分有:
1、型別定義:
每個config選單項都要有型別定義,bool:布林型別,tristate三態:內建、模組、移除,string:字串,hex:十六進位制, integer:整型
例如 configHELLO_MODULE
bool"hello testmodule"
bool型別的只能選中或不選中,tristate型別的選單項多了編譯成核心模組的選項,假如選擇編譯成核心模組,則會在.config中生成一個CONFIG_HELLO_MODULE=m的配置,假如選擇內建,就是直接編譯成核心影響,就會在.config中生成一個CONFIG_HELLO_MODULE=y的配置.
2、依賴型定義dependson或requires
指此選單的出現是否依賴於另一個定義
configHELLO_MODULE
bool"hello testmodule"
dependsonARCH_PXA
這個例子表明HELLO_MODULE這個選單項只對XScale處理器有效,即只有在選擇了ARCH_PXA,該選單才可見(可配置)。
3、幫助性定義
只是增加幫助用關鍵字help或---help---
- === 3 、應用舉例
☃最後舉個例子:
假設想把自己寫的一個flash的驅動程式載入到工程中,而且能夠通過menuconfig配置核心時選擇該驅動該怎麼辦呢?可以分三步:
第一:將您寫的flashtest.c 文件新增到/driver/mtd/maps/目錄下。
第二:修改/driver/mtd/maps目錄下的kconfig文件:
configMTD_flashtesttristate“ap71flash"
這樣當make menuconfig時 ,將會出現 ap71 flash選項。
第三:修改該目錄下makefile文件。
新增如下內容:obj-$(CONFIG_MTD_flashtest) +=flashtest.o
這樣,當您執行make menucofnig時,您將發現ap71flash選項,假如您選擇了此項。該選擇就會儲存在.config文件中。當您編譯核心時,將會讀取.config文件,當發現ap71flash 選項為yes 時,系統在呼叫/driver/mtd/maps/下的makefile 時,將會把 flashtest.o加入到核心中。即可達到您的目的。
上面瞭解了其關係及基本的操作,下面再詳細講解一下具體的執行過程。
在編譯核心前,一般是根據已有的配置檔案(一般在核心根目錄下的arch/arm/configs/資料夾下,把該目錄下的xxx_defconfig檔案拷貝到核心根目錄下,並重命名為.config)來進行編譯; 或者需要先配置裁剪核心。
假設我們要基於一塊ARM晶片的開發板配置裁剪核心時,在核心的根目錄下執行:make ARCH=arm menuconfig命令後,會彈出如下配置介面:
當我們在核心的根目錄下執行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的預設行為。
如果你的makefile中的第一個目標是由許多個目標組成,你可以指示make,讓其完成你所指定的目標。要達到這一目的很簡單,需在make命令後直接跟目標的名字就可以完成(如make clean)。任何在makefile中的目標都可以被指定成終極目標,但是 除了以“-”打頭,或是包含了“=”的目標,因為有這些字元的目標,會被解析成命令列引數或是變數。甚至沒有被我們明確寫出來的目標也可以成為make的終極目標,也就是說,只要make可以找到其隱含規則推導規則,那麼這個隱含目標同樣可以被指定成終極目標。
當在Linux核心(核心版本為3.18.42)頂層目錄執行”make ARCH=arm menuconfig”時,命令列對核心根目錄下Makefile檔案的ARCH這個變數賦值為arm ,並且指定了make的目標是menuconfig。“menuconfig”這個目標在根目錄下的Makefile中找到的匹配的目標是“%config”,因此會執行如下的規則:
%config: scripts_basic outputmakefile FORCE (Q)(Q)(MAKE)$(build)=scripts/kconfig [email protected] |
上面的規則等價於:
menuconfig: scripts_basic outputmakefile FORCE (Q)(Q)(MAKE)$(build)=scripts/kconfig menuconfig |
“menuconfig”這個目標有三個依賴:scripts_basic、outputmakefile、FORCE。先來分析下“menuconfig”這個目標下的命令:(Q)(Q)(MAKE)$(build)=scripts/kconfig [email protected]
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)/(RELDIR)/@ # cmd_cc_o_c = (CC)(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(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)(Q)(MAKE) -f scripts/Makefile.build obj= # Usage: # (Q)(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。
[email protected]是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)(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(if(filter /%,(src)),(src)),(src),(srctree)/(srctree)/(src)) kbuild-file := (if(if(wildcard (kbuild−dir)/Kbuild),(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)") (errorCFLAGSwaschangedin"(errorCFLAGSwaschangedin"(kbuild-file)". Fix it to use ccflags-y) endif endif include scripts/Makefile.lib |
在$(srctree)/scripts/Makefile.build這個Makefile檔案中的第一個目標是:
__build: (if(if(KBUILD_BUILTIN),(builtin−target)(builtin−target)(lib-target) $(extra-y)) \ (if(if(KBUILD_MODULES),(obj−m)(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(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 ((filterallallmodules,(filterallallmodules,(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)(builtin−target)(lib-target) (extra−y)(extra−y)(subdir-ym) $(always)。
__build規則展開為:
__build: (builtin−target)(builtin−target)(lib-target) (extra−y)(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)(builtin−target)(lib-target) (extra−y)(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)(Q)(CONFIG_SHELL)$(srctree)/scripts/mkmakefile \ $(srctree)$(objtree)$(VERSION)$(PATCHLEVEL) endif |
由於這裡KBUILD_SRC為空,所以這個指令碼並不會被執行。
到這裡我們分析完了menuconfig的依賴,在處理完這些依賴後就開始執行規則的命令:把(Q)(Q)(MAKE)$(build)=scripts/kconfig [email protected]這條命令展開:
$(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(if(filter /%,(src)),(src)),(src),(srctree)/(srctree)/(src)) kbuild-file := (if(if(wildcard (kbuild−dir)/Kbuild),(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/(filter/(src))就是scripts/kconfig,故kbuild-dir就被賦值為$(src),即kbuild-dir為scripts/kconfig。由於scripts/kconfig目錄下並沒有Kbuild檔案,因此函式(wildcard(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(obj)/mconf(Kconfig), obj的值為scripts/kconfig,因為沒有定義KBUILD_KCONFIG,而且SRCARCH之前已被賦值為$(ARCH),即SRCAR