實際文檔位置:Documentation/kbuild/makefiles.txt,此爲翻譯稿。
*******************************************************************************
Linux內核的Makefile
=== 目錄
=== 1 概述
=== 2 用戶與做用
=== 3 Kbuild文件
--- 3.1 目標定義
--- 3.2 編譯進內核 - obj-y
--- 3.3 編譯可裝載模塊 - obj-m
--- 3.4 輸出的符號
--- 3.5 目標庫文件 - lib-y
--- 3.6 遞歸躺下訪問目錄
--- 3.7 編輯標誌
--- 3.8 命令行的依賴關係(原文中沒有寫:-))
--- 3.9 跟蹤依賴
--- 3.10 特殊規則
--- 3.11 $(CC) 支持的函數
=== 4 本機程序支持
--- 4.1 簡單的本機程序
--- 4.2 複合的本機程序
--- 4.3 定義共享庫
--- 4.4 使用用C++編寫的本機程序
--- 4.5 控制本機程序的編譯選項
--- 4.6 編譯主機程序時
--- 4.7 使用 hostprogs-$(CONFIG_FOO)
=== 5 Kbuild清理
=== 6 架構Makefile
--- 6.1 調整針對某一具體架構生成的鏡像
--- 6.2 將所需文件加到 archprepare 中
--- 6.3 遞歸下向時要訪問的目錄列表
--- 6.4 具體架構的啓動鏡像
--- 6.5 構造非Kbuild目標
--- 6.6 構建啓動鏡像的命令
--- 6.7 Kbuild自定義命令
--- 6.8 聯接器預處理腳本
=== 7 Kbuild 變量
=== 8 Makefile語言
=== 9 關於做者
=== 10 TODO
=== 1 概述
Linux內核的Makefile分爲5個部分:
Makefile 頂層Makefile
.config 內核配置文件
arch/$(ARCH)/Makefile 具體架構的Makefile
scripts/Makefile.* 通用的規則等。面向全部的Kbuild Makefiles。
kbuild Makefiles 內核源代碼中大約有500個這樣的文件
頂層Makefile閱讀的.config文件,而該文件是由內核配置程序生成的。
頂層Makefile負責製做:vmlinux(內核文件)與模塊(任何模塊文件)。製做的過程主要是
經過遞歸向下訪問子目錄的形式完成。並根據內核配置文件肯定訪問哪些子目錄。頂層
Makefile要原封不動的包含一具體架構的Makefile,其名字相似於 arch/$(ARCH)/
Makefile。該架構Makefile向頂層Makefile提供其架構的特別信息。
每個子目錄都有一個Kbuild Makefile文件,用來執行從其上層目錄傳遞下來的命令。
Kbuild Makefile從.config文件中提取信息,生成Kbuild完成內核編譯所需的文件列表。
scripts/Makefile.*包含了全部的定義、規則等信息。這些文件被用來編譯基於kbuild
Makefile的內核。(**有點不通**)
=== 2 用戶與做用
能夠將人們與內核Makefile的關係分紅4類。
*使用者* 編譯內核的人。他們只是鍵入"make menuconfig"或"make"這樣的命令。通常
狀況下是不會讀或編輯任何內核Makefile(或者任何的源文件)。
*普通開發人員* 這是一羣工做在內核某一功能上的人,好比:驅動開發,文件系統或
網絡協議。他們所須要維護的只是他們所工做的子系統的Kbuild Makefile。爲了提升
工做的效率,他們也須要對內核Makefile有一個全面的認識,而且要熟悉Kbuild的接口
。
*架構開發人員* 這是一些工做在具體架構,好比sparc 或者ia64,上面的人。架構開
發者須要在熟悉kbuild Makefile的同時,也要熟悉他所工做架構的Makefile。
*Kbuild開發者* 維護Kbuild系統的人。他們須要知曉內核Makefile的方方面面。
該文件是爲普通開發人員與架構開發人員所寫。
=== 3 Kbuild文件
大部份內核中的Makefile都是使用Kbuild組織結構的Kbuild Makefile。這章介紹了
Kbuild Makefile的語法。
Kbuild文件傾向於"Makefile"這個名字,"Kbuild"也是能夠用的。但若是"Makefile"
"Kbuild"同時出現的話,使用的將會是"Kbuild"文件。
3.1節 目標定義是一個快速介紹,之後的幾章會提供更詳細的內容以及實例。
--- 3.1 目標定義
目標定義是Kbuild Makefile的主要部分,也是核心部分。主要是定義了要編
譯的文件,全部的選項,以及到哪些子目錄去執行遞歸操做。
最簡單的Kbuild makefile 只包含一行:
例子:
obj-y += foo.o
該例子告訴Kbuild在這目錄裏,有一個名爲foo.o的目標文件。foo.o將從foo.c
或foo.S文件編譯獲得。
若是foo.o要編譯成一模塊,那就要用obj-m了。所採用的形式以下:
例子:
obj-$(CONFIG_FOO) += foo.o
$(CONFIG_FOO)能夠爲y(編譯進內核) 或m(編譯成模塊)。若是CONFIG_FOO不是y
和m,那麼該文件就不會被編譯聯接了。
--- 3.2 編譯進內核 - obj-y
Kbuild Makefile 規定全部編譯進內核的目標文件都存在$(obj-y)列表中。而
這些列表依賴內核的配置。
Kbuild編譯全部的$(obj-y)文件。而後,調用"$(LD) -r"將它們合併到一個
build-in.o文件中。稍後,該build-in.o會被其父Makefile聯接進vmlinux中。
$(obj-y)中的文件是有順序的。列表中有重複項是能夠的:當第一個文件被聯
接到built-in.o中後,其他文件就被忽略了。
聯接也是有順序的,那是由於有些函數(module_init()/__initcall)將會在啓
動時按照他們出現的順序進行調用。因此,記住改變聯接的順序可能改變你
SCSI控制器的檢測順序,從而致使你的硬盤數據損害。
例子:
#drivers/isdn/i4l/Makefile
# Makefile for the kernel ISDN subsystem and device drivers.
# Each configuration option enables a list of files.
obj-$(CONFIG_ISDN) += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
--- 3.3 編譯可裝載模塊 - obj-m
$(obj-m) 列舉出了哪些文件要編譯成可裝載模塊。
一個模塊能夠由一個文件或多個文件編譯而成。若是是一個源文件,Kbuild
Makefile只需簡單的將其加到$(obj-m)中去就能夠了。
例子:
#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
注意:此例中 $(CONFIG_ISDN_PPP_BSDCOMP) 的值爲'm'
若是內核模塊是由多個源文件編譯而成,那你就要採用上面那個例子同樣的
方法去聲明你所要編譯的模塊。
Kbuild須要知道你所編譯的模塊是基於哪些文件,因此你須要經過變量
$(<module_name>-objs)來告訴它。
例子:
#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
在這個例子中,模塊名將是isdn.o,Kbuild將編譯在$(isdn-objs)中列出的
全部文件,而後使用"$(LD) -r"生成isdn.o。
Kbuild可以識別用於組成目標文件的後綴-objs和後綴-y。這就讓Kbuild
Makefile能夠經過使用 CONFIG_ 符號來判斷該對象是不是用來組合對象的。
例子:
#fs/ext2/Makefile
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
在這個例子中,若是 $(CONFIG_EXT2_FS_XATTR) 是 'y',xattr.o將是複合
對象 ext2.o的一部分。
注意:固然,當你要將其編譯進內核時,上面的語法一樣適用。因此,若是
你的 CONFIG_EXT2_FS=y,那Kbuild會按你所指望的那樣,生成 ext2.o文件
,而後將其聯接到 built-in.o中。
--- 3.4 輸出的符號
在Makefile中,沒有對模塊輸出的符號有特殊要求。
--- 3.5 目標庫文件 - lib-y
在 obj-* 中所列文件是用來編譯模塊或者是聯接到特定目錄中的 built-in.o
。一樣,也能夠列出一些將被包含在lib.a庫中的文件。
在 lib-y 中所列出的文件用來組成該目錄下的一個庫文件。
在 obj-y 與 lib-y 中同時列出的文件,由於都是能夠訪問的,因此該文件是
不會被包含在庫文件中的。
一樣的狀況, lib-m 中的文件就要包含在 lib.a 庫文件中。
注意,一個Kbuild makefile能夠同時列出要編譯進內核的文件與要編譯成庫
的文件。因此,在一個目錄裏能夠同時存在 built-in.o 與 lib.a 兩個文件。
例子:
#arch/i386/lib/Makefile
lib-y := chechsum.o delay.o
這將由 checksum.o 和delay.o 兩個文件建立一個庫文件 lib.a。爲了讓
Kbuild 真正認識到這裏要有一個庫文件 lib.a 要建立,其所在的目錄要加
到 libs-y 列表中。
還可參考"6.3 遞歸下向時要訪問的目錄列表"
lib-y 使用通常限制在 lib/ 和 arch/*/lib 中。
--- 3.6 遞歸向下訪問目錄
一個Makefile只對編譯所在目錄的對象負責。在子目錄中的文件的編譯要由
其所在的子目錄的Makefile來管理。只要你讓Kbuild知道它應該遞歸操做,
那麼該系統就會在其子目錄中自動的調用 make 遞歸操做。
這就是 obj-y 和 obj-m 的做用。
ext2 被放的一個單獨的目錄下,在fs目錄下的Makefile會告訴Kbuild使用
下面的賦值進行向下遞歸操做。
例子:
#fs/Makefile
obj-$(CONFIG_EXT2_FS) += ext2/
若是 CONFIG_EXT2_FS 被設置爲 'y'(編譯進內核)或是'm'(編譯成模塊),相
應的 obj- 變量就會被設置,而且Kbuild就會遞歸向下訪問 ext2 目錄。
Kbuild只是用這些信息來決定它是否須要訪問該目錄,而具體怎麼編譯由該目
錄中的Makefile來決定。
將 CONFIG_ 變量設置成目錄名是一個好的編程習慣。這讓Kbuild在徹底忽略那
些相應的 CONFIG_ 值不是'y'和'm'的目錄。
--- 3.7 編輯標誌
EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
全部的 EXTRA_ 變量只在所定義的Kbuild Makefile中起做用。EXTRA_ 變量可
以在Kbuild Makefile中全部命令中使用。
$(EXTRA_CFLAGS) 是用 $(CC) 編譯C源文件時的選項。
例子:
# drivers/sound/emu10kl/Makefile
EXTRA_CFLAGS += -I$(obj)
ifdef DEBUG
EXTRA_CFLAGS += -DEMU10KL_DEBUG
endif
該變量是必須的,由於頂層Makefile擁有變量 $(CFLAGS) 並用來做爲整個源
代碼樹的編譯選項。
$(EXTRA_AFLAGS) 也是一個針對每一個目錄的選項,只不過它是用來編譯彙編
源代碼的。
例子:
#arch/x86_64/kernel/Makefile
EXTRA_AFLAGS := -traditional
$(EXTRA_LDFLAGS) 和 $(EXTRA_ARFLAGS)分別與 $(LD)和 $(AR)相似,只不
過,他們是針對每一個目錄的。
例子:
#arch/m68k/fpsp040/Makefile
EXTRA_LDFLAGS := -x
CFLAGS_$@, AFLSGA_$@
CFLAGS_$@ 和 AFLAGS_$@ 只能在當前Kbuild Makefile中的命令中使用。
$(CFLAGS_$@) 是 $(CC) 針對每一個文件的選項。$@ 代表了具體操做的文件。
例子:
# drivers/scsi/Makefile
CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF
CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ /
-DGDTH_STATISTICS
CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
以上三行分別設置了aha152x.o,gdth.o 和 seagate.o的編輯選項。
$(AFLAGS_$@) 也相似,只不是是針對彙編語言的。
例子:
# arch/arm/kernel/Makefile
AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional
AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional
--- 3.9 跟蹤依賴
Kbuild 跟蹤在如下方面依賴:
1) 全部要參與編譯的文件(全部的.c 和.h文件)
2) 在參與編譯文件中所要使用的 CONFIG_ 選項
3) 用於編譯目標的命令行
所以,若是你改變了 $(CC) 的選項,全部受影響的文件都要從新編譯。
--- 3.10 特殊規則
特殊規則就是那Kbuild架構不能提供所要求的支持時,所使用的規則。一個
典型的例子就是在構建過程當中生成的頭文件。
另外一個例子就是那些須要採用特殊規則來準備啓動鏡像。
特殊規則的寫法與普通Make規則同樣。
Kbuild並不在Makefile所在的目錄執行,因此全部的特殊規則都要提供參與
編譯的文件和目標文件的相對路徑。
在定義特殊規則時,要使用如下兩個變量:
$(src)
$(src) 代表Makefile所在目錄的相對路徑。常常在定位源代碼樹中的文件時
,使用該變量。
$(obj)
$(obj) 代表目標文件所要存儲目錄的相對路徑。常常在定位所生成的文件時
,使用該變量。
例子:
#drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
這就是一個特殊規則,遵照着make所要求的普通語法。
目標文件依賴於兩個源文件。用$(obj)來定位目標文件,用$(src)來定位源文
件(由於它們不是咱們生成的文件)。
--- 3.11 $(CC) 支持的函數
內核可能由多個不一樣版本的$(CC)編譯,而每一個版本都支持一不一樣的功能集與
選項集。Kbuild提供了檢查 $(CC) 可用選項的基本功能。$(CC)通常狀況下是
gcc編譯器,但也可使用其它編譯器來代替gcc。
as-option
as-option,當編譯彙編文件(*.S)時,用來檢查 $(CC) 是否支持特定選項。如
果第一個選項不支持的話,可選的第二個選項能夠用來指定。
例子:
#arch/sh/Makefile
cflags-y += $(call as-option,-Wa$(comma)-isa=$(isa-y),)
在上面的例子裏,若是 $(CC) 支持選項 -Wa$(comma)-isa=$(isa-y),
cflags-y就會被賦予該值。
第二個參數是可選的,當第一個參數不支持時,就會使用該值。
ld-option
ld-option,當聯接目標文件時,用來檢查 $(CC) 是否支持特定選項。若是第
一個選項不支持的話,可選的第二個選項能夠用來指定。
例子:
#arch/i386/kernel/Makefile
vsyscall-flags += $(call ld-option, -Wl$(comma)--hash-style=sysv)
在上面的例子中,若是 $(CC)支持選項 -Wl$(comma)--hash-style=sysv,
ld-option就會被賦予該值。
第二個參數是可選的,當第一個參數不支持時,就會使用該值。
cc-option
cc-option,用來檢查 $(CC) 是否支持特定選項,而且不支持使用可選的第二
項。
例子:
#arch/i386/Makefile
cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)
在上面的例子中,若是 $(CC)支持選項 -march=pentium-mmx,cc-option就
會被賦予該值,不然就賦 -march-i586。
cc-option的第二個參數是可選的。若是忽略的話,當第一個選項不支持時,
cflags-y 不會被賦值。
cc-option-yn
cc-option-yn,用來檢查 gcc 是否支持特定選項,返回'y'支持,不然爲'n'。
例子:
#arch/ppc/Makefile
biarch := $(call cc-option-yn, -m32)
aflags-$(biarch) += -a32
cflags-$(biarch) += -m32
在上面的例子裏,當 $(CC) 支持 -m32選項時,$(biarch)設置爲y。當
$(biarch) 爲y時,擴展的 $(aflags-y) 和 $(cflags-y)變量就會被賦值爲
-a32 和 -m32。
cc-option-align
gcc版本大於3.0時,改變了函數,循環等用來聲明內存對齊的選項。當用到
對齊選項時,$(cc-option-align) 用來選擇正確的前綴:
gcc < 3.00
cc-option-align = -malign
gcc >= 3.00
cc-option-align = -falign
例子:
CFLAGS += $(cc-option-align)-functions=4
在上面的例子中,選項 -falign-funcions=4 被用在gcc >= 3.00的時候。對
於小於3.00時, 使用 -malign-funcions=4 。
cc-version
cc-version以數學形式返回 $(CC) 編譯器的版本號。
其格式是:<major><minor>,兩者都是數學。好比,gcc 3.41 會返回 0341。
當某版本的 $(CC) 在某方面有缺陷時,cc-version就會頗有用。好比,選項
-mregparm=3 雖然會被gcc接受,但其實現是有問題的。
例子:
#arch/i386/Makefile
cflags-y += $(shell /
if [ $(call cc-version) -ge 0300 ] ; then /
echo "-meregparm=3"; fi ;)
在上面的例子中,-mregparm=3只會在gcc的版本號大於等於3.0的時候使用。
cc-ifversion
cc-ifversion測試 $(CC) 的版本號,若是版本表達式爲真,就賦值爲最後的
參數。
例子:
#fs/reiserfs/Makefile
EXTRA_CFLAGS := $(call cc-ifversion, -lt, 0402, -O1)
在這個例子中,若是 $(CC) 的版本小於4.2,EXTRA_CFLAGS就被賦值 -O1。
cc-ifversion 可以使用全部的shell 操做符:-eq,-ne,-lt,-le,-gt,和-ge。
第三個參數能夠像上面例子同樣是個文本,但也能夠是個擴展的變量或宏。
/*這段翻譯的很差*/
=== 4 本機程序支持
Kbuild 支持編譯那些將在編譯階段使用的可執行文件。
爲了使用該可執行文件,要將編譯分紅二個階段。
第一階段是告訴Kbuild存在哪些可執行文件。這是經過變量 hostprogs-y來完成的。
第二階段是添加一個對可執行文件的顯性依賴。有兩種方法:增長依賴關係到一個規則
中,或是利用變量 $(always)。
如下是詳細敘述.
--- 4.1 簡單的本機程序
在編譯內核時,有時會須要編譯並運行一個程序。
下面這行就告訴了kbuild,程序bin2hex應該在本機上編譯。
例子:
hostprogs-y := bin2hex
在上面的例子中,Kbuild假設bin2hex是由一個與其在同一目錄下,名爲
bin2hex.c 的C語言源文件編譯而成的。
--- 4.2 複合的本機程序
本機程序能夠由多個文件編譯而成。
所使用的語法與內核的相應語法很類似。
$(<executeable>-objs) 列出了聯接成最後的可執行文件所需的全部目標文件。
例子:
#scripts/lxdialog/Makefile
hostprogs-y := lxdialog
lxdialog-objs := checklist.o lxdialog.o
擴展名爲.o的文件是從相應的.c文件編譯而來的。在上面的例子中,
checklist.c 編譯成了checklist.o,lxdialog.c編譯成了lxdialog.o。
最後,兩個.o文件聯接成了一可執行文件,lxdialog。
注意:語法 <executable>-y不是隻能用來生成本機程序。
--- 4.3 定義共享庫
擴展名爲so的文件稱爲共享庫,被編譯成位置無關對象。
Kbuild也支持共享庫,但共享庫的使用頗有限。
在下面的例子中,libconfig.so共享庫用來聯接到可執行文件 conf中。
例子:
#scripts/kconfig/Makefile
hostprogs-y := conf
conf-objs := conf.o libkconfig.so
libkcofig-objs := expr.o type.o
共享庫文件常常要求一個相應的 -objs,在上面的例子中,共享庫libkconfig
是由 expr.o 和 type.o兩個文件組成的。
expr.o 和 type.o 將被編譯成位置無關碼,而後聯接成共享庫文件
libkconfig.so。C++並不支持共享庫。
--- 4.4 使用用C++編寫的本機程序
kbuild也支持用C++編寫的本機程序。在此專門介紹是爲了支持kconfig,而且
在通常狀況下不推薦使用。
例子:
#scripts/kconfig/Makefile
hostprogs-y := qconf
qconf-cxxobjs := qconf.o
在上面的例子中,可執行文件是由C++文件 qconf.cc編譯而成的,由
$(qconf-cxxobjs)來標識。
若是qconf是由.c和.cc一塊兒編譯的,那麼就須要專門來標識這些文件了。
例子:
#scripts/kconfig/Makefile
hostprogs-y := qconf
qconf-cxxobjs := qconf.o
qconf-objs := check.o
--- 4.5 控制本機程序的編譯選項
當編譯本機程序時,有可能使用到特殊選項。程序常常是利用$(HOSTCC)編譯
,其選項在 $(HOSTCFLAGS)變量中。
可經過使用變量 HOST_EXTRACFLAGS,影響全部在Makefile文件中要建立的
主機程序。
例子:
#scripts/lxdialog/Makefile
HOST_EXTRACFLAGS += -I/usr/include/ncurses
爲一單個文件設置選項,可按形式進行:
例子:
#arch/ppc64/boot/Makefile
HOSTCFLAGS_pinggyback.o := -DKERNELBASE=$(KERNELBASE)
一樣也能夠給聯接器聲明一特殊選項。
例子:
#scripts/kconfig/Makefile
HOSTLOADLIBES_qconf := -L$(QTDIR)/lib
當聯接qconf時,將會向聯接器傳遞附加選項 "-L$(QTDIR)/lib"。
--- 4.6 編譯主機程序時
Kbuild只在須要時編譯主機程序。
有兩種方法:
(1) 在一具體的規則中顯性列出所須要的文件
例子:
#drivers/pci/Makefile
hostprogs-y := gen-devlist
$(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist
( cd $(obj); ./gen-devlist ) < $<
目標 $(obj)/devlist.h 是不會在 $(obj)/gen-devlist 更新以前編譯的。注意
在該規則中全部有關主機程序的命令必須以$(obj)開頭。
(2) 使用 $(always)
當Makefile要編譯主機程序,但沒有適合的規則時,使用 $(always)。
例子:
#scripts/lxdialog/Makefile
hostprogs-y := lxdialog
always := $(hostprogs-y)
這就是告訴Kbuild,即便沒有在規則中聲明,也要編譯 lxdialog。
--- 4.7 使用 hostprogs-$(CONFIG_FOO)
一個典型的Kbuild模式以下:
例子:
#scripts/Makefile
hostprogs-$(CONFIG_KALLSYMS) += kallsyms
Kbuild 知道 'y' 是編譯進內核,而 'm' 是編譯成模塊。因此,若是配置符號
是'm',Kbuild仍然會編譯它。換句話說,Kbuild處理 hostprogs-m 與
hostprogs-y 的方式是徹底一致的。只是,若是不用 CONFIG,最好用
hostprogs-y。
=== 5 Kbuild清理(clean)
"make clean"刪除幾乎全部的在編譯內核時生成的文件,包括了主機程序在內。
Kbuild 經過列表 $(hostprogs-y),$(hostprogs-m),$(always),$(extra-y) 和
$(targets) 知道所要編譯的目標。這些目標文件都會被 "make clean" 刪除。另外
,在"make clean"還會刪除匹配 "*.[oas]","*.ko" 的文件,以及由 Kbuild生成
的輔助文件。
輔助文件由 Kbuild Makefile 中的 $(clean-files) 指明。
例子:
#drivers/pci/Makefile
clean-files := devlist.h classlist.h
當執行 "make clean" 時,"devlist.h classlist.h"這兩個文件將被刪除。若是不使用
絕對路徑(路徑以'/'開頭)的話,Kbuild假設所要刪除的文件與Makefile在同一個相對路
徑上。
要刪除一目錄:
例子:
#scripts/package/Makefile
clean-dirs := $(objtree)/debian/
這就會刪除目錄 debian,包括其全部的子目錄。若是不使用絕對路徑(路徑以'/'開頭)的
話,Kbuild假設所要刪除的目錄與Makefile在同一個相對路徑上。
通常狀況下,Kbuild會根據 "obj-* := dir/" 遞歸訪問其子目錄,但有的時候,Kbuild
架構還不足以描述全部的狀況時,還要顯式的指明所要訪問的子目錄。
例子:
#arch/i386/boot/Makefile
subdir- := compressed/
上面的賦值命令告訴Kbuild,當執行"make clean"時,要遞歸訪問目錄 compressed/。
爲了支持在最終編譯完成啓動鏡像後的架構清理工做,還有一可選的目標 archclean:
例子:
#arch/i386/Makefile
archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
當"make clean"執行時,make會遞歸訪問並清理 arch/i386/boot。在 arch/i386/boot
中的Makefile能夠用來提示make進行下一步的遞歸操做。
注意1:arch/$(ARCH)/Makefile 不能使用"subdir-",由於該Makefile被包含在頂層的
Makefile中,Kbuild是不會在此處進行操做的。
注意2:"make clean" 會訪問在 core-y,libs-y,drivers-y 和 net-y 列出的全部目
錄。
=== 6 架構Makefile
在遞歸訪問目錄以前,頂層Makefile要完成設置環境變量以及遞歸訪問的準備工做。頂
層Makefile包含的公共部分,而 arch/$(ARCH)/Makefile 包含着針對某一特定架構的
配置信息。
因此,要在 arch/$(ARCH)/Makefile 中設置一部分變量,並定義一些目標。
Kbuild執行的幾個步驟(大體):
1) 根據內核配置生成文件 .config
2) 將內核的版本號存儲在 include/linux/version.h
3) 生成指向 include/asm-$(ARCH) 的符號連接
4) 更新全部編譯所需的文件:
-附加的文件由 arch/$(ARCH)/Makefile 指定。
5) 遞歸向下訪問全部在下列變量中列出的目錄: init-* core* drivers-* net-*
libs-*,並編譯生成目標文件。
-這些變量的值能夠在 arch/$(ARCH)/Makefile 中擴充。
6) 聯接全部的目標文件,在源代碼樹頂層目錄中生成 vmlinux。最早聯接是在 head-y中
列出的文件,該變量由 arch/$(ARCH)/Makefile 賦值。
7) 最後完成具體架構的特殊要求,並生成最終的啓動鏡像。
-包含生成啓動指令
-準備 initrd 鏡像或相似文件
--- 6.1 調整針對某一具體架構生成的鏡像
LDFLAGS 通常是 $(LD) 選項
該選項在每次調用聯接器時都會用到。
通常狀況下,只用來指明模擬器。
例子:
#arch/s390/Makefile
LDFLAGS := -m elf_s390
注意:EXTRA_LDFLAGS 和 LDFLAGS_$@ 可用來進一步自定義選項。請看第七章。
LDFLAGS_MODULE 聯接模塊時的聯接器的選項
LDFLAGS_MODULE 所設置的選項將在聯接器在聯接模塊文件 .ko 時使用。
默認值爲 "-r",指定輸出文件是可重定位的。
LDFLAGS_vmlinux 聯接vmlinux時的選項
LDFLAGS_vmlinux用來傳遞聯接vmlinux時的聯接器的選項。
LDFLAGS_vmlinux需 LDFLAGS_$@ 支持。
例子:
#arch/i386/Makefile
LDFLAGS_vmlinux := -e stext
OBJCOPYFLAGS objcopy 選項
當用 $(call if_changed,objcopy) 來轉換(translate)一個.o文件時,該選項
就會被使用。
$(call if_changed,objcopy) 常常被用來爲vmlinux生成原始的二進制代碼。
例子:
#arch/s390/Makefile
OBJCOPYFLAGS := -O binary
#arch/s390/boot/Makefile
$(obj)/image: vmlinux FORCE
$(call if_changed,objcopy)
在此例中,二進制文件 $(obj)/image 是 vmlinux 的一個二進制版本。
$(call if_chagned,xxx)的用法稍後描述。
AFLAGS $(AS) 彙編編譯器選項
默認值在頂層Makefile
擴充或修改在各具體架構的Makefile
例子:
#arch/sparc64/Makefile
AFLAGS += -m64 -mcpu=ultrasparc
CFLAGS $(CC) 編譯器選項
默認值在頂層Makefile
擴充或修改在各具體架構的Makefile。
通常,CFLAGS要根據內核配置設置。
例子:
#arch/i386/Makefile
cflags-$(CONFIG_M386) += -march=i386
CFLAGS += $(cflags-y)
許多架構Makefile都經過調用所要使用的C編譯器,動態的檢查其所支持的選
項:
#arch/i386/Makefile
...
cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,/
-march=pentium2,-march=i686)
...
# Disable unit-at-a-time mode ...
CFLAGS += $(call cc-option,-fno-unit-at-a-time)
...
第一個例子利用了一個配置選項,當其爲'y'時,擴展。
CFLAGS_KERNEL :
#arch/i386/Makefile
...
cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,/
-march=pentium2,-march=i686)
...
# Disable unit-at-a-time mode ...
CFLAGS += $(call cc-option,-fno-unit-at-a-time)
...
第一個例子利用了一個配置選項,當其爲'y'時,擴展。
CFLAGS_KERNEL 編譯進內核時,$(CC) 所用的選項
$(CFLAGS_KERNEL) 包含了用於編譯常駐內核代碼的附加編譯器選項。
CFLAGS_MODULE 編譯成模塊時,$(CC)所用的選項
$(CFLAGS_MODULE) 包含了用於編譯可裝載模塊的附加編譯器選項。
--- 6.2 將所需文件加到 archprepare 中:
archprepare規則在遞歸訪問子目錄以前,列出編譯目標文件所需文件。
通常狀況下,這是一個包含彙編常量的頭文件。(assembler constants)
例子:
#arch/arm/Makefile
archprepare: maketools
此例中,目標文件 maketools 將在遞歸訪問子目錄以前編譯。
在TODO一章能夠看到,Kbuild是如何支持生成分支頭文件的。
(offset header files)
--- 6.3 遞歸下向時要訪問的目錄列表
如何生成 vmlinux,是由架構makefile和頂層Makefile一塊兒來定義的。注意,
架構Makefile是不會定義與模塊相關的內容的,全部構建模塊的定義是與架構
無關的。
head-y,init-y,core-y,libs-y,drivers-y,net-y
$(head-y) 列出了最早被聯接進 vmlinux 的目標文件。
$(libs-y) 列出了生成的全部 lib.a 所在的目錄。
其他所列的目錄,是 built-in.o 所在的目錄。
$(init-y) 在 $(head-y) 以後所要使用的文件。
而後,剩下的步驟以下:
$(core-y),$(libs-y),$(drivers-y)和$(net-y)。
頂層makefile定義了通用的部分,arch/$(ARCH)/Makefile 添加了架構的特殊
要求。
例子:
#arch/sparc64/Makefile
core-y += arch/sparc64/kernel/
libs-y += arch/sparc64/prom/ arch/sparc64/lib/
drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/
--- 6.4 具體架構的啓動鏡像
一具體架構Makefile的具體目的就是,將生成並壓縮 vmlinux 文件,寫入啓動
代碼,並將其拷貝到正確的位置。這就包含了多種不一樣的安裝命令。該具體目的
也沒法在各個平臺間進行標準化。
通常,附加的處理命令入在 arch/$(ARCH)/下的boot目錄。
Kbuild並無爲構造boot所指定的目標提供任何更好的方法。因此,
arch/$(ARCH)/Makefile 將會調用 make 以手工構造 boot的目標文件。
比較好的方法是,在 arch/$(ARCH)/Makefile 中包含快捷方式,並在
arch/$(ARCH)/boot/Makefile 中使用所有路徑。
例子:
#arch/i386/Makefile
boot := arch/i386/boot
bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
當在子目錄中調用 make 時,推薦使用 "$(Q)$(MAKE) $(build)=<dir>" 。
並無對架構特殊目標的命名規則,但用命令 "make help" 能夠列出全部的
相關目標。
爲了支持 "make help",$(archhelp) 必須被定義。
例子:
#arch/i386/Makefile
define archhelp
echo '* bzImage - Image (arch/$(ARCH)/boot/bzImage)'
endef
當make 沒帶參數執行時,所遇到的第一個目標將被執行。在頂層,第一個目標
就是 all:。
每一個架構Makefile都要默認構造一可啓動的鏡像文件。
在 "make help"中,默認目標就是被加亮的'*'。
添加一新的前提文件到 all:,就能夠構造出一不一樣的vmlinux。
例子:
#arch/i386/Makefile
all: bzImage
當 make 沒有參數時,bzImage將被構造。
--- 6.5 構造非Kbuild目標
extra-y
extra-y 列出了在當前目錄下,所要建立的附加文件,不包含任何已包含在
obj-* 中的文件。
用 extra-y 列目標,主要是兩個目的:
1) 可使Kbuild檢查命令行是否發生變化
- 使用 $(call if_changed,xxx) 的時候
2) 讓Kbuild知道哪些文件要在 "make clean" 時刪除
例子:
#arch/i386/kernel/Makefile
extra-y := head.o init_task.o
在此例子中,extra-y用來列出全部只編譯,但不聯接到 built-in.o的目標
文件。
--- 6.6 構建啓動鏡像的命令
Kbuild 提供了幾個用在構建啓動鏡像時的宏。
if_changed
if_changed 爲下列命令的基礎。
使用方法:
target: source(s) FORCE
$(call if_changed,ld/objcopy/gzip)
當執行該規則時,就檢查是否有文件須要更新,或者在上次調用之後,命令行
發生了改變。若是有選項發生了改變,後者會致使從新構造。
只有在 $(targets)列出的的目標文件,才能使用 if_changed,不然命令行的
檢查會失敗,而且目標總會被重建。
給 $(targets)的賦值沒有前綴 $(obj)/ 。 if_changed 可用來聯接自定義的
Kbuild命令,關於Kbuild自定義命令請看 6.7節。
注意:忘記 FORCE 是一種典型的錯誤。還有一種廣泛的錯誤是,空格有的時候
是有意義的;好比。下面的命令就會錯誤(注意在逗號後面的那個多餘的空格):
target: source(s) FORCE
#WRONG!# $(call if_changed, ld/objcopy/gzip)
ld
聯接目標。常常是使用LDFLAGS_$@來設置ld的特殊選項。
objcopy
拷貝二進制代碼。通常是在 arch/$(ARCH)/Makefile 中使用 OBJCOPYFLAGS。
OBJCOPYFLAGS_$@ 能夠用來設置附加選項。
gzip
壓縮目標文件。儘量的壓縮目標文件。
例子:
#arch/i386/boot/Makefile
LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary
LDFLAGS_setup := -Ttext 0x0 -s --oformat binary -e begtext
targets += setup setup.o bootsect bootsect.o
$(obj)/setup $(obj)/bootsect: %: %.o FORCE
$(call if_changed,ld)
在這個例子中,有兩個可能的目標文件,分別要求不一樣的聯接選項。定義聯接
器的選項使用的是 LDFLAGS_$@ 語法,每一個潛在的目標一個。
$(targets) 被分配給全部的潛在目標,所以知道目標是哪些,而且還會:
1) 檢查命令行是否改變
2) 在 "make clean" 時,刪除目標文件
前提部分中的 ": %: %.o" 部分使咱們沒必要在列出文件 setup.o 和
bootsect.o 。
注意:一個廣泛的錯誤是忘記了給 "target"賦值,致使在target中的文件老是
平白無故的被從新編譯。
--- 6.7 Kbuild自定義命令
當Kbuild的變量 KBUILD_VERBOSE 爲0時,只會顯示命令的簡寫。
若是要爲自定義命令使用這一功能,須要設置2個變量:
quiet_cmd_<command> - 要顯示的命令
cmd_<command> - 要執行的命令
例子:
#
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(BUILDFLAGS) /
$(obj)/vmlinux.bin > $@
targets += bzImage
$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: $@ is ready'
當用"make KBUILD_VERBOSE=0"更新 $(obj)/bzImage 目標時顯示:
BUILD arch/i386/boot/bzImage
--- 6.8 聯接器預處理腳本
當構造 vmlinux 鏡像時,使用聯接器腳本:
arch/$(ARCH)/kernel/vmlinux.lds。
該腳本是由在同一目錄下的 vmlinux.lds.S 生成的。
Kbuild認識.lds文件,幷包含由*.lds.S文件生成*.lds文件的規則。
例子:
#arch/i386/kernel/Makefile
always := vmlinux.lds
#Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
$(always)的值是用來告訴Kbuild,構造目標 vmlinux.lds。
$(CPPFLAGS_vmlinux.lds),Kbuild在構造目標vmlinux.lds時所用到的特殊
選項。
當構造 *.lds 目標時,Kbuild要用到下列變量:
CPPFLAGS : 在頂層目錄中設置
EXTRA_CPPFLAGS : 能夠在Kbuild Makefile中設置
CPPFLAGS_$(@F) : 目標特別選項
注意,此處的賦值用的完整的文件名。
針對*.lds文件的Kbuild構架還被用在許多具體架構的文件中。(***不通***)
=== 7 Kbuild 變量
頂層Makefile輸出如下變量:
VERSION,PATCHLEVEL,SUBLEVEL,EXTRAVERSION
這些變量定義了當前內核的版本號。只有不多一部分Makefile會直接用到這些
變量;可以使用 $(KERNELRELEASE)代替。
$(VERSION),$(PATCHLEVEL),和$(SUBLEVEL) 定義了最初使用的三個數字的版本
號,好比"2""4"和"0"。這三個值通常是數字。
$(EXTRAVERSION) 爲了補丁定義了更小的版本號。通常是非數字的字符串,好比
"-pre4" ,或就空着。
KERNELRELEASE
$(KERNELRELEASE) 是一個字符串,相似"2.4.0-pre4",用於安裝目錄的命名或
顯示當前的版本號。一部分架構Makefile使用該變量。
ARCH
該變量定義了目標架構,好比"i386","arm" 或"sparc"。有些Kbuild Makefile
根據 $(ARCH) 決定編譯哪些文件。
默認狀況下,頂層Makefile將其設置爲本機架構。若是是跨平臺編譯,用戶能夠
用下面的命令覆蓋該值:
make ARCH=m68k ...
INSTALL_PATH
該變量爲架構Makefile定義了安裝內核鏡像與 System.map 文件的目錄。
主要用來指明架構特殊的安裝路徑。
INSTALL_MOD_PATH,MODLIB
$(INSTALL_MOD_PATH) 爲了安裝模塊,給 $(MODLIB) 聲明瞭前綴。該變量不能
在Makefile中定義,但能夠由用戶傳給Makefile。
$(MODLIB) 具體的模塊安裝的路徑。頂層Makefile將$(MODLIB)定義爲
$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。用戶能夠經過命令行
參數的形式將其覆蓋。
INSTALL_MOD_STRIP
若是該變量有定義,模塊在安裝以前,會被剝出符號表。若是
INSTALL_MOD_STRIP 爲 "1",就使用默認選項 --strip-debug。不然,
INSTALL_MOD_STRIP 將做爲命令 strip 的選項使用。
=== 8 Makefile語言
內核的Makefile使用的是GNU Make。該Makefile只使用GNU Make已註明的功能,並使用
了許多GNU 的擴展功能。
GNU Make支持基本的顯示處理過程的函數。內核Makefile 使用了一種相似小說的方式
,顯示"if"語句的構造、處理過程。
GNU Make 有2個賦值操做符,":="和"="。":=",將對右邊的表達式求值,並將所求的值
賦給左邊。"="更像是一個公式定義,只是將右邊的值簡單的賦值給左邊,當左邊的表達
式被使用時,才求值。
有時使用"="是正確的。可是,通常狀況下,推薦使用":="。
=== 9 關於做者
初版由 Michael Elizabeth Chastain,<mailto:mec@shout.NET>
修改:kai Germaschewski <kai@tpl.ruhr-uni-bochum.de>
Sam Ravnborg <sam@ravnborg.org>
=== 10 TODO
- 描述Kbuild是如何用 _shipped 來支持 shipped 文件的。
- 生成分支頭文件
- 在第7節加入更多的變量 linux