本篇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需要知道你所編譯的模組是基於哪些檔,所以你需要通過變數 $(
例子:
#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) 編譯器的版本號。
其格式是:
例子:
#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 複合的本機程式
本機程式可以由多個檔編譯而成。
所使用的語法與內核的相應語法很相似。
$(
例子:
#scripts/lxdialog/Makefile
hostprogs-y := lxdialog
lxdialog-objs := checklist.o lxdialog.o
副檔名為.o的文件是從相應的.c檔編譯而來的。在上面的例子中,checklist.c 編譯成了checklist.o,lxdialog.c編譯成了lxdialog.o。最後,兩個.o檔聯接成了一可執行檔,lxdialog。注意:語法
--- 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
# 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時的配置
....
沒有留言:
張貼留言