nux-2.6.x makefile
linux-2.6.20.6/Documentation/kbuild/makefiles.txt
一、 概述
linux的makefile有五個部分:
Makefile:頂層Makefile
.config:內核配置文件
arch/$(ARCH)/Makefile:體系結構相關的Makefile
scripts/Makefile.*:通用的規則等,用於所有的kbuild Makefiles
kbuild Makefiles:大約有500個這樣的makefile
頂層Makefile讀取在內核配置過程中生成的.config文件。負責構建兩個主要的文件:vmlinux和各模塊。它還包含了一個名爲arch/$(ARCH)/Makefile的體系結構相關Makefile,這個Makefile給頂層Makefile提供了體系結構相關的信息。
每個子目錄有一個kbuild Makefile,它執行上層傳入的命令。kbuild Makefile使用.config文件中的信息構建各種文件列表,供kbuild構建任何built-in或modular目標時使用。
scripts/Makefile.*包含所有的定義和規則等,這些被用來和kbuild makeflie一起構建內核
二、 kbuild文件
kbuild Makefile中的語法。
kbuild文件首選的文件名是”Makefile”,也可以用”kbuild”。如果”Makefile”和”kbuild”同時存在,則使用”kbuild”文件。
1、 目標定義
目標定義是kbuild Makefile的主要部分。這些行定義了要被編譯的文件、所有指定的編碼選項及所有將要遞歸進入的子目錄。
最簡單的kbuild Makefile包括一行:
obj-y += foo.o
如果foo.o要被編譯成模塊,就用obj-m。因此,經常使用下面的模式:
obj-$(CONFIG_FOO) += foo.o
$(CONFIG_FOO)的值是y (for built-in)或者m(for module)。如果CONFIG_FOO既不是y也不是m,這個文件不會被編譯或鏈接
2、 Built-in 對象目標 - obj-y
kbuild Makefile 在$(obj-y)列表中給vmlinux指定目標文件,這個列表依賴於內核配置。
kbuild 編譯所有的$(obj-y)文件,然後調用”$(LD) –r”把這些文件合併成一個built-in.o文件,之後built-in.o被頂層Makefile鏈接成vmlinux。
$(obj-y)中文件的順序很重要,在這個列表中,完全相同的文件可以同時存在:第一個被鏈接到built-in.o,以後的都被忽略了。
鏈接順序也很重要,因爲特定的函數(module_init() / __initcall)會在啓動期間按照它們出現的順序被調用。
*.c------>*.o------>build-in.o------->vmlinux
3、 可加載模塊目標 - obj-m
$(obj-m)指定被編譯爲可加載內核模塊的文件。一個模塊可以是一個或多個源文件編譯生成。如果是一個源文件,kbuild makefile只是簡單的把這個文件加入$(obj-m)。如果內核模塊由多個源文件編譯生成,內核要知道你想要編譯哪些源文件,所以你必須設置變量$(<module_name>-objs)來告訴內核。
eg.
#derivers/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識別用於編譯生成目標的對像。它允許Makefile使用CONFIG_符號的值來決定一個對像是否是某個目標的一部分。
eg.
#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的一部分。
4、 庫文件目標 - lib-y
所有列在lib-y中的對像都被組合到一個單獨的庫中。既列在obj-y中又列在lib-y中的對像不會被包含到庫中。因爲不管怎樣,它們都是可訪問的。爲了保持一致,列在lib-m中的對像會被包含到lib.a。
Example:
#arch/i386/lib/Makefile
lib-y := checksum.o delay.o
此處基於checksum.o和delay.o創建了庫lib.a.
5、 向下進入目錄
一個Makefile僅負責在它自己的目錄中編譯文件。子目錄中的文件由子目錄中的Makefile負責編譯。如果你告訴了編譯系統這些子目錄,它會自動在子目錄中遞歸調用make。
ext2放在了一個單獨的目錄中,在fs/目錄下的Makefile用正面的賦值告訴kbuild向下進入目錄
eg:
#fs/Makefile
obj-$(CONFIG_EXT2_FS) += ext2/
如果CONFIG_EXT2_FS的值是’y’(built-in)或’m’(modular),對應的obj-變量就會被設置,kbuild向下進入ext2目錄。
kbuild只是通過這個信息決定要訪問哪個目錄,子目錄中的Makefile指定什麼是modules、什麼是built-in。
6、編譯flags
EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
所有EXTRA_變量只用在它的定義所在的那個kbuild makefile,它們被用於kbuild makefile中所有命令的執行
$(EXTRA_CFLAGS)指定了用$(CC)編譯c文件時的選項。
$(EXTRA_AFLAGS)是每個目錄在編譯彙編源文件時的選項。
$(EXTRA_LDFLAGS) 和 $(EXTRA_ARFLAGS)是用於$(LD)和$(AR)的選項。
Example:
#arch/m68k/fpsp040/Makefile
EXTRA_LDFLAGS := -x
CFLAGS_$@, AFLAGS_$@
CFLAGS_$@ 和AFLAGS_$@ 只在當前kbuild makefile的命令中使用。
$(CFLAGS_$@)爲$(CC)指定了每個文件的選項。$@指定了使用它的文件。
Example:
# 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
這三行爲aha152.o、gdth.o和seagate.o指定了編譯選項。
$(AFLAGS_$@)對於彙編源文件有類似的作用
Example:
# arch/arm/kernel/Makefile
AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional
AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) –traditional
8、對依賴的追蹤
Kbuild追蹤下面的依賴:
1)、所有必備的文件(*.c和*.h)
2)、用在所有必備文件中的CONFIG_選項
3)、用於編譯目標的命令行
因此,如果改變了$(CC)的選項,所有受影響的文件都會被重新編譯。
9、特殊規則
如果kbuild的基本組織沒提供必要的支持,特殊規則就會被使用。典型的例子就是在編譯過程中產生頭文件。另一個例子就是體系結構相關的Makefile,爲了準備啓動映像,它需要特殊的規則。
Kbuild不在Makefile所在的目錄中執行,所以所有的特殊規則應拱必備文件和目標文件的相對路徑。定義特殊規則時要用到兩個變量:
$(src)
$(src)是指向Makefile所在目錄的相對路徑。涉及到源目錄樹中的文件總會用到$(src)。
$(obj)
$(obj)是指向保存目標的目錄的相對路徑。涉及到生成文件時總會用到$(obj)。
Example:
#drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
10、$(CC)支持函數
內核可能用好幾個不同版本的$(CC)進行編譯,每個版本都支持一組唯一的特徵和選項。kbuild提供了基本的支持用於檢測有效的$(CC)選項。
as-option
as-option用於檢測$(CC)是否支持給定式的選項(在編譯彙編文件*.S時)。如果不支持第一個選項,將會指定一個可供選擇的第二選項。
Example:
#arch/sh/Makefile
cflags-y += $(call as-option,-Wa$(comma)-isa=$(isa-y),)
在上例中,如果$(CC)支持-Wa$(comma)-isa=$(isa-y),cflags-y就等於-Wa$(comma)-isa=$(isa-y)。第二個參數是可選的,如果 不支持第一個參數,而又提供了第二個參數,那麼第二個參數就會被使用。
ld-option
ld-option用於在鏈接時檢測$(CC)是否支持給定的選項。如果不支持第一個,可能會指定一個可選的第二選項。
as-instr
as-instr檢測彙編器是否報告一個特殊指令並輸出選項1或選項2。
cc-option
cc-option用來檢測$(CC)是否支持一個給定式的選項,並且不支持一個可選的第二選項。
Example:
#arch/i386/Makefile
cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)
上例中,如果$(CC)支持,cflags-y會被賦值爲選項-march=pentium-mmx,否則賦值爲-march=i586。對於cc-option,第二個參數是可選的,如果被優化了,當不支持第一個選項時,cflags-y將不會被賦值。
cc-option-yn
cc-option-yn檢測gcc是否支持一個給定的選項,如果支持,返回’y’,否則,返回’n’。
Example:
#arch/ppc/Makefile
biarch := $(call cc-option-yn, -m32)
aflags-$(biarch) += -a32
cflags-$(biarch) += -m32
cc-option-align
gcc 3.0及其以上的版本改變了選項的類型,這些選項用於指定函數、循環等的排列。當被用作排列選項的前綴時,$(cc-option-align)會選擇正確的前綴
gcc < 3.00
cc-option-align = -malign
gcc >= 3.00
cc-option-align = -falign
Example:
CFLAGS += $(cc-option-align)-functions=4
上例中,gcc >=3.00時,使用-falign-functions=4。gcc < 3.00時,使用用-malign-functions=4.
cc-version
cc-version返回$(CC)編譯器版本的數字版本。
cc-ifversion
cc-ifversion檢測$(CC)的版本,如果版本表達式爲真,其返回值就是最後一個參數。
三、 主機程序支持
Kbuild支持在主機上編譯生成可執行文件,用於編譯階段。爲了使用一個主機可執行程序,需要如下兩步:
第一步,告訴kbuild存在一個主機程序。這是利用變量hostprogs-y來做的
第二步,給這個可執行程序添加一個直接的依賴。可以用兩種方法做到:在規則中添加依賴或者利用變量$(always)添加。
這兩種可能會在下面描述。
1、 簡單主機程序
有些情況下,需要在主機上編譯並運行一個程序。下面這行告訴kbuild程序bin2hex將在主機上被編譯。
Example:
hostprogs-y := bin2hex
上例中,kbuild假定bin2hex是由當前Makefile所在目錄中的一個c文件bin2hex.c編譯生成的。
2、 組合主機程序
主機程序可以通過組合目標來構建。$(<executable>-objs)列出了所有對像,這些對像被鏈接成最終的可執行程序。
Example:
#scripts/lxdialog/Makefile
hostprogs-y := lxdialog
lxdialog-objs := checklist.o lxdialog.o
3、 定義共享庫
Kbuild提供對共享庫的支持,但用法會受限制。下例中,用libkconfig.so共享庫鏈接生成可執行文件conf.
Example:
#scripts/kconfig/Makefile
hostprogs-y := conf
conf-objs := conf.o libkconfig.so
libkconfig-objs := expr.o type.o
共享庫總是需要一個對應的-objs行,上例中libkconfig共享由expr.o和type.o組合而成。expr.o和type.o會被編譯成位置無關代碼,並被鏈接成共享庫libkconfig.so。c++不支持共享庫。
4、 把c++用於主機程序
kbuild提供了對c++主機程序的支持。
Example:
#scripts/kconfig/Makefile
hostprogs-y := qconf
qconf-cxxobjs := qconf.o
上例中可執行程序由c++文件qconf.cc(由$(qconf-cxxobjs)標識)組合生成.
如果qconf由.c和.cc文件混合組成,那就要再添加一行
Example:
#scripts/kconfig/Makefile
hostprogs-y := qconf
qconf-cxxobjs := qconf.o
qconf-objs := check.o
5、 爲主機程序控制編譯選項
編譯主機程序時,會用$(HOSTCC),並傳遞由$(HOSTCFLAGS)指定的選項。爲了設置可以影響所有在那個Makefile中創建的主機程序的標記,可以使用變量HOST_EXTRACFLAGS。
Example:
#scripts/lxdialog/Makefile
HOST_EXTRACFLAGS += -I/usr/include/ncurses
給單個文件設置特殊的標記:
Example:
#arch/ppc64/boot/Makefile
HOSTCFLAGS_piggyback.o := -DKERNELBASE=$(KERNELBASE)
也可以給鏈接器指定附加選項
Example:
#scripts/kconfig/Makefile
HOSTLOADLIBES_qconf := -L$(QTDIR)/lib
6、 何時主機程序被編譯
只有在主機程序被當作依賴引用時,Kbuild纔會編譯主機程序
有兩種方法:
1)、在一個特殊的規則中直接列出依賴
Example:
#drivers/pci/Makefile
hostprogs-y := gen-devlist
$(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist
( cd $(obj); ./gen-devlist ) < $<
在$(obj)/gen-devlist被更新之前,不會編譯生成$(obj)/devlist.h。注意,在特殊規則中引用主機程序必須加前綴$(obj).
2)、使用$(always)
當沒有合適的特殊規則,而在進入makefile時主機程序要被編譯,這時就要用$(always)變量。
Example:
#scripts/lxdialog/Makefile
hostprogs-y := lxdialog
always := $(hostprogs-y)
這裏告訴kbuild,即使沒有在任何規則中被引用,也編譯lxdialog。
7、 使用hostprog-$(CONFIG_FOO)
Kbuild文件中一個典型的模式如下
Example:
#scripts/Makefile
hostprogs-$(CONFIG_KALLSYMS) += kallsyms
Kbuild知道’y’對應built-in,’m’對應module。所以如果一個配置符號賦值爲’m’,kbuild依然會編譯生成二進制碼。換句話說,kbuild把hostprogs-m和hostprogs-y作同樣處理。在沒有包括CONFIG符號時,只推薦使用hostprogs-y.
四、 Kbuild的清除結構組織
“make clean”會刪除大部分生成的文件,這些文件在編譯內核的目錄樹中。包括主機程序。Kbuild知道列在$(hostprogs-y), $(hostprogs-m), $(always),$(extra-y) 和 $(targets)中的目標。它們都會在”make clean”時被刪除。符合”*.[oas]”、”*.ko”模式的文件,加上一些由kbuild生成的附加文件,都會在執行”make clean”時,從整個內核源碼樹中被刪除。
注意:
1)、arch/$(ARCH)/Makefile不能使用”subdir-”,因爲這個文件被包含在頂層Makefile中,而kbuild結構組織在那裏是不運作的。
2)、”make lcean”時,所有下個列在core-y, libs-y, drivers-y 和 net-y中的目錄都會被訪問。
五、 體系結構Makefile
在開始向下進入各單獨的目錄前,頂層目錄設置了環境變量,並做了準備工作。頂層Makefile包含了通用的部分,而arch/$(ARCH)/Makefile爲上述體系結構設置kbuild所要用的內容。因此arch/$(ARCH)/Makefile設置了大量的變量並定義少量的目標。
執行kbuild時,會有以下步驟:
1)、內核的配置=>生成.config
2)、在include/linux/version.h中存儲內核版本
3)、include/asm到include/asm-$(ARCH)的符號鏈接
4)、更新所有目標的其他依賴:――附加的依賴在arch/$(ARCH)/Makefile中指定。
5)、遞歸向下進入所有列在init-* core* drivers-* net-* libs-*中的目錄,並編譯所有目標。――上述變量的值在arch/$(ARCH)/Makefile中擴展
6)、然後所有目標文件被鏈接,生成的文件vmlinux放在obj目錄樹的根目錄中。最先被鏈接的目標列在head-y中,head-y由arch/$(ARCH)/Makefile賦值。
7)、最後,體系結構相關的部分做了所有必需的處理,並編譯生成了最終的啓動映像。包括編譯引導記錄,準備initrd映像及相關文件。
1、 設置變量,把編譯轉向具體某個體系結構
LDFLAGS Generic $(LD) options
調用鏈接器時用到的標記。通常指定emulation就足夠了
注意:EXTRA_LDFLAGS和LDFLAGS_$@可用來進一步定製用到的標記。後面會提到。
LDFLAGS_MODULE 鏈接模塊時$(LD)的選項。默認是”-r”,用於生成可重定位輸出文件。
LDFLAGS_vmlinux 鏈接vmlinux時$(LD)用到的選項。LDFLAGS_vmlinux要用到LDFLAGS_$@的支持。
OBJCOPYFLAGS objcopy 標記。
當用$(call if_changed,objcopy)轉換.o文件時,會用到由OBJCOPYFLAGS指定的標記。$(call if_changed,objcopy)通常用來生成一個基於vmlinux的原始的二進制文件。
Example:
#arch/s390/Makefile
OBJCOPYFLAGS := -O binary
#arch/s390/boot/Makefile
$(obj)/image: vmlinux FORCE
$(call if_changed,objcopy)
AFLAGS 用於$(AS)的彙編程序標記
默認值在頂層Makefile,各體系結構中根據情況做些添加修改
CFLAGS $(CC)編譯標記
默認值在頂層Makefile,各體系結構中根據情況做些添加修改
CFLAGS_KERNEL 爲built-in指定的$(CC)選項。$(CFLAGS_KERNEL)包含編譯固定內核代碼時用的額外的編譯標記
CFLAGS_MODULE $(CC)編譯模塊時用到的編譯選項,$( CFLAGS_MODULE)包含編譯可加載內核模塊代碼時用的額外的編譯標記
2、 向archprepare添加依賴
archprepare:在開始向下進入各子目錄前,用規則列出需要編譯的依賴。
這個經常用在包含彙編常量的頭文件
Example:
#arch/arm/Makefile
archprepare: maketools
上例中,maketools在向下進入各子目錄前會被處理好
3、 向下進入時,列出要訪問的目錄
體系結構相關Makefile和頂層Makefile結合定義了一些變量,這些變量指定指出怎樣編譯vmlinux文件。對於模塊,沒有對應的體系結構相關部分;模塊編譯機制是完全體系結構無關的。
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只添加了體系結構相關的目錄。
Example:
#arch/sparc64/Makefile
core-y += arch/sparc64/kernel/
libs-y += arch/sparc64/prom/ arch/sparc64/lib/
drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/
4、 體系結構相關引導映像
體系結構相關Makefile指定生成vmlinux文件的目標,然後壓縮vmlinux,把它封裝成引導代碼,並把最終的文件複製到某個位置。
通常把所有附加處理放在arch/$(ARCH)/下的boot/目錄中。
Kbuild並不提供智能的方法來支持編譯在boot/目錄中指定的目標。因此,arch/$(ARCH)/Makefile要手工調用make在boot/目錄中編譯目標。
Example:
#arch/i386/Makefile
boot := arch/i386/boot
bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
推薦用"$(Q)$(MAKE) $(build)=<dir>"在子目錄中調用make。
沒有命名體系結構相關目標的規則,但是執行”make help”會列出所有相關的目標。要支持這個,必須定義$(archelp)
Example:
#arch/i386/Makefile
define archhelp
echo '* bzImage - Image (arch/$(ARCH)/boot/bzImage)'
endif
如果不帶參數執行make,遇到的第一個目標會被編譯。頂層Makefile中第一個出現的目標是all: 。
每種體系結構總要默認的編譯一個可引導映像。執行”make help”時,默認目標用”*”突出顯示。給all:添加一個新的依賴來選擇不同於vmlinux的默認目標
Example:
#arch/i386/Makefile
all: bzImage
5、 編譯非內核目標
extra-y
extra-y指定了在當前目標下創建的附加目標, obj-*指定的目標除外。
有兩種情況要列出extra-y中所有的目標:
1)、讓kbuild能夠檢測命令行的改變――使用$(call if_changed,xxx)時
2)、執行”make clean”時,kbuild知道要刪除什麼文件。
Example:
#arch/i386/kernel/Makefile
extra-y := head.o init_task.o
上例中,用extra-y列出需要被編譯但不用鏈接到built-in.o中的目標文件
6、 對編譯引導映像有用的命令
Kbuild提供了少量的宏,這些宏在編譯引導映像時很有用。
if_changed
if_changed是以下命令用到的基本組織結構。
Usage:
target: source(s) FORCE
$(call if_changed,ld/objcopy/gzip)
在求這個規則的值時,會檢查是否有文件需要更新,或者因爲最後一次調用使得命令行已經改變。接下來,如果可執行程序的選項被改變了,會強制進行重編譯。
任何使用if_changed的目標必須在$(targets)中列出,否則命令行檢測會失敗,目標總會被編譯。
注意,忘記依賴是一個典型的錯誤。另外一個常見的缺陷是空格有時也很重要,例如,下面會出錯(注意逗號後的空格):
target: source(s) FORCE
#WRONG!# $(call if_changed, ld/objcopy/gzip)
ld
鏈接目標。通常會用LDFLAGS_$@爲ld設置特定選項
objcopy
拷貝二進制碼,通常在arch/$(ARCH)/Makefile中指定OBJCOPYFLAGS。可以用OBJCOPYFLAGS_$@設置附加選項。
gzip
壓縮目標。最大限度壓縮目標。
Example:
#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)
$(targets)被賦予了所有潛在的目標,kbuild會通過它知道目標並進行如下操作:
1)、檢查命令行的改變
2)在make clean時刪除目標
注意:忘記”target :=”賦值操作是常見的錯誤,這將導致目標文件無條件重新編譯。
7、 自定義kbuild命令
在KBUILD_VERBOSE=0的情況下執行kbuild,只有一個命令的簡寫正常顯示。
kbuild需要設置兩個變量來打開自定義命令的這種行爲:
quiet_cmd_<command> -- 要被回顯的
cmd_<command> -- 要執行的命令
Example:
#
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'
當更新目標$(obj)/bzImage時,
BUILD arch/i386/boot/bzImage
會在”make KBUILD_VERBOSE=0”時顯示。
8、 預處理鏈接腳本
當編譯vmlinux映像時,使用arch/$(ARCH)/kernel/vmlinux.lds這個鏈接腳本。這個腳本是同一目標下文件vmlinux.lds.S的預處理變量。kbuild知道.lds文件,幷包含了一個*lds.S到*lds的規則
Example:
#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 : 在頂層Makefile中設置
EXTRA_CPPFLAGS : 可能在 kbuild makefile中設置
CPPFLAGS_$(@F) : 目標相關標記. 注意,在這個賦值中使用全名
zz linux 2.6.x Makefile
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.