linux2.6内核Makefile详解
生活随笔
收集整理的這篇文章主要介紹了
linux2.6内核Makefile详解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
熟悉內核的Makefile對開發設備驅動、理解內核代碼結構都是非常重要的
linux2.6內核Makefile的許多特性和2.4內核差別很大,在內核目錄的documention/kbuild/makefiles.txt中有詳細的說明。給大家一個中文版的翻譯
=== 目錄
??????? === 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需要知道你所編譯的模塊是基于哪些文件,所以你需要通過變量
??? $(-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輸出以下變量:
??? 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,
修改:kai Germaschewski?
????? Sam Ravnborg?
=== 10 TODO
- 描述Kbuild是如何用 _shipped 來支持 shipped 文件的。
- 生成分支頭文件
- 在第7節加入更多的變量
linux2.6內核Makefile的許多特性和2.4內核差別很大,在內核目錄的documention/kbuild/makefiles.txt中有詳細的說明。給大家一個中文版的翻譯
=== 目錄
??????? === 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需要知道你所編譯的模塊是基于哪些文件,所以你需要通過變量
??? $(-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輸出以下變量:
??? 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,
修改:kai Germaschewski?
????? Sam Ravnborg?
=== 10 TODO
- 描述Kbuild是如何用 _shipped 來支持 shipped 文件的。
- 生成分支頭文件
- 在第7節加入更多的變量
總結
以上是生活随笔為你收集整理的linux2.6内核Makefile详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Asterisk 并发量到100后就出现
- 下一篇: 深圳补贴新能源汽车:最高2万、搭载鸿蒙系