1. 程式人生 > >make menuconfig makefile kconfig詳解

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.*

 一些Makefile的通用的規則,面向所有的Kbuild Makefiles。
 
 kbuildMakefiles 核心各級目錄原始碼中大約有500個這樣的檔案,編譯時根據上層Makefile傳下來的巨集定義和其他編譯規則,將原始碼編譯成模組或編入核心。

  • 相互作用關係

 頂層的Makefile文件讀取核心配置檔案.config文件的內容,確定核心配置情況,通過遞歸向下訪問子目錄的形式完成build核心和模組的工作。.config檔案的內容是在makemenuconfig的時候,通過Kconfig文件配置的結果;arch/$(ARCH)/Makefile則提供補充體系結構相關的資訊;每一個子目錄都有一個KbuildMakefile檔案,用來執行從其上層目錄傳遞下來的命令。KbuildMakefile從.config檔案中提取資訊,生成Kbuild完成核心編譯所需的檔案列表。scripts/Makefile.*文件包含了任何用來根據kbuildMakefile 構建核心所需的定義和規則。

=== 2 、核心中Kconfig文件的作用

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命令後,會彈出如下配置介面:

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的預設行為。

如果你的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)時,就會使能quietQ變數的值為空,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

    4、[email protected]

    [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 (kbuilddir)/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),(builtintarget)(builtin−target)(lib-target) $(extra-y)) \

(if(if(KBUILD_MODULES),(objm)(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目標的依賴就是(builtintarget)(builtin−target)(lib-target) (extray)(extra−y)(subdir-ym) $(always)。

__build規則展開為:

__build: (builtintarget)(builtin−target)(lib-target) (extray)(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)(builtintarget)(builtin−target)(lib-target) (extray)(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.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(if(filter /%,(src)),(src)),(src),(srctree)/(srctree)/(src))

kbuild-file := (if(if(wildcard (kbuilddir)/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