核心makefile檔案
--譯自linux3.9.5 kernel
makefiles(核心目錄documention/kbuild/makefiles.txt)
this document describes the
linux kernel makefiles
本文當介紹了linux核心的makefile
=== table of
contents
=== 目錄
=== 1 overview
=== 1 概述
=== 2 who does what
=== 2
角色分工
=== 3 the kbuild files
=== 3 核心編譯檔案
--- 3.1 goal definitions
--- 3.1 目标定義
--- 3.2 built-in object goals -
obj-y
--- 3.2 内嵌對象 - obj-y
--- 3.3 loadable module goals -
obj-m
--- 3.3 可加載子產品 - obj-m
--- 3.4 objects which export
symbols
--- 3.4 導出符号
--- 3.5 library file goals -
lib-y
--- 3.5 庫檔案 - lib-y
--- 3.6 descending down in
directories
--- 3.6 目錄遞歸
--- 3.7 compilation flags
--- 3.7 編譯标記
--- 3.8 command line
dependency
--- 3.8 指令依賴
--- 3.9 dependency tracking
--- 3.9 依賴關系
--- 3.10 special rules
--- 3.10 特殊規則
--- 3.11 $(cc) support
functions
--- 3.11 $(cc) 支援的函數
--- 3.12 $(ld) support functions
---
3.12 $(ld) 支援的函數
=== 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
增加預設定項到archheaders中
--- 6.3 增加預設定項到archprepare中
--- 6.4 遞歸向下通路的目錄清單
--- 6.5 具體架構的引導映像
--- 6.6 編譯非核心目标
--- 6.7 編譯引導映像指令
--- 6.8 自定義核心編譯指令
--- 6.9 預處理連接配接腳本
--- 6.10 $(cc)支援功能
=== 7 頭檔案的kbuild文法
--- 7.1 header-y
--- 7.2 genhdr-y
--- 7.3 destination-y
--- 7.4 generic-y
=== 8 kbuild變量
=== 9 makefile語言
=== 10 credits
===11 todo
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檔案的所有方面.
本文檔的讀者對象是普通開發者
和系統開發者.
核心中大多數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,那麼這個檔案不會被編譯和連結.
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
$(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)變量.
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檔案中沒有特别導出符号的标記.
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
核心編譯記錄如下依賴關系:
1) 所有的前提檔案(both
*.c and *.h)
2) config_ 選項影響到的所有檔案
3) 編譯目标檔案使用的指令行
是以,假如改變$(cc)的一個選項,所有相關的檔案都要重新編譯.
特殊規則使用在核心編譯需要規則定義而沒有相應定義的時候.典型的例子如編譯時頭檔案的産生規則.其他例子有體系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)(因為它們不是生成檔案).
核心編譯系統支援在編譯(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檔案混合組成,附加行表示這種情況.
qconf-objs := check.o
--- 4.5 輔助程式編譯控制選項
當編譯主機程式時仍然可以使用$(hostcflags)設定編譯選項傳遞給$(hostcc).這些選項将影響所有使用變量
host_extracflag的makefile建立的主機程式.
host_extracflags += -i/usr/include/ncurses
為單個檔案設定選項使用下面方式:
例如: #arch/ppc64/boot/makefile
hostcflags_piggyback.o := -dkernelbase=$(kernelbase)
也可以使用附加連結選項:
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産生原始的二進制檔案.
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)編譯器選項
通常cflags變量值取決于核心配置.
cflags-$(config_m386) += -march=i386
cflags +=
$(cflags-y)
許多體系makefiles檔案動态啟動市場目标機器上的c編譯器檢測支援的選項:
...
cflags-$(config_mpentiumii) += $(call cc-option,\
-march=pentium2,-march=i686) ...
# disable
unit-at-a-time mode ...
$(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.
boot :=
arch/i386/boot
bzimage:
vmlinux
$(q)$(make) $(build)=$(boot) $(boot)/$@
建議使用"$(q)$(make)
$(build)=<dir>"方式在子目錄中調用make指令.
沒有定義體系目标系統檔案的規則,但執行"make
help"指令要列出所有目标系統檔案,是以必須定義$(archhelp)變量.
define
archhelp echo ‘*
bzimage - image (arch/$(arch)/boot/bzimage)‘
endef
當執行不帶參數的make指令時,将首先編譯第一個目标對象.在頂層makefile中第一個目标對象是all:.
一個體系結構需要定義一個預設的可引導映像.
"make
help"指令的預設目标是以*開頭的對象.
增加新的前提檔案給all目标可以設定不同于vmlinux的預設目标對象.
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.
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)是否支援給定的選項,如果不支援就使用第二個可選項.
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接受這個選 項.
gcc_version :=
$(call cc-version)
$(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"語句使用了簡單的清單建立和維護功能.
make程式有兩種指派操作符:":="和"=". ":="執行時立即計算右值并指派給左值."="類似公式定義,當每次使用左值要被使用時計算右值并賦給它.
一些情況中使用"="合适,而一些情況中使用":="才是正确選擇.