2011年9月27日 星期二

linux2.6 makefiles.txt學習及實例分析

linux2.6 makefiles.txt學習及實例分析 (2008-08-28 10:50)


本篇blog主要分為四部分,地一部分和第二部分主要是參考網上的文章,第三部分為自己在學習過程中總結的一些知識,第四部分想自己編寫一個簡單的Makefile,以鞏固學習成果!



本篇blog目的:通過對Makefile的學習,進一步理解linux內核如何通過makefile實現對make過程的自動化,掌握makefile語言編寫規則,最終實現自己能夠編寫出makefile檔。



本篇blog預計完成時間一個禮拜。



這篇文章主要是參考資料,不敢邀功,現給出原地址和參考書籍,感謝這兩位大蝦的慷慨分享。

參考文件:

http://forum.eepw.com.cn/forum/main?url=http%3A%2F%2Fbbs.edw.com.cn%2Fthread%2F128730%2F1

http://blog.chinaunix.net/u2/66601/showart_1150788.html Author:cyliu

《嵌入式linux系統開發技術詳解-基於arm》孫紀坤 張小全 人民郵電出版社

________________________________________

第一部分:對makefiles.txt的學習

內核目錄documention/kbuild/makefiles.txt中文版的翻譯

This document describes the Linux kernel Makefiles

=== 目錄

=== 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(由頂層Makefile語句include $(srctree)/arch/$(ARCH)/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需要知道你所編譯的模組是基於哪些檔,所以你需要通過變數 $(-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) 編譯器的版本號。

其格式是:,二者都是數學。比如,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 複合的本機程式



本機程式可以由多個檔編譯而成。

所使用的語法與內核的相應語法很相似。

$(-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。注意:語法 -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)=

" 。



並沒有對架構特殊目標的命名規則,但用命令 "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_ - 要顯示的命令

cmd_ - 要執行的命令



例子:

#

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輸出以下變數:



1、VERSION,PATCHLEVEL,SUBLEVEL,EXTRAVERSION



這些變數定義了當前內核的版本號。只有很少一部分Makefile會直接用到這些

變數;可使用 $(KERNELRELEASE)代替。



$(VERSION),$(PATCHLEVEL),和$(SUBLEVEL) 定義了最初使用的三個數位的版本

號,比如"2""4"和"0"。這三個值一般是數字。



$(EXTRAVERSION) 為了補丁定義了更小的版本號。一般是非數位的字串,比如

"-pre4" ,或就空著。



KERNELRELEASE



$(KERNELRELEASE) 是一個字串,類似"2.4.0-pre4",用於安裝目錄的命名或

顯示當前的版本號。一部分架構Makefile使用該變數。



2、ARCH



該變數定義了目標架構,比如"i386","arm" 或"sparc"。有些Kbuild Makefile

根據 $(ARCH) 決定編譯哪些文件。



默認情況下,頂層Makefile將其設置為本機架構。如果是跨平臺編譯,用戶可以

用下麵的命令覆蓋該值:



make ARCH=m68k ...





3、INSTALL_PATH



該變數為架構Makefile定義了安裝內核鏡像與 System.map 檔的目錄。主要用來指明架構特殊的安裝路徑。



4、INSTALL_MOD_PATH,MODLIB



$(INSTALL_MOD_PATH) 為了安裝模組,給 $(MODLIB) 聲明了首碼。該變數不能

在Makefile中定義,但可以由用戶傳給Makefile。



$(MODLIB) 具體的模組安裝的路徑。頂層Makefile將$(MODLIB)定義為$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。用戶可以通過命令行參數的形式將其覆蓋。



5、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,

修改:kai Germaschewski

Sam Ravnborg



=== 10 TODO



- 描述Kbuild是如何用 _shipped 來支持 shipped 檔的。

- 生成分支頭檔

- 在第7節加入更多的變數

________________________________________

第二部分:Makefile 實例分析



VERSION = 2

PATCHLEVEL = 6

SUBLEVEL = 20

EXTRAVERSION = .7

NAME = Homicidal Dwarf Hamster



# 以上表明了內核版本。組合起來就是:2.6.20.7 ,yeah,這就是我分析的內核版本



# 注意寫makefile時不要使用makefile的內建的規則和變數



#要想不列印"Entering directory ..."字樣,請使用no-print-directory選項

MAKEFLAGS += -rR --no-print-directory



# 因為需要遞迴執行build, 所以必須注意要保證按照正確順序執行make.



# 使用 'make V=1' 可以看到完整命令



ifdef V

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

KBUILD_VERBOSE = $(V)

endif

endif

ifndef KBUILD_VERBOSE

KBUILD_VERBOSE = 0

endif



# 使用 'make C=1' 僅檢查需要重新使用c編譯器編譯的檔

# 使用 'make C=2' 檢查所有c編譯器編譯的檔



ifdef C

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

KBUILD_CHECKSRC = $(C)

endif

endif

ifndef KBUILD_CHECKSRC

KBUILD_CHECKSRC = 0

endif



# 使用 make M=dir 表明需要編譯的模組目錄

# 舊的語法make ... SUBDIRS=$PWD 依然支持

# 這裏通過環境變數 KBUILD_EXTMOD來表示

ifdef SUBDIRS

KBUILD_EXTMOD ?= $(SUBDIRS)

endif

ifdef M

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

KBUILD_EXTMOD := $(M)

endif

endif





# kbuild 可以把輸出檔放到一個分開的目錄下

# 定位輸出檔目錄有兩種語法支援:

# 兩種方法都要求工作目錄是內核原始檔案目錄的根目錄

# 1) O=

# 使用 "make O=dir/to/store/output/files/"

#

# 2) 設置 KBUILD_OUTPUT

# 設置環境變數 KBUILD_OUTPUT 來指定輸出檔存放的目錄

# export KBUILD_OUTPUT=dir/to/store/output/files/

# make

#

# 但是 O= 方式優先於KBUILD_OUTPUT環境變數方式



# KBUILD_SRC 會再obj目錄的make調用

# KBUILD_SRC 到目前還不是為了給一般用戶使用的

ifeq ($(KBUILD_SRC),)



ifdef O

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

KBUILD_OUTPUT := $(O)

endif

endif



# 缺剩目標

PHONY := _all

_all:



ifneq ($(KBUILD_OUTPUT),)

# 調用輸出目錄的第二個make, 傳遞相關變數檢查輸出目錄確實存在



saved-output := $(KBUILD_OUTPUT)

KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)

$(if $(KBUILD_OUTPUT),, \

$(error output directory "$(saved-output)" does not exist))



PHONY += $(MAKECMDGOALS)



$(filter-out _all,$(MAKECMDGOALS)) _all:

$(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \

KBUILD_SRC=$(CURDIR) \

KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@



# Leave processing to above invocation of make

skip-makefile := 1

endif # ifneq ($(KBUILD_OUTPUT),)

endif # ifeq ($(KBUILD_SRC),)



ifeq ($(skip-makefile),)



PHONY += all

ifeq ($(KBUILD_EXTMOD),)

_all: all

else

_all: modules

endif



srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))

TOPDIR := $(srctree)

# FIXME - TOPDIR is obsolete, use srctree/objtree

objtree := $(CURDIR)

src := $(srctree)

obj := $(objtree)



VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))



export srctree objtree VPATH TOPDIR





# SUBARCH 告訴 usermode build 當前的機器是什麼體系. 它是第一個設置的,並且如果

# 是usermode build ,命令行中的 "ARCH=um" 優先下面的 ARCH 設置. 如果是 native build ,

# 設置ARCH , 獲取正常的數值, 將會忽略SUBARCH .



SUBARCH := $(shell uname -m
sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \

-e s/arm.*/arm/ -e s/sa110/arm/ \

-e s/s390x/s390/ -e s/parisc64/parisc/ \

-e s/ppc.*/powerpc/ -e s/mips.*/mips/ )



# 交叉編譯和選擇不同的gcc/bin-utils

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

#

# 當交叉編譯其他體系的內核時,ARCH 應該設置為目標體系.

# ARCH 可以再make執行間設置:

# make ARCH=ia64

# 另外一種方法是設置 ARCH 環境變數.

# 默認 ARCH 是當前執行的宿主機.



# CROSS_COMPILE 作為編譯時所有執行需要使用的首碼

# 只有gcc 和 相關的 bin-utils 使用 $(CROSS_COMPILE)設置的首碼.

# CROSS_COMPILE 可以再命令行設置:

# make CROSS_COMPILE=ia64-linux-

# 另外 CROSS_COMPILE 也可以再環境變數中設置.

# CROSS_COMPILE 的預設值是空的

# Note: 一些體系的 CROSS_COMPILE 是再其 arch/*/Makefile中設置的



ARCH ?= $(SUBARCH)

CROSS_COMPILE ?=



# Architecture as present in compile.h

UTS_MACHINE := $(ARCH)



KCONFIG_CONFIG ?= .config



# SHELL used by kbuild

CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \

else if [ -x /bin/bash ]; then echo /bin/bash; \

else echo sh; fi ; fi)



HOSTCC = gcc

HOSTCXX = g++

HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer

HOSTCXXFLAGS = -O2



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

# Normally, just do built-in.



KBUILD_MODULES :=

KBUILD_BUILTIN := 1



# 如果僅編譯模組 "make modules", 就不會編譯內建的 objects.

# 當使用modversions編譯 modules 時 , 就需要考慮build-in objects,

# 目的是再記錄前確認 checksums 已經更新.



ifeq ($(MAKECMDGOALS),modules)

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

endif



# If we have "make 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



# Beautify output

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

#

# 一般,make再執行命令前會列印整個命令資訊. 現在通過$($(quiet)$(cmd))方式

# 可以設置 $(quiet) 來選擇不同的命令輸出方式等.

#

# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@

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

#

# 如果 $(quiet) 為空, 整個命令行將會列印.

# 如果 $(quiet)設置為 "quiet_", 只列印簡短的版本資訊.

# 如果 $(quiet)設置為 "silent_", 將不會列印任何資訊, 因為沒有$(silent_cmd_cc_o_c) 變數存在.

#

# 一個簡單的首碼 $(Q)放到命令前面,以便再 non-verbose 模式下可以隱藏命令:

#

# $(Q)ln $@ :<

#

# 如果 KBUILD_VERBOSE 等於 0 ,那麼上面的命令將隱藏.

# 如果 KBUILD_VERBOSE 等於 1 ,那麼上面的命令將顯示.



ifeq ($(KBUILD_VERBOSE),1)

quiet =

Q =

else

quiet=quiet_

Q = @

endif



# 如果make -s (silent mode), 不會顯示命令



ifneq ($(findstring s,$(MAKEFLAGS)),)

quiet=silent_

endif



export quiet Q KBUILD_VERBOSE





# Look for make include files relative to root of kernel src

MAKEFLAGS += --include-dir=$(srctree)



# We need some generic definitions.

include $(srctree)/scripts/Kbuild.include



# Make variables (CC, etc...)



AS = $(CROSS_COMPILE)as

LD = $(CROSS_COMPILE)ld

CC = $(CROSS_COMPILE)gcc

CPP = $(CC) -E

AR = $(CROSS_COMPILE)ar

NM = $(CROSS_COMPILE)nm

STRIP = $(CROSS_COMPILE)strip

OBJCOPY = $(CROSS_COMPILE)objcopy

OBJDUMP = $(CROSS_COMPILE)objdump

AWK = awk

GENKSYMS = scripts/genksyms/genksyms

DEPMOD = /sbin/depmod

KALLSYMS = scripts/kallsyms

PERL = perl

CHECK = sparse



CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF)

MODFLAGS = -DMODULE

CFLAGS_MODULE = $(MODFLAGS)

AFLAGS_MODULE = $(MODFLAGS)

LDFLAGS_MODULE = -r

CFLAGS_KERNEL =

AFLAGS_KERNEL =





# Use LINUXINCLUDE when you must reference the include/ directory.

# Needed to be compatible with the O= option

LINUXINCLUDE := -Iinclude \

$(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \

-include include/linux/autoconf.h



CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE)



CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \

-fno-strict-aliasing -fno-common

AFLAGS := -D__ASSEMBLY__



# Read KERNELRELEASE from include/config/kernel.release (if it exists)

KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)

KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)



export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION

export ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC

export CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE

export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS



export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS

export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE

export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE



# When compiling out-of-tree modules, put MODVERDIR in the module

# tree rather than in the kernel tree. The kernel tree might

# even be read-only.

export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions



# Files to ignore in find ... statements



RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o

export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git

[未完,待續]

# ===========================================================================

# 下面是設置config和build 內核時共同使用的規則



# scripts裏面是最基本的幫助builds,在scripts/basic目錄下的makefile是所有build時都會用到的工具:fixdep/

PHONY += scripts_basic

scripts_basic:

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



# 把對scripts/basic/makefile轉化到scripts_basic處理

scripts/basic/%: scripts_basic ;



#這裏先說說script目錄裏面的makefile檔作用

#makefile:配置目標,包括編譯島內核和模組方式的target

#makefile_clean:當然是刪除了

#kbuild_include:一般的配置選項,會在makefile_build中調用

#makefile_build:build配置

#makefile_lib:module,vmlinux的配置

#makefile_host:配置binary

#makefile_modinst:安裝module

#makefile_modpost:



PHONY += outputmakefile

# outputmakefile規則目的是在輸出目錄產生一個makefile檔。這會為make提供方便。這裏是調用scripts/mkmakefile創建makfile檔

outputmakefile:

ifneq ($(KBUILD_SRC),)

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

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

endif



# 設置是.config,還是module,還是build命令



no-dot-config-targets := clean mrproper distclean \

cscope TAGS tags help %docs check% \

include/linux/version.h headers_% \

kernelrelease kernelversion



config-targets := 0

mixed-targets := 0

dot-config := 1



ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)

ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)

dot-config := 0

endif

endif



ifeq ($(KBUILD_EXTMOD),)

ifneq ($(filter config %config,$(MAKECMDGOALS)),)

config-targets := 1

ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)

mixed-targets := 1

endif

endif

endif



ifeq ($(mixed-targets),1)

# ===========================================================================

# We're called with mixed targets (*config and build targets).

# Handle them one by one.



%:: FORCE

$(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@



else

ifeq ($(config-targets),1)

# ===========================================================================

#僅配置linux



# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.

# KBUILD_DEFCONFIG may point out an alternative default configuration

# used for 'make defconfig'

include $(srctree)/arch/$(ARCH)/Makefile

export KBUILD_DEFCONFIG



config %config: scripts_basic outputmakefile FORCE

$(Q)mkdir -p include/linux include/config

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



else

# ===========================================================================

# 僅編譯內核



ifeq ($(KBUILD_EXTMOD),)

# Additional helpers built in scripts/

# Carefully list dependencies so we do not try to build scripts twice

# in parallel

PHONY += scripts

scripts: scripts_basic include/config/auto.conf

$(Q)$(MAKE) $(build)=$(@)



#下麵就不用說了吧

# Objects we will link into vmlinux / subdirs we need to visit

init-y := init/

drivers-y := drivers/ sound/

net-y := net/

libs-y := lib/

core-y := usr/

endif # KBUILD_EXTMOD



ifeq ($(dot-config),1)

# Read in config

-include include/config/auto.conf



ifeq ($(KBUILD_EXTMOD),)

# Read in dependencies to all Kconfig* files, make sure to run

# oldconfig if changes are detected.

-include include/config/auto.conf.cmd



# To avoid any implicit rule to kick in, define an empty command

$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;



# If .config is newer than include/config/auto.conf, someone tinkered

# with it and forgot to run make oldconfig.

# if auto.conf.cmd is missing then we are probably in a cleaned tree so

# we execute the config step to be sure to catch updated Kconfig files

include/config/auto.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd

$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

else

# external modules needs include/linux/autoconf.h and include/config/auto.conf

# but do not care if they are up-to-date. Use auto.conf to trigger the test

PHONY += include/config/auto.conf



include/config/auto.conf:

$(Q)test -e include/linux/autoconf.h -a -e $@

( \

echo; \

echo " ERROR: Kernel configuration is invalid."; \

echo " include/linux/autoconf.h or $@ are missing."; \

echo " Run 'make oldconfig && make prepare' on kernel src to fix it."; \

echo; \

/bin/false)



endif # KBUILD_EXTMOD



else

# Dummy target needed, because used as prerequisite

include/config/auto.conf: ;

endif # $(dot-config)





# ===========================================================================

# 僅編譯內核



ifeq ($(KBUILD_EXTMOD),)

# Additional helpers built in scripts/

# Carefully list dependencies so we do not try to build scripts twice

# in parallel

PHONY += scripts

scripts: scripts_basic include/config/auto.conf

$(Q)$(MAKE) $(build)=$(@)



#下麵就不用說了吧

# Objects we will link into vmlinux / subdirs we need to visit

init-y := init/

drivers-y := drivers/ sound/

net-y := net/

libs-y := lib/

core-y := usr/

endif # KBUILD_EXTMOD



.....



all: vmlinux



#配置gcc選項

ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE

CFLAGS += -Os

else

CFLAGS += -O2

endif

....



# 缺剩的編譯的內核鏡像

export KBUILD_IMAGE ?= vmlinux



#

# vmlinux與map安裝路徑,默認時boot目錄

export INSTALL_PATH ?= /boot



#

#module安裝目錄

#



MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)

export MODLIB



#

# 如果定義了INSTALL_MOD_STRIP,在安裝module後會strip這些安裝的modules



ifdef INSTALL_MOD_STRIP

ifeq ($(INSTALL_MOD_STRIP),1)

mod_strip_cmd = $(STRIP) --strip-debug

else

mod_strip_cmd = $(STRIP) $(INSTALL_MOD_STRIP)

endif # INSTALL_MOD_STRIP=1

else

mod_strip_cmd = true

endif # INSTALL_MOD_STRIP

export mod_strip_cmd



#如果不是安裝module

ifeq ($(KBUILD_EXTMOD),)

core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/



vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \

$(core-y) $(core-m) $(drivers-y) $(drivers-m) \

$(net-y) $(net-m) $(libs-y) $(libs-m)))



vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \

$(init-n) $(init-) \

$(core-n) $(core-) $(drivers-n) $(drivers-) \

$(net-n) $(net-) $(libs-n) $(libs-))))



init-y := $(patsubst %/, %/built-in.o, $(init-y))

core-y := $(patsubst %/, %/built-in.o, $(core-y))

drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))

net-y := $(patsubst %/, %/built-in.o, $(net-y))

libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))

libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))

libs-y := $(libs-y1) $(libs-y2)



# Build vmlinux

...



#配置syms

...



#以下是內核link時的配置

....


沒有留言:

張貼留言