Linux核心的Makefile分為5個部分:
Makefile 頂層Makefile,它讀取.config檔案,並負責建立vmlinux(核心鏡像)和modules(模組檔案)。
.config 核心設定檔,調用make menuconfig或者make xconfig命令等配置
arch/$(ARCH)/Makefile 核心相關,具體架構的Makefile
scripts/Makefile.* 公用編譯規則定義檔案。所有kbuild Makefile的規則,它們包含了定義/規則等。
kbuild Makefiles 每個子目錄都有kbuild Makefile,它們負責產生built-in或模組化目標。(注意:kbuild Makefile是指使用kbuild結構的Makefile,核心中的大多數Makefile都是kbuild Makefile。)
頂層Makefile閱讀的.config檔案,而該檔案是由核心配置程式產生的。
頂層Makefile負責製作:vmlinux(核心檔案)與modules(模組檔案)。製作的過程主要是通過遞迴向下訪問子目錄的形式完成。並根據核心設定檔確定訪問哪些子目錄。頂層Makefile要原封不動的包含一具體架構的Makefile,其名字類似於 arch/$(ARCH)/Makefile。該架構Makefile向頂層Makefile提供其架構的特別資訊。
每一個子目錄都有一個Kbuild Makefile檔案,用來執行從其上層目錄傳遞下來的命令。
Kbuild Makefile從.config檔案中提取資訊,產生Kbuild完成核心編譯所需的檔案清單。
scripts/Makefile.*包含了所有的定義、規則等資訊。這些檔案被用來編譯基於kbuild
Makefile的核心
Makefile編譯流程
當使用者使用Linux的Makefile編譯核心版本時,Makefile的編譯流程如下:
使用命令列或者圖形介面組態工具,對核心進行裁減,產生.config設定檔
儲存核心版本資訊到 include/linux/version.h
產生符號連結 include/asm,指向實際目錄 include/asm-$(ARCH)
為最終目標檔案的產生進行必要的準備工作
遞迴進入 /init 、/core、 /drivers、 /net、 /lib等目錄和其中的子目錄來編譯產生所有的目標檔案
連結上述過程產生的目標檔案產生vmlinux,vmlinux存放在核心代碼樹的根目錄下
最後根據 arch/$(ARCH)/Makefile檔案定義的後期編譯的處理規則建立最終的映象bootimage,包括建立引導記錄、準備initrd映象和相關處理
Makefile關鍵規則和定義描述
1) 目標定義
目標定義是Makefile檔案的核心部分,目標定義通知Makefile需要產生哪些目標檔案、如何根據特殊的編譯選項連結目標檔案,同時控制哪些子目錄要遞迴進入進行編譯。
這個例子Makefile檔案位於/fs/ext2目錄 :
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o ioctl.o namei.o super.o symlink.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
ext2-$(CONFIG_EXT2_FS_SECURITY) += xattr_security.o
ext2-$(CONFIG_EXT2_FS_XIP) += xip.o
這表示與 ext2相關的目標檔案由 ext2-y定義的檔案清單組成,其中ext2-$(*)是由核心設定檔.config中的配置項決定,最終Makefile會在這個目錄下統一產生一個目標檔案ext2.o(由obj-$(CONFIG_EXT2_FS)決定)。其中obj-y表示為產生vmlinux檔案所需要的目標檔案集合,具體的檔案依賴於核心配置。
Makefile會編譯所有的$(obj-y)中定義的檔案,然後調用連結器將這些檔案連結到built-in.o 檔案中。最終built-in.o檔案通過頂層Makefile連結到vmlinux中。$(obj-y)的檔案順序很重要。列表檔案可以重複,檔案第一次出現時將會連結到built-in.o中,後來出現的同名檔案將會被忽略。檔案順序直接決定了他們被調用的順序。
所有包含在lib-y定義中的目標檔案都將會被編譯到該目錄下一個統一的庫檔案中。值得注意的是lib-y定義一般被限制在 lib 和arch/$(ARCH)/lib 目錄中。
體系makefile檔案和頂層makefile檔案共同定義了如何建立vmlinux檔案的規則。
$(head-y) 列舉首先連結到vmlinux的對象檔案。
$(libs-y) 列舉了能夠找到lib.a檔案的目錄。
其餘的變數列舉了能夠找到內嵌對象檔案的目錄。
$(init-y) 列舉的對象位於$(head-y)對象之後。
然後是如下位置順序:
$(core-y), $(libs-y), $(drivers-y) 和 $(net-y)。
頂層makefile定義了所有通用目錄,arch/$(ARCH)/Makefile檔案只需增加體系相關的目錄。
例如: #arch/i386/Makefile
libs-y += arch/i386/lib/
core-y += arch/i386/kernel/ \
arch/i386/mm/ \
arch/i386/$(mcore-y)/ \
arch/i386/crypto/
drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/
drivers-$(CONFIG_PCI) += arch/i386/pci/
…………………………………………
2) 目錄遞迴
Makefile檔案只負責目前的目錄下的目標檔案,子目錄中的檔案由子目錄中的makefile負責編譯,編譯系統使用obj-y 和obj-m來自動遞迴編譯各個子目錄中的檔案。
對於fs/Makefile:
obj-$(CONFIG_EXT2_FS) += ext2/
如果在核心設定檔.config中,CONFIG_EXT2_FS被設定為y或者m,則核心makefile會自動進入ext2目錄來進行編譯。核心 Makefile只使用這些資訊來決定是否需要編譯這個目錄,子目錄中的makefile規定哪些檔案編譯為模組,哪些檔案編譯進核心。
3) 依賴關係
Linux Makefile通過在編譯過程中產生的 .檔案名稱.o.cmd(比如對於main.c檔案,它對應的依賴檔案名稱為.main.o.cmd)來定義相關的依賴關係。
一般檔案的依賴關係由如下部分組成:
所有的前期依賴檔案(包括所有相關的*.c 和 *.h)
所有與CONFIG_選項相關的檔案
編譯目標檔案所使用到的命令列
4) 特殊規則
特殊規則使用在核心編譯需要規則定義而沒有相應定義的時候。典型的例子如編譯時間標頭檔的產生規則。其他例子有體系makefile編譯引導映像的特殊規則。特殊規則寫法同普通的makefile規則。
編譯器在makefile所在的目錄不能被執行,因此所有的特殊規則需要提供前提檔案和目標檔案的相對路徑。
定義特殊規則時將使用到兩個變數:
$(src): $(src)是對於makefile檔案目錄的相對路徑,當使用代碼樹中的檔案時使用該變數$(src)。
$(obj): $(obj)是目標檔案目錄的相對路徑。組建檔案使用$(obj)變數。
例如: #drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
這就是使用普通文法的特殊編譯規則。
目標檔案依賴於兩個前提檔案。目標檔案的首碼是$(obj), 前提檔案的首碼是$(src)(因為它們不是組建檔案)。
5) 引導映象
體系makefile檔案定義了編譯vmlinux檔案的目標對象,將它們壓縮和封裝成引導代碼,並複製到合適的位置。這包括各種安裝命令。在Linux中 Makefile無法為所有的體繫結構提供標準化的方法,因此常需要具體硬體體繫結構下makefile提供附加處理規則。
附加處理過程常位於arch/$(ARCH)/下的boot/目錄。
核心編譯體系無法在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)/$@
建議使用"$(Q)$(MAKE) $(build)=<dir>"方式在子目錄中調用make命令。
當執行不帶參數的make命令時,將首先編譯第一個目標對象。在頂層makefile中第一個目標對象是all:。
一個體繫結構需要定義一個預設的可引導映像。
增加新的前提檔案給all目標可以設定不同於vmlinux的預設目標對象。
例如: #arch/i386/Makefile
all: bzImage
當執行不帶參數的"make"命令時,bzImage檔案將被編譯。
6) 常用編譯命令
if_changed
如果必要,執行傳遞的命令。
用法:
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
當這條規則被使用時它將檢查哪些檔案需要更新,或命令列被改變。後面這種情況將迫使重新編譯編譯選項被改變的執行檔案。使用if_changed的目標對象必須列舉在$( builtin-target)中,否則命令列檢查將失敗,目標一直會編譯。
if_changed_dep
如果必要,執行傳遞的命令並更新依賴檔案。
用法:
%.o: %.S FORCE
$(call if_changed_dep,as_o_S)
當這條規則被使用時它將檢查哪些檔案需要更新,或命令列被改變。同時它會重新檢測依賴關係的改變並將產生新的依賴檔案。這是與if_changed命令的區別。
7) 定製命令
當正常執行帶編譯命令時命令的簡簡訊息會被顯示(要想顯示詳細的命令,請在命令列中加入V=1)。要讓定製命令具有這種功能需要設定兩個變數:
quiet_cmd_<command> - 將被顯示的內容
cmd_<command> - 被執行的命令
例如: #
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(BUILDFLAGS) \
$(obj)/vmlinux.bin > $@
targets += bzImage
$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: $@ is ready'
執行make命令編譯$(obj)/bzImage目標時將顯示:
BUILD arch/i386/boot/bzImage
8) 預先處理連結指令碼
當編譯vmlinux映像時將使用arch/$(ARCH)/kernel/vmlinux.lds連結指令碼。
相同目錄下的vmlinux.lds.S檔案是這個指令碼預先處理的變體。核心編譯系統知曉.lds檔案。並使用規則*lds.S -> *lds。
例如: #arch/i386/kernel/Makefile
always := vmlinux.lds
#Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
$(always)指派陳述式告訴編譯系統編譯目標是vmlinux.lds。$(CPPFLAGS_vmlinux.lds)
指派陳述式告訴編譯系統編譯vmlinux.lds目標的編譯選項。
編譯*.lds時將使用到下面這些變數:
CPPFLAGS : 定義在頂層Makefile
EXTRA_CPPFLAGS : 可以設定在編譯的makefile檔案中
CPPFLAGS_$(@F) : 目標編譯選項。注意要使用檔案全名。
9) 主機輔助程式的編譯
核心編譯系統支援在編譯階段編譯主機可執行程式。為了使用主機程式需要兩個步驟:第一個步驟使用hostprogs-y變數告訴核心編譯系統有主機程式可用。第二步給主機程式添加潛在的依賴關係。有兩種方法,在規則中增加依賴關係或使用$(always)變數。
10) Clean機制
clean命令清除在編譯核心產生的大部分檔案,例如主機程式,列舉在 $(hostprogs-y)、$(hostprogs-m)、$(always)、$(extra-y)和$(targets)中目標檔案都將被刪除。代碼目錄數中的"*.[oas]"、"*.ko"檔案和一些由編譯系統產生的附加檔案也將被刪除。
附加檔案可以使用$(clean-files)進行定義。
例如: #drivers/pci/Makefile
clean-files := devlist.h classlist.h
當執行"make clean"命令時, "devlist.h classlist.h"兩個檔案將被刪除。核心編譯系統預設這些檔案與makefile具有相同的相對路徑,否則需要設定以'/'開頭的絕對路徑。
刪除整個目錄使用以下方式:
例如: #scripts/package/Makefile
clean-dirs := $(objtree)/debian/
這樣就將刪除包括子目錄在內的整個debian目錄。如果不使用以'/'開頭的絕對路徑核心編譯系統見預設使用相對路徑。
通常核心編譯系統根據"obj-* := dir/"進入子目錄,但是在體系makefile中需要顯式使用如下方式:
例如: #arch/i386/boot/Makefile
subdir- := compressed/
上面指派陳述式指示編譯系統執行"make clean"命令時進入compressed/目錄。
在編譯最終的引導映像檔案的makefile中有一個可選的目標對象名稱是archclean。
例如: #arch/i386/Makefile
archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
當執行"make clean"時編譯器進入arch/i386/boot,arch/i386/boot中的makefile檔案可以使用subdir-標識進入更下層的目錄。
注意1: arch/$(ARCH)/Makefile不能使用"subdir-",因為它被包含在頂層makefile檔案中,在這個位置編譯機制是不起作用的。
注意2: 所有列舉在core-y、libs-y、drivers-y和net-y中的目錄將被"make clean"命令清除。