Linux內核Makefile文件

=== 目錄
     === 1 概述
     === 2 角色分工
     === 3 內核編譯文件
        --- 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 特殊規則
     === 4 輔助程序
        --- 4.1 簡單輔助程序
        --- 4.2 組合輔助程序
        --- 4.3 定義共享庫 
        --- 4.4 C++語言使用方法
        --- 4.5 輔助程序編譯控制選項
        --- 4.6 何時建立輔助程序
        --- 4.7 使用hostprogs-$(CONFIG_FOO)
     === 5 編譯清除機制
     === 6 體系Makefile文件
        --- 6.1 變量設置
        --- 6.2 增加預設置項
        --- 6.3 目錄表
        --- 6.4 引導映像
        --- 6.5 編譯非內核目標
        --- 6.6 編譯引導映像命令
        --- 6.7 定製編譯命令
        --- 6.8 預處理連接腳本
        --- 6.9 $(CC)支持功能
     === 7 Kbuild變量
     === 8 Makefile語言
     === 9 Credits
     === 10 TODO

=== 1 概述
Makefile包括五部分:
     Makefile            頂層Makefile文件
     .config                  內核配置文件
     arch/$(ARCH)/Makefile      機器體系Makefile文件
     scripts/Makefile.*      所有內核Makefiles共用規則
     kbuild Makefiles      其它makefile文件
通過內核配置操作產生.config文件,頂層Makefile文件讀取該文件的配置。頂層Makefile文件負責產生兩個主要的程序:vmlinux (內核image)和模塊。頂層Makefile文件根據內核配置,通過遞歸編譯內核代碼樹子目錄建立這兩個文件。頂層Makefile文件文本一個名爲arch/$(ARCH)/Makefile的機器體系makefile文件。機器體系Makefile文件爲頂層makefile文件提供與機器相關的信息。每一個子目錄有一個makefile文件,子目錄makefile文件根據上級目錄makefile文件命令啓動編譯。這些makefile使用.config文件配置數據構建各種文件列表,並使用這些文件列表編譯內嵌或模塊目標文件。scripts/Makefile.*包含了所有的定義和規則,與makefile文件一起編譯出內核程序。

=== 2 角色分工
人們與內核makefile存在四種不同的關係:
*用戶* 用戶使用"make menuconfig"或"make"命令編譯內核。他們通常不讀或編輯內核makefile文件或其他源文件。
*普通開發者* 普通開發者維護設備驅動程序、文件系統和網絡協議代碼,他們維護相關子系統的makefile文件,因此他們需要內核makefile文件整體性的一般知識和關於kbuild公共接口的詳細知識。
*體系開發者* 體系開發者關注一個整體的體系架構,比如sparc或者ia64。體系開發者既需要掌握關於體系的makefile文件,也要熟悉內核makefile文件。
*內核開發者* 內核開發者關注內核編譯系統本身。他們需要清楚內核makefile文件的所有方面。
本文檔的讀者對象是普通開發者和系統開發者。

=== 3 內核編譯文件
內核中大多數makefile文件是使用kbuild基礎架構的makefile文件。本章介紹kbuild的makefile中的語法。
3.1節“目標定義”是一個快速導引,後面各章有詳細介紹和實例。
--- 3.1 目標定義
     目標定義是makefile文件的主要部分(核心)。這些目標定義行定義瞭如何編譯文件,特殊的兼容選項和遞歸子目錄。
      最簡單的makefile文件只包含一行:
     Example: obj-y += foo.o
    這行告訴kbuild在該目錄下名爲foo.o的目標文件(object),foo.o通過編譯foo.c或者foo.S而得到。
    如果foo.o編譯成一個模塊,則使用obj-m變量,因此常見寫法如下:
     Example: obj-$(CONFIG_FOO) += foo.o
     $(CONFIG_FOO)可以代表y(built-in對象)或m(module對象)。
      如果CONFIG_FOO不是y或m,那麼這個文件不會被編譯和鏈接。
--- 3.2 內嵌對象 - obj-y
    makefile文件將爲編譯vmlinux的目標文件放在$(obj-y)列表中,這些列表依賴於內核配置。
      Kbuild編譯所有的$(obj-y)文件,然後調用"$(LD) -r"合併這些文件到一個built-in.o文件中。built-in.o經過父makefile文件鏈接到vmlinux。$(obj-y)中的文件順序很重要。列表中文件允許重複,文件第一次出現將被鏈接到built-in.o,後續出現該文件將被忽略。
      鏈接順序之所以重要是因爲一些函數在內核引導時將按照他們出現的順序被調用,如函數(module_init() / __initcall)。所以要牢記改變鏈接順序意味着也要改變SCSI控制器的檢測順序和重數磁盤。
      例如: #drivers/isdn/i4l/Makefile
     # 內核ISDN子系統和設備驅動程序Makefile
     # 每個配置項是一個文件列表
     obj-$(CONFIG_ISDN)         += isdn.o
     obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
--- 3.3 可加載模塊 - obj-m
  $(obj-m)表示對象文件(object files)編譯成可加載的內核模塊。
  一個模塊可以通過一個源文件或幾個源文件編譯而成。makefile只需簡單地它們加到$(obj-m)。
      例如:#drivers/isdn/i4l/Makefile
        obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
    注意:在這個例子中$(CONFIG_ISDN_PPP_BSDCOMP)含義是'm'。
      如果內核模塊通過幾個源文件編譯而成,使用以上同樣的方法。
      Kbuild需要知道通過哪些文件編譯模塊,因此需要設置一個$(<module_name>-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)中的object文件,然後運行"$(LD) -r"將列表中文件生成isdn.o.
  Kbuild使用後綴-objs、-y識別對象文件。這種方法允許makefile使用CONFIG_符號值確定一個object文件是否是另外一個object的組成部分。
     例如: #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',則ext2.o只有xattr.o組成部分。
     注意: 當然,當你將對象文件編譯到內核時,以上語法同樣有效。因此,如果CONFIG_EXT2_FS=y,Kbuild將先編譯ext2.o文件,然後鏈接到built-in.o。
--- 3.4 導出符號目標
      在makefile文件中沒有特別導出符號的標記。
--- 3.5 庫文件 - lib-y
      obj-*中的object文件用於模塊或built-in.o編譯。object文件也可能編譯到庫文件中--lib.a。
      所有羅列在lib-y中的object文件都將編譯到該目錄下的一個單一的庫文件中。
      包含在0bj-y中的object文件如果也列舉在lib-y中將不會包含到庫文件中,因爲他們不能被訪問。但lib-m中的object文件將被編譯進lib.a庫文件。 
      注意在相同的makefile中可以列舉文件到buit-in內核中也可以作爲庫文件的一個組成部分。因此在同一個目錄下既可以有built-in.o也可以有lib.a文件。
      例如:#arch/i386/lib/Makefile
        lib-y   := checksum.o delay.o
     這樣將基於checksum.o、delay.o創建一個lib.a文件。
      對於內核編譯來說,lib.a文件被包含在libs-y中。將“6.3 目錄表”。
      lib-y通常被限制使用在lib/和arch/*/lib目錄中。
--- 3.6 目錄遞歸
     makefile文件負責編譯當前目錄下的目標文件,子目錄中的文件由子目錄中的makefile文件負責編譯。編譯系統將使用obj-y和obj-m自動遞歸編譯各個子目錄中文件。
     如果ext2是一個子目錄,fs目錄下的makefile將使用以下賦值語句是編譯系統編譯ext2子目錄。
     例如: #fs/Makefile
        obj-$(CONFIG_EXT2_FS) += ext2/
     如果CONFIG_EXT2_FS設置成'y(built-in)或'm'(modular),則對應的obj-變量也要設置,內核編譯系統將進入ext2目錄編譯文件。
      內核編譯系統只使用這些信息來決定是否需要編譯這個目錄,子目錄中makefile文件規定那些文件編譯爲模塊那些是內核內嵌對象。
      當指定目錄名時使用CONFIG_變量是一種良好的做法。如果CONFIG_選項不爲'y'或'm',內核編譯系統就會跳過這個目錄。
--- 3.7 編譯標記
  EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
  所有的EXTRA_變量只能使用在定義該變量後的makefile文件中。EXTRA_變量被makefile文件所有的執行命令語句所使用。
     $(EXTRA_CFLAGS)是使用$(CC)編譯C文件的選項。
     例如: # drivers/sound/emu10k1/Makefile
           EXTRA_CFLAGS += -I$(obj)
           ifdef 
            DEBUG EXTRA_CFLAGS += -DEMU10K1_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_$@, AFLAGS_$@
     CFLAGS_$@和AFLAGS_$@只使用到當前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 依賴關係
     內核編譯記錄如下依賴關係:
      1) 所有的前提文件(both *.c and *.h) 
      2) CONFIG_ 選項影響到的所有文件
      3) 編譯目標文件使用的命令行
     因此,假如改變$(CC)的一個選項,所有相關的文件都要重新編譯。
--- 3.10 特殊規則
      特殊規則使用在內核編譯需要規則定義而沒有相應定義的時候。典型的例子如編譯時頭文件的產生規則。其他例子有體系makefile編譯引導映像的特殊規則。特殊規則寫法同普通的Make規則。
     Kbuild(應該是編譯程序)在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)(因爲它們不是生成文件)。

=== 4 輔助程序
    內核編譯系統支持在編譯(compliation)階段編譯主機可執行程序。爲了使用主機程序需要兩個步驟:第一個步驟使用hostprogs-y變量告訴內核編譯系統有主機程序可用。第二步給主機程序添加潛在的依賴關係。有兩種方法,在規則中增加依賴關係或使用$(always)變量。具體描述如下。
--- 4.1 簡單輔助程序
     在一些情況下需要在主機上編譯和運行主機程序。下面這行告訴kbuild在主機上建立bin2hex程序。
     例如: hostprogs-y := bin2hex
     Kbuild假定使用makefile相同目錄下的單一C代碼文件bin2hex.c編譯bin2hex。
--- 4.2 組合輔助程序
     主機程序也可以由多個object文件組成。定義組合輔助程序的語法同內核對象的定義方法。
     $(<executeable>-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。
     注意:語法<executable>-y不能用於定義主機程序。
--- 4.3 定義共享庫 
     擴展名爲.so的對象是共享庫文件,並且是位置無關的object文件。內核編譯系統提供共享庫使用支持,但使用方法有限制。在下面例子中libkconfig.so庫文件被鏈接到可執行文件conf中。
     例如: #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.4 C++語言使用方法
     內核編譯系統提供了對C++主機程序的支持以用於內核配置,但不主張其它方面使用這種方法。
     例如: #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 輔助程序編譯控制選項
     當編譯主機程序時仍然可以使用$(HOSTCFLAGS)設置編譯選項傳遞給$(HOSTCC)。這些選項將影響所有使用變量HOST_EXTRACFLAG的makefile創建的主機程序。
     例如: #scripts/lxdialog/Makefile
           HOST_EXTRACFLAGS += -I/usr/include/ncurses
     爲單個文件設置選項使用下面方式:
     例如: #arch/ppc64/boot/Makefile
     HOSTCFLAGS_piggyback.o := -DKERNELBASE=$(KERNELBASE)
     也可以使用附加鏈接選項:
     例如: #scripts/kconfig/Makefile
           HOSTLOADLIBES_qconf := -L$(QTDIR)/lib
     當鏈接qconf時將使用外部選項"-L$(QTDIR)/lib"。
--- 4.6 何時建立輔助程序
     只有當需要時內核編譯系統纔會編譯主機程序。有兩種方式:
     (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)
     這樣就告訴內核編譯系統即使沒有任何規則使用lxdialog也要編譯它。
--- 4.7 使用hostprogs-$(CONFIG_FOO)
     在Kbuild文件中典型模式如下:
     例如: #scripts/Makefile
           hostprogs-$(CONFIG_KALLSYMS) += kallsyms
     對Kbuild來說'y'用於內嵌對象'm'用於模塊。
     因此如果config符號是'm',編譯系統也將創建該程序。換句話說內核編譯系統等同看待hostprogs-m和hostprogs-y。但如果不涉及到CONFIG符號僅建議使用hostprogs-y。

=== 5 編譯清除機制
  "make 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"命令清除。

=== 6 體系Makefile文件
  在開始進入各個目錄編譯之前,頂層makefile文件設置編譯環境和做些準備工作。頂層makefile文件包含通用部分,arch/$(ARCH)/Makefile包含該體系架構所需的設置。因此arch/$(ARCH)/Makefile會設置一些變量和少量的目標。
  當編譯時將按照以下大概步驟執行: 
1) 配置內核 => 產生 .config文件 
2) 保存內核版本到include/linux/version.h文件中 
3) 符號鏈接include/asm to include/asm-$(ARCH) 
4) 更新所有目標對象的其它前提文件
  - 附加前提文件定義在arch/$(ARCH)/Makefile文件中 
5) 遞歸進入init-* core* drivers-* net-* libs-*中的所有子目錄和編譯所有的目標對象
  - 上面變量值都引用到arch/$(ARCH)/Makefile文件。 
6) 鏈接所有的object文件生成vmlinux文件,vmlinux文件放在代碼樹根目錄下。
  最開始鏈接的幾個object文件列舉在arch/$(ARCH)/Makefile文件的head-y變量中。
7) 最後體系makefile文件定義編譯後期處理規則和建立最終的引導映像bootimage。
  - 包括創建引導記錄
  - 準備initrd映像和相關處理
--- 6.1 變量設置
  LDFLAGS      $(LD)一般選項
     選項使用於鏈接器的所有調用中。通常定義emulation就可以了。
     例如: #arch/s390/Makefile
           LDFLAGS   := -m elf_s390 
      注意: EXTRA_LDFLAGS和LDFLAGS_$@可以進一步訂製使用選項,將第7章。
  LDFLAGS_MODULE       $(LD)鏈接模塊的選項
     LDFLAGS_MODULE通常設置$(LD)鏈接模塊的.ko選項。
     默認爲"-r"即可重定位輸出文件。
  LDFLAGS_vmlinux   $(LD)鏈接vmlinux選項
     LDFLAGS_vmlinux定義鏈接最終vmlinux時鏈接器的選項。
     LDFLAGS_vmlinux支持使用LDFLAGS_$@。
     例如: #arch/i386/Makefile
           LDFLAGS_vmlinux := -e stext
  OBJCOPYFLAGS      objcopy選項
     當使用$(call if_changed,objcopy)轉化a .o文件時,OBJCOPYFLAGS中的選項將被使用。
     $(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_changed,xxx)
的使用方法見後。
  AFLAGS   $(AS)彙編選項
     默認值見頂層Makefile文件
     針對每個體系需要另外添加和修改它。
     例如: #arch/sparc64/Makefile
           AFLAGS += -m64 -mcpu=ultrasparc
  CFLAGS      $(CC)編譯器選項
     默認值見頂層Makefile文件
     針對每個體系需要另外添加和修改它。
     通常CFLAGS變量值取決於內核配置。
     例如: #arch/i386/Makefile
           cflags-$(CONFIG_M386) += -march=i386
           CFLAGS += $(cflags-y)
     許多體系Makefiles文件動態啓動市場目標機器上的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)
           ...
     第一個例子當config選項是'y'時將被選中。
  CFLAGS_KERNEL      $(CC)編譯built-in對象的選項
     $(CFLAGS_KERNEL)包含外部C編譯器選項編譯本地內核代碼。
  CFLAGS_MODULE      $(CC)編譯模塊選項
     $(CFLAGS_MODULE)包含外部C編譯器選項編譯可加載內核代碼。
--- 6.2 增加預設置項
     prepare: 這個規則用於列舉開始進入子目錄編譯前需要的前提文件。通常是些包含彙編常量的頭文件。
     例如:
     #arch/s390/Makefile
     prepare: include/asm-$(ARCH)/offsets.h
     在這個例子中include/asm-$(ARCH)/offsets.h將在進入子目錄前編譯。
      詳見XXX-TODO文件描述了kbuild如何產生offset頭文件。
--- 6.3 目錄表
     體系makefile文件和頂層makefile文件共同定義瞭如何建立vmlinux文件的變量。注意沒有體系相關的模塊對象定義部分:所有的模塊對象都是體系無關的。
  head-y, init-y, core-y, libs-y, drivers-y, net-y
     $(head-y) 列舉首先鏈接到vmlinux的對象文件。
     $(libs-y) 列舉了能夠找到lib.a文件的目錄。
     其餘的變量列舉了能夠找到內嵌對象文件的目錄。
     $(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/目錄。
     內核編譯系統無法在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 help"命令要列出所有目標系統文件,因此必須定義$(archhelp)變量。
     例如: #arch/i386/Makefile
     define 
        archhelp echo '* bzImage     - Image (arch/$(ARCH)/boot/bzImage)' 
      endef
     當執行不帶參數的make命令時,將首先編譯第一個目標對象。在頂層makefile中第一個目標對象是all:。
     一個體繫結構需要定義一個默認的可引導映像。
     "make help"命令的默認目標是以*開頭的對象。
     增加新的前提文件給all目標可以設置不同於vmlinux的默認目標對象。
     例如: #arch/i386/Makefile
           all: bzImage 
     當執行不帶參數的"make"命令時,bzImage文件將被編譯。
--- 6.5 編譯非內核目標
  extra-y
     extra-y定義了在當前目錄下創建沒有在obj-*定義的附加的目標文件。
     在extra-y中列舉目標是處於兩個目的: 
      1) 是內核編譯系統在命令行中檢查變動情況
        - 當使用$(call if_changed,xxx)時 
      2) 內核編譯系統知道執行"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)
     當這條規則被使用時它將檢查哪些文件需要更新,或命令行被改變。後面這種情況將迫使重新編譯編譯選項被改變的執行文件。使用if_changed的目標對象必須列舉在$(targets)中,否則命令行檢查將失敗,目標一直會編譯。
     賦值給$(targets)的對象沒有$(obj)/前綴。
     if_changed也可以和定製命令配合使用,見6.7"kbuild定製命令"。
     注意: 一個常見錯誤是忘記了FORCE前導詞。
  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 :="語句,導致沒有明顯的原因目標文件被重新編譯。
--- 6.7 定製編譯命令
     當執行帶KBUILD_VERBOSE=0參數的編譯命令時命令的簡短信息會被顯示。要讓定製命令具有這種功能需要設置兩個變量:
     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 KBUILD_VERBOSE=0"命令編譯$(obj)/bzImage目標時將顯示:
     BUILD   arch/i386/boot/bzImage
--- 6.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) : 目標編譯選項。注意要使用文件全名。
--- 6.9 $(CC)支持功能
     內核可能會用不同版本的$(CC)進行編譯,每個版本有不同的性能和選項,內核編譯系統提供基本的支持用於驗證$(CC)選項。$(CC)通常是gcc編譯器,但其它編譯器也是可以。
  cc-option cc-option 用於檢測$(CC)是否支持給定的選項,如果不支持就使用第二個可選項。
     例如: #arch/i386/Makefile
           cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)
     在上面例子中如果$(CC)支持-march=pentium-mmx則cflags-y等於該值,否則等於-march-i586。如果沒有第二個可選項且第一項不支持則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用於定義functions、loops等邊界對齊選項。
     gcc < 3.00 
        cc-option-align = -malign 
      gcc >= 3.00 
        cc-option-align = -falign
     例如: 
        CFLAGS += $(cc-option-align)-functions=4
     在上面例子中對於gcc >= 3.00來說-falign-functions=4,gcc < 3.00版本使用-malign-functions=4。
  cc-version cc-version返回$(CC)編譯器數字版本號。
     版本格式是<major><minor>,均爲兩位數字。例如gcc 3.41將返回0341。
     當一個特定$(CC)版本在某個方面有缺陷時cc-version是很有用的。例如-mregparm=3在一些gcc版本會失敗儘管gcc接受這個選項。
     例如: #arch/i386/Makefile
           GCC_VERSION := $(call cc-version)
           cflags-y += $(shell /
           if [ $(GCC_VERSION) -ge 0300 ] ; then echo "-mregparm=3"; fi ;)
     在上面例子中-mregparm=3只使用在版本大於等於3.0的gcc中。
=== 7 Kbuild變量
  頂層Makefile文件導出下面這些變量:
  VERSION, PATCHLEVEL, SUBLEVEL, EXTRAVERSION
     這幾個變量定義了當前內核版本號。很少體系體系Makefiles文件直接使用他們,常用$(KERNELRELEASE)代替。
     $(VERSION)、$(PATCHLEVEL)和$(SUBLEVEL)定義了三個基本部分版本號,例如"2", "4",和"0"。這三個變量一直使用數值表示。
     $(EXTRAVERSION)定義了更細的補釘號,通常是短橫跟一些非數值字符串,例如"-pre4"。
  KERNELRELEASE
     $(KERNELRELEASE)是一個單一字符如"2.4.0-pre4",適合用於構造安裝目錄和顯示版本字符串。一些體系文件使用它用於以上目的。
  ARCH
     這個變量定義了目標系統體系結構,例如"i386"、“arm"、"sparc". 一些內核編譯文件測試$(ARCH)用於確定編譯哪個文件。默認情況下頂層Makefile文件設置$(ARCH)爲主機相同的系統體系。當交叉編譯編譯時,用戶可以使用命令行改變$(ARCH)值:
        make ARCH=m68k ...
  INSTALL_PATH
     這個變量定義了體系Makefiles文件安裝內核映項和System.map文件的路徑。
  INSTALL_MOD_PATH, MODLIB
     $(INSTALL_MOD_PATH)定義了模塊安裝變量$(MODLIB)的前綴。這個變量通常不在Makefile文件中定義,如果需要可以由用戶添加。
     $(MODLIB)定義了模塊安裝目錄。
     頂層Makefile定義$(MODLIB)爲$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。用戶可以使用命令行修改這個值。
=== 8 Makefile語言
  內核Makefiles設計目標用於運行GNU Make程序。Makefiles僅使用GNU Make提到的特性,但使用了較多的GNU擴展部分。
  GNU Make程序支持基本的列表處理功能。內核Makefiles文件結合"if"語句使用了簡單的列表建立和維護功能。
  GNU Make程序有兩種賦值操作符:":="和"="。 ":="執行時立即計算右值並賦值給左值。"="類似公式定義,當每次使用左值要被使用時計算右值並賦給它。
  一些情況中使用"="合適,而一些情況中使用":="纔是正確選擇。
=== 9 Credits
  Original version made by Michael Elizabeth Chastain, <mailto:[email protected]> Updates 
by Kai Germaschewski <
[email protected]> Updates by Sam Ravnborg <[email protected]>
=== 10 TODO
- Describe how kbuild support shipped files with _shipped.
- Generating offset header files.
- Add more variables to section 7?
發佈了64 篇原創文章 · 獲贊 17 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章