源地址 :http://blog.csdn.net/maopig/article/details/6801749
1、前言
回想本身的第一個Makefile,是這個樣子的
html
後來有所進步,陸續地寫了一些大都是這個樣子的Makefile:mysql
看上去還行,用起來也不錯,可是隨着程序規模的擴大,每次添加一個新文件,都要手動修改Makefile,實在是不厭其煩。
後來閱讀了一些開源程序的Makefile源代碼,固然,不是automake生成的那種,有了一些心得,幾番進化,一段時間後,感受對GNU make算是有了些初步的瞭解,在此總結一下,也算是溫故而知新了。並且我記性比較差 ,放在這裏算是記錄一下,省得之後忘記。同時也省得你們再去翻那些繁複的手冊,浪費沒必要要的時間。
下文中makefile操做的對象有三個文件: foo.c , bar.c 和bar.h,內容分別以下:
foo.clinux
bar.csql
bar.hshell
OK,該交代的都交代了,進入正題。
2、個人makefile模板
把上個項目的makefile整理了一下,感受結構比較清晰,能夠做爲模板供之後使用。
文件內容大致是這個樣子的:編程
解釋:
前幾行都是變量的定義,至於爲何要定義這些變量,理由和編程中使用宏定義是同樣的,那就是改一個就可使不少地方同時生效,避免了重複的工做。
按照慣例:
CC變量指定了使用的編譯器
CFLAGS變量包含了所需的編譯選項
INCLUDE是尋找頭文件的路徑
LFLAGS是加載外部庫時的指定選項。
TARGET變量表明最終要生成的可執行程序
下面的內容就是關鍵了,咱們將利用一些GNU make內置的函數與推導規則來完成咱們的目標。
首先的任務是自動得到當前目錄下全部的源文件,好讓咱們新添文件後沒必要再修改Makefile。
完成這個功能的是這行代碼
SOUCE_FILES = $(wildcard *.c)
wildcard 是GNU make程序預約義的一個函數,做用即是獲取匹配模式文件名,原型爲$(wildcard PATTERN)。它的詳細說明能夠看這裏。簡單來講wildcard函數的參數只有一個,就是函數名以後的文件名模式,這裏的模式使用shell可識別 的通配符,包括「?」(單字符)、「*」(多字符)等。如今咱們的需求是獲取當前目錄下的全部.c文件,模式天然是*.c。
按照最基本的依賴規則,生成TARGET文件依賴於一系列的.o文件,那麼如何得到這些.o文件的列表呢?答案是使用patsubst模式替換函數函數:
$(patsubst %.c,%.o,$(SOUCE_FILES))
模 式替換函數patsubst函數原型爲$(patsubst PATTERN,REPLACEMENT,TEXT),相比wildcard,它要複雜一些,顧名思義,三個參數依次表明了匹配模式,替換規則,替換目標 字符串。在這裏,咱們須要把全部.c替換成.o,因此寫成上面的樣子就能夠了。
如今c源文件列表和obj文件列表都有了,下一步就該爲每一個源文件編寫規則了。
其實不少源文件的編譯規則都是同樣的,就像最開始那個Makefile中那樣小程序
僅僅是文件名不一樣而已,所以就給了咱們提取模式的某種可能性。我在一個關於winsock的makefile中找到了答案:bash
這個規則利用了GNU make的後綴規則。
在這裏,當定義了一個目標是「.c.o」的規則時。它的含義是全部「.o」文件的依賴文件是對應的「.c」文件。所以在這條規則下,foo.c將被自動編譯成foo,bar.c被編譯成bar。
而特殊目標.SUFFIXES這句的做用是: 在默認後綴的基礎上,增長了能夠做爲後綴的關鍵字符串。
其實.c.o是確定在默認識別的規則中的,不過爲了保險起見,仍是顯式地聲明一下比較好。
能夠看到,這個規則十分的晦澀,反正我第一眼真是沒看明白。所以,新版本的GNU make已經使用模式規則替代了後綴規則。
一樣的功能,利用模式規則實現以下:函數
這樣看起來便清晰多了。若是考慮到頭文件,完美的寫法應該是這樣的:工具
在上面的規則中,還使用了一些GNU make的自動化變量,他們的含義分別以下:
$@ --- 目標文件
$< --- 第一個依賴文件
$^ --- 全部的依賴文件
更多的自動化變量能夠參見這裏
最後的規則就是生成可執行文件了,很普通,再也不贅述。
爲了方便調試,能夠在makefile中定義一些僞目標。(僞目標的解釋和意義能夠看這裏)
通常調試用的makefile中都會有兩個僞目標,一個clean,一個debug
對 於clean,手冊裏說:「make存在一個內嵌隱含變量「RM」,它被定義爲:「RM = rm –f」。所以在書寫「clean」規則的命令行時可使用變量「$(RM)」來代替「rm」,這樣能夠免出現一些沒必要要的麻煩!」雖然不知道「必要的麻 煩」是什麼,可是當心不爲過,照着手冊作比較好。
對於debug,和正常模式不一樣的就是添加了一些編譯選項,修改CFLAGS的內容就能夠了。但目前還沒搞明白怎麼動態地在makefile裏修改變量的內容。這個問題之後再說。
3、在多文件夾狀況使用makefile組織代碼
上一段中給出的makefile,對於通常的小程序已經足矣,可是若是代碼文件愈來愈多,最後不得不放到幾個文件夾中,這時又該怎麼辦?
好比說咱們準備把bar.c中的函數整理成了一個函數庫libbar放在主程序文件夾中的子文件夾libbar中,這時該如何利用makefile來組織這些文件?
比較好的辦法是在libbar文件夾中放置一個獨立的子makefile,而後在主makefile裏調用它。
libbar/Makefile:
主Makefile:
在主makefile中使用了shell的for語句,循環取出SUBDIRS中的子文件夾名,而後進入子文件夾執行make,而後返回。若是在子makefile中出錯,編譯過程將終止。
4、編譯多個目標
不知你有沒有遇到過這樣的狀況,那就是須要從不少的代碼,生成不少的可執行文件。
例如編寫了一堆小工具,而每一個工具只有一個源文件,用foo.c生成foo,用bar.c生成bar。
一個一個編譯確定不現實,這時該怎麼作?讓咱們用GNU make來解決吧!
仔細閱讀手冊,發現GNU make中的靜態模式,正好能夠知足這個要求。
方便閱讀,直接將手冊中關於靜態模式的解釋粘貼以下:
對應咱們的需求,應該是用符合%.c模式的文件,生成文件名爲%的可執行文件,同時利用自動化變量,構造規則以下:
其中$(TARGET_FILES)爲最終的可執行文件名,能夠用wildcard配合patsubs函數得到。
由於$(TARGET_FILES)不止一個,因此直接寫這個命令的結果是隻會編譯出一個可執行文件,即第目標文件列表中的一個文件,要想成功編譯出全部的,還須要僞目標的幫忙。
完整的makefile以下:
CC = gcc
CFLAGS = -Wall -O
SOUCE_FILES=$(wildcard *.c)
TARGET_FILES=$(patsubst %.c,%,$(SOUCE_FILES))
.PHONY:all
all:$(TARGET_FILES)
$(TARGET_FILES): % : %.c
g++ $(CFLAGS) $< -o $@
clean:
$(RM) $(TARGET_FILES)
這裏介紹兩種變量的高級使用方法,第一種是變量值的替換。
咱們能夠替換變量中的共有的部分,其格式是「$(var:a=b)」或是「${var:a=b}」,其意思是,把變量「var」中全部以「a」字串「結尾」的「a」替換成「b」字串。這裏的「結尾」意思是「空格」或是「結束符」。
仍是看一個示例吧:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
這個示例中,咱們先定義了一個「$(foo)」變量,而第二行的意思是把「$(foo)」中全部以「.o」字串「結尾」所有替換成「.c」,因此咱們的「$(bar)」的值就是「a.c b.c c.c」。
另一種變量替換的技術是以「靜態模式」(參見前面章節)定義的,如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
這依賴於被替換字串中的有相同的模式,模式中必須包含一個「%」字符,這個例子一樣讓$(bar)變量的值爲「a.c b.c c.c」。