在linux環境下做嵌入式無論是編寫應用程序還是驅動程序等等,都需要用make來進行程序的編譯,就需要學會自己編寫Makefile。Makefile主要的作用有3點:1、決定編譯哪些文件 2、怎樣編譯這些文件 3、怎樣連接這些文件,他們的順序是什么樣的
一個簡單的makefile文件如下:
1 hell:hello.c 2 gcc -o hello hello.c 3 clean: 4 rm -f hello
這個是最簡單的makefile,makefile的作用就是寫程序者可以決定哪些文件需要編譯。上面的2、4行需要使用Tab鍵不能使用空格。
makefile中可以定義變量和使用函數。
makefile中定義變量:
在 Makefile 中,變量是一個名字(像是 C語言中的宏),代表一個文本字符串(變量的值)。在 Makefile 的目標、依賴、命令中引用變量的地方,變量會被它的值所取代(與 C 語言中宏引用的方式相同,因此其他版本的 make 也把變量稱之為“宏”)。變量名是大小寫敏感的。變量“foo”、“Foo”和“FOO”指的是三個不同的變量。Makefile 傳統做法是變量名是全采用大寫的方式。
變量的引用:當我們定義了一個變量之后,就可以在 Makefile 的很多地方使用這個變量。變量的引用方式是:“$(VARIABLE_NAME) ”或者“${ VARIABLE_NAME }”來引用一個變量的定義。對一個變量的引用可以在 Makefile 的任何上下文中,目標、依賴、命令、絕大多數指示符和新變量的賦值中。這里有一個例子,其中變量objects保存了所有.o文件的列表:
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
在 GNU make 中,變量的定義有兩種方式(或者稱為風格)。我們把使用這兩種方式定義的變量可以看作變量的兩種不同風格。變量的這兩種不同的風格的區別在於:1. 定義方式;2. 展開時機。
1、遞歸展開式變量
這一類型變量的定義是通過“=” 或者使用指示符“define”定義。這種變量的引用,在引用的地方是嚴格的文本替換過程,此變量值的字符串原模原樣的出現在引用它的地方。如果此變量定義中存在對其他變量的引用,這些被引用的變量會在它被展開的同時被展開。就是說在變量定義時,變量值中對其他變量的引用不會被替換展開;而是變量在引用它的地方替換展開的同時,它所引用的其它變量才會被一同替換展開。例如執行如下代碼:
1 foo = $(a) 2 a= $(b) 3 b = c? 4 5 all:;echo $(foo)
執行“make”將會打印出“c?”,整個變量的替換過程時這樣的:首先“$(foo)”被替換為“$(a)”,接下來“$(a)”被替換為“$(b)”,最后“$(b)”被替換為“Hug?”。整個替換的過程是在執行“echo $(foo)”時完成的。其優點是: 這種類型變量在定義時,可以引用其它的之前沒有定義的變量(可能在后續部分定義,或者是通過 make 的命令行選項傳遞的變量)。使用此風格的變量定義,可能會由於出現變量的遞歸定義而導致 make 陷入到無限的變量展開過程中,最終使 make 執行失敗。
2、直接展開式變量
為了避免“遞歸展開式”變量存在的問題和不方便。GNU make 支持另外一種風格的變量,稱為“直接展開”式。這種風格的變量使用“:=”定義。在使用“:=”定義變量時,變量值中對其他量或者函數的引用在定義變量時被展開(對變量進行替換)。所以變量被定義后就是一個實際需要的文本串,其中不再包含任何變量的引用。
注意“?=”操作符,GNU make 中,還有一個被稱為條件賦值的賦值操作符“?=”。被稱為條件賦值是因為:只有此變量在之前沒有賦值的情況下才會對這個變量進行賦值。
FOO ?= bar
其等價於:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
含義是:如果變量“FOO”在之前沒有定義,就給它賦值“bar” 。否則不改變它的值。
makefile中定義函數:
GNU make 的函數提供了處理文件名、變量、文本和命令的方法。使用函數我們的 Makefile 可以書寫的更加靈活和健壯。可以在需要的地方地調用函數來處理指定的文本(需要處理的文本作為函數的參數),函數的在調用它的地方被替換為它的處理結果。函數調用(引用)的展開和變量引用的展開方式相同。
GUN make函數調用的語法:
GNU make 函數的調用格式類似於變量的引用,以“$”開始表示一個引用。語法格式如下:
$(FUNCTION ARGUMENTS) 或者${FUNCTION ARGUMENTS}
1、調用語法格式中“FUNCTION”是需要調用的函數名,它應該是 make 內嵌的函數名。對於用戶自己的函數需要通過make的“call”函數來間接調用。
2、“ARGUMENTS”是函數的參數,參數和函數名之間使用若干個空格或者[tab]字符分割(建議使用一個空格,這樣不僅使在書寫上比較直觀,更重要的是當你不能確定是否可以使用[Tab]的時候,避免不必要的麻煩) ;如果存在多個參數時,參數之間使用逗號“,”分開。
3、以“$”開頭,使用成對的圓括號或花括號把函數名和參數括起(在 Makefile中,圓括號和花括號在任何地方必須成對出現)。參數中存在變量或者函數的引用時,對它們所使用的分界符(圓括號或者花括號)建議和引用函數的相同,不使用兩種不同的括號。推薦在變量引用和函數引用中統一使用圓括號;這樣在使用“vim”編輯器書寫 Makefile 時,使用圓括它可以亮度顯式 make的內嵌函數名,避免函數名的拼寫錯誤。在 Makefile 中應該這樣來書寫“$(sort $(x))”;而不是“$(sort ${x})”和其它幾種。
4、函數處理參數時,參數中如果存在對其它變量或者函數的引用,首先對這些引用進行展開得到參數的實際內容。而后才對它們進行處理。參數的展開順序是按照參數的先后順序來進行的。
5、書寫時,函數的參數不能出現逗號“,”和空格。這是因為逗號被作為多個參數的分隔符,前導空格會被忽略。在實際書寫 Makefile 時,當有逗號或者空格作為函數的參數時,需要把它們賦值給一個變量,在函數的參數中引用這個變量來實現。
GUN make中有我們常用的一些 內嵌的文本(字符串)處理函數:
1、$(subst FROM,TO,TEXT)
函數名稱:字符串替換函數—subst。
函數功能:把字串“TEXT”中的“FROM”字符替換為“TO”。
返回值:替換后的新字符串。
2、$(patsubst PATTERN,REPLACEMENT,TEXT)
函數名稱:模式替換函數—patsubst。
函數功能:搜索“TEXT”中以空格分開的單詞,將“TEXT”中符合“PATTERN”替換為“REPLACEMENT” 。參數“PATTERN”中可以使用模式通配符“%”來代表一個單詞中的若干字符。如果參數“REPLACEMENT”中也包含一個“%” ,那么“REPLACEMENT”中的“%”將是“TATTERN”中的那個“%”所代表的字符串。在“TATTERN”和“REPLACEMENT”中,只有第一個“%”被作為模式字符來處理,之后出現的不再作模式字符(作為一個字符)。在參數中如果需要將第一個出現的“%”作為字符本身而不作為模式字符時,可使用反斜杠“\”進行轉義處理(轉義處理的機制和使用靜態模式的轉義一致。
返回值:替換后的新字符串。
函數說明:參數“TEXT”單詞之間的多個空格在處理時被合並為一個空格,並忽略前導和結尾空格。
3、排序函數
函數名稱:排序函數—sort。
函數功能:給字串“LIST”中的單詞以首字母為准進行排序(升序),並取掉重復的單詞。
返回值:空格分割的沒有重復單詞的字串。
函數說明:兩個功能,排序和去字串中的重復單詞。可以單獨使用其中一個功能。
示例:
$(sort foo bar lose foo)
返回值為:“bar foo lose” 。
。。。。。。更多函數參考 GUN Make中文手冊
一般在編譯linux驅動時,我們需要在交叉編譯環境中進行編譯,所以要在編譯環境中搭建交叉編譯環境。編寫驅動的makefile使需要制定驅動依賴的內核的路徑,makefile寫法如下:
1 KERN_DIR = /xxx/xxx/linux-2.6.22.6 2 3 all: 4 make -C $(KERN_DIR) M=`pwd` modules 5 6 clean: 7 make -C $(KERN_DIR) M=`pwd` modules clean 8 rm -rf modules.order 9 10 obj-m += buttons.o
KERN_DIR=/XXX/XXX/linux-2.6.22.6,這句是對KERN_DIR進行賦值,確定后面使用內核源碼時的內核源碼路徑。
make -C $(KERN_DIR) M='pwd' modules,這句是makefile的規則:-C選項的作用是指將當前工作目錄轉移到你所指定的位置,當make的目標為all時,-C $(KDIR) 指明跳轉到內核源碼目錄下讀取那里的Makefile。
M=$(PWD) 表明然后從內核makefile中返回到當前目錄繼續讀入、執行當前的Makefile。M是內核根目錄下的Makefile中使用的變量,"M="選項的作用是,當用戶需要以某個內核為基礎編譯一個外部模塊的話,需要在make modules命令中加入"m=dir",程序會自動到你所指定的dir目錄中查找模塊源碼,將其編譯,生成ko文件。M=‘pwd’這句話是用來制定我們編譯的驅動的路徑。這句可以這樣來寫PWD:=$(shell pwd) M='PWD'。
