Linux編譯系統
1.概述
從Linux核心2.6開始,Linux核心的編譯采用Kbuild系統,這同過去的編譯系統有很大的不同,尤其對于Linux核心子產品的編譯。在新的系統下,Linux編譯系統會兩次掃描Linux的Makefile:首先編譯系統會讀取Linux核心頂層的 Makefile,然後根據讀到的内容第二次讀取Kbuild的Makefile來編譯Linux核心。
Makefile由五個部分組成:
-
:根目錄Makefile,它讀取Makefile
,并負責建立vmlinux(核心鏡像)和modules(子產品檔案).這編譯核心或子產品是,這個檔案會被首先 讀取,并根據讀到的内容配置編譯環境變量。對于核心或驅動開發人員來說,這個檔案幾乎不用任何修改。.config檔案
-
:核心配置檔案(一般由make menuconfig生成).config
-
:目标處理器的Makefile是系統對應平台的Makefile。Kernel Top Makefile會包含這個檔案來指定平台相關資訊。隻有平台開發人員會關心這個檔案。arch/$(ARCH)/Makefile
-
:所有kbuild Makefile的規則,它們包含了定義/規則等。scripts/Makefile.*
-
:每個子目錄都有kbuild Makefiles
,它們負責生成built-in或子產品化目标。(注意:kbuild Makefile是指使用kbuild結構的Makefile,核心中的大多數Makefile都是kbuild Makefile。)Kernel Makefile被解析完成後,Kbuild會讀取相關的Kbuild Makefile進行核心或子產品的編譯。Kbuild Makefile有特定的文法指定哪些編譯進核心中、哪些編譯為子產品、及對應的源檔案是什麼等。核心及驅動開發人員需要編寫這個Kbuild Makefile檔案。kbuild Makefile
2.kbuild檔案
Kbuild Makefile的檔案名不一定是Makefile,盡管推薦使用Makefile這個名字。 大多的Kbuild檔案的名字都是Makefile。為了與其他Makefile檔案相差別,你也可以指定Kbuild Makefile的名字為
Kbuild
。
注意:而且如果“Makefile”和“Kbuild”檔案同時存在,則Kbuild系統會使用“Kbuild”檔案。
2.1 obj-y 和 obj-m
最簡單的kbuild Makefile可以僅包含:
obj-$(CONFIG_FOO) += foo.o
其中
$(CONFIG_FOO)
可以等于
y
或
m
,它的值由
.config
檔案給出。如果
$(CONFIG_FOO)
既不是y也不是m,那麼該檔案不會被編譯和連結。
- 當
等于y時,上面語句等價于$(CONFIG_FOO)
,它告訴kbuild在目前目錄下,有一個叫做foo.o的目标檔案,它将從obj-y += foo.o
或則foo.c
編譯得到。foo.S
-
等于m時,表示$(CONFIG_FOO)
需要被編譯成子產品。foo.o
2.1.1 obj-y生成built-in.o
Kbuild編譯所有的
$(obj-y)
檔案,并調用
$(LD) -r
把所有這些檔案合并到
built-in.o
檔案。這個
built-in.o
會被上一級目錄的Makefile使用,最終連結到vmlinux中。
2.1.2 目标由多個源檔案編譯得到
如果某個目标由多個源檔案編譯得到,那麼可以通過
$(<module_name>-objs)
$(<module_name>-y)
把這些源檔案告訴kbuild。Kbuild能夠識别字尾
-objs
和
-y
,例如:
#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
Kbuild會編譯所有
$(isdn-objs)
中的對象,并調用
"$(LD) -r"
把它們連結成
isdn.o
檔案。
下面是一個使用字尾
-y
的例子。字尾
-y
的好處是,可以使用
CONFIG_XXX
符号來決定是否加入某些源檔案(
.o
從對應的
.c
.S
編譯得到):
#fs/ext2/Makefile
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
2.1.3 調用子目錄Makefile
Makefile隻負責編譯目前目錄中的對象。子目錄中的對象,由子目錄中的Makefile負責。如何讓make調用子目錄中的Makefile?答案是把子目錄包含到
obj-y
obj-m
中。例如:
#fs/Makefile
obj-$(CONFIG_EXT2_FS) += ext2/
2.2 lib-y和lib-m
在一個目錄下,obj-y所列出的檔案,将被編譯成
built-in.o
檔案,而lib-y或lib-m所列出的檔案,将在目前目錄下生成
lib.a
注意:一般
lib-y
lib-m
隻用在
lib/
arch/*/lib
這兩個目錄中。
2.3 編譯選項變量
-
、ccflags-y
asflags-y
ldflags-y
這三個變量隻在目前Makefile中有效。
補充:
是定義在根目錄Makefile中的變量,它适用于整個核心數。$(KBUILD_CFLAGS)
-
subdir-ccflags-y
這兩個變量作用于目前Makefile及其所有子目錄。subdir-asflags-y
CFLAGS_$@
AFLAGS_$@
這兩個變量隻在目前Makefile中有效.
$@
可以用來指定檔案名,讓不同檔案可以使用不同的編譯選項。例如:
# 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
2.4 $(src)
$(obj)
$(kecho)
$(src)
$(obj)
$(kecho)
-
指向目前Makefile所在目錄的相對路徑。$(src)
-
指向用來儲存目标檔案的相對目錄。示例如下:$(obj)
#drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
這裡,
$(src)
等于
drivers/scsi/
,
$(obj)
同樣也等于
drivers/scsi/
使用“
make -s
”指令時,隻會輸出警告和錯誤資訊。
$(kecho)
能夠将其後的内容輸出到标準輸出流(一般就是顯示器),前提是沒有使用“
make -s
”。示例如下:
#arch/blackfin/boot/Makefile
$(obj)/vmImage: $(obj)/vmlinux.gz
$(call if_changed,uimage)
@$(kecho) 'Kernel: $@ is ready'
2.5 $(CC)
相關功能
$(CC)
2.5.1 as-option
ld-option
cc-option
as-option
ld-option
cc-option
當編譯、連結檔案時,
xx-opiton
可以用來檢查目前使用的
$(CC)
是否支援給出的編譯選項。如前者不支援,可使用後者。例如:
#arch/sh/Makefile
cflags-y += $(call as-option,-Wa$(comma)-isa=$(isa-y),)
如果目前的
$(CC)
不支援
-Wa$(comma)-isa=$(isa-y)
,那麼可以使用第二個編譯選項(這裡為空)。
2.5.2 cc-option-yn
cc-option-yn
cc-option-yn
用來檢查
$(CC)
是否支援給出的編譯選項。如支援,傳回y,否則傳回n。例如:
#arch/ppc/Makefile
biarch := $(call cc-option-yn, -m32)
aflags-$(biarch) += -a32
cflags-$(biarch) += -m32
2.5.3 cc-option-align
cc-option-align
gcc在3.0之後改變了指定函數、循環等對齊選項的類型。
-
時,gcc < 3.00
cc-option-align = -malign;
-
gcc >= 3.00
。使用cc-option-align = -falign
來選擇正确的字首。例如:$(cc-option-align)
KBUILD_CFLAGS += $(cc-option-align)-functions=4
2.5.4 cc-version
cc-ifversion
cc-fullversion
cc-version
cc-ifversion
cc-fullversion
cc-version
傳回
$(CC
)版本。如
$(CC)
為gcc 3.41,那麼
cc-version
0341
。例如:
#arch/i386/Makefile
cflags-y += $(shell /
if [ $(call cc-version) -ge 0300 ] ; then /
echo "-mregparm=3"; fi ;)
cc-ifversion
在版本符合條件的前提下傳回最後一個參數。示例如下:
#fs/reiserfs/Makefile
ccflags-y := $(call cc-ifversion, -lt, 0402, -O1)
如果
$(CC)
版本低于4.2,那麼
ccflags-y
将等于
-O1
cc-fullversion
給出更詳細的版本資訊,例如:
#arch/powerpc/Makefile
$(Q)if test "$(call cc-fullversion)" = "040200" ; then /
echo -n '*** GCC-4.2.0 cannot compile the 64- bit powerpc ' ; /
false ; /
fi
2.5.5 cc-cross-prefix
cc-cross-prefix
cc-cross-prefix
用于檢查是否存在給定字首的
$(CC)
,如存在,傳回第一個比對的字首,否則傳回空。如有多個字首需要比對,各字首之間使用單個空格隔開。例如:
#arch/m68k/Makefile
ifneq ($(SUBARCH),$(ARCH))
ifeq ($(CROSS_COMPILE),)
CROSS_COMPILE := $(call cc-cross-prefix, m68k-linux-gnu-)
endif
endif
3.編譯外部子產品
有時候我們需要在核心源代碼數的外面編譯核心子產品,編譯的基本指令是:
make -C $(KERNEL_DIR) M=`pwd` modules
我們可以把這個指令內建到Makefile裡,這樣我們就可以隻輸入“make”指令就可以了。
Makefile,它把
Normal Makefile
Kbuild Makefile
內建到一個檔案中了。為了差別Kbuild Makefile 和Normal Makefile,這樣我們改寫Makefile為如下形式,并且添加
Kbuild Makefile - “Kbuild”
#Makefile
ifneq ($(KERNELRELEASE),)
include "Kbuild"
else
KERNEL_DIR = /lib/modules/`uname -r`/build
MODULEDIR := $(shell pwd)
.PHONY: modules
default: modules
modules:
make -C $(KERNEL_DIR) M=$(MODULEDIR) modules
clean distclean:
rm -f *.o *.mod.c .*.*.cmd *.ko
rm -rf .tmp_versions
endif
#Kbuild Makefile
MODULE_NAME = helloworld
$(MODULE_NAME)-objs := hello.o
obj-m := $(MODULE_NAME).o
# 一般不需要在Makefile裡包含如下代碼,這樣寫完全是為了相容老版本的Kbuild系統。KERNELRELEASE變量在Kernel Makefile裡定義的,是以隻有在第二次由Kbuild讀取這個Makefile檔案時才會解析到Kbuild的内容。
ifneq ($(KERNELRELEASE),)
include "Kbuild"
else
...
endif
3.3 外部頭檔案
- EXTRA_CFLAGS :
可以給Kbuild系統添加外部系統頭檔案,EXTRA_CFLAGS
EXTRA_CFLAGS += $(ext_include_path)
-
/$(src)
$(obj)
-
是一個相對路徑,它就是$(src)
Makefile
檔案所在的路徑。Kbuild
- 同樣
就是編譯目标儲存的路徑,預設就是源代碼所在路徑。$(obj)
-