什么是Makefile?首先,make是一個工具程序(Utility software),是一種控制編譯或者重復編譯軟件的工具;make可以自動管理軟件編譯的內容、方式和時機從而使程序員能夠把精力集中在編寫代碼上。那make怎樣工作呢?其實make是通過makefile文件實現的。makefile是一個文本形式的數據庫文件。其中包含一些規則,這些規則告訴make需要編譯哪些文件、怎樣編譯這些文件以及在什么樣的條件下去編譯。
關於Makefile的基本規則:
target: dependency [dependency [...]]
command
command
[...]
每個命令的第一個字符必須是制表符,使用空格代替是不正確的。
其中,target是需要創建的二進制文件或目標文件,雖然目標體通常是程序,但它們也可以是文本文件、手冊頁面等任何東西甚至可以被用作測試和設置環境變量。dependency是在創建target時需要輸入的一個或者是多個文件的列表。命令序列是創建target文件所需的步驟,如編譯命令。一般,沒有特殊指定的情況下,make的工作目錄就是當前的目錄。當GNU make被調用時會順序查找名為GNUmakefile、makefile或者是Makefile的文件,但不知出於於某種不可描述的原因,絕大部分的Linux程序員習慣性的使用 Makefile這種形式。
簡單的makefile寫法:
首先有這么幾個簡單的程序 howdy.c helper.h helper.c
howdy.c
#include<stdio.h> #include"helper.h" int main() { printf("hello,linux programming world\n"); msg(); return 0; }
頭文件 helper.h
void msg(void);
helper.c
#include<stdio.h> void msg(void) { printf("This message sent form helper.c .\n"); }
好了,利用上述的這些程序寫一個簡單的Makefile文件
howdy:howdy.o helper.o helper.h gcc howdy.o helper.o -o howdy helper.o:helper.c helper.h gcc -c helper.c howdy.o:howdy.c gcc -c howdy.c clean: rm howdy *.o
要編譯howdy,只要在Makefile目錄下鍵入make即可。第一個目標體howdy稱為默認(default)目標體--這就是該Makefile文件要創建的文件。howdy有三個依賴體,分別為howdy.o helper.o helper.h;要生成howdy這個文件就必須要他所依賴的這三個文件。第二行就是調用編譯器的命令來供make執行來創建howdy文件,其中helper.h也列入依賴體的原因是為了防止編譯器調用未申明的函數產生錯誤。接下來的規則是告訴make怎樣生成單個目標文件:helper.o和howdy.o。這兩個規則用了gcc -c選項,其含義為只編譯但不鏈接文件。

嗯,,,大概的編譯鏈接過程就是上圖這樣,howdy.c和helper.c這兩個源文件經過預處理后編譯成目標文件,然后連接器把來自howdy.o和helper.o的目標代碼以及標准庫和c啟動代碼鏈接到一起生成可執行的二進制文件howdy。通常情況下,如果在依賴體helper.o和howdy.o不存在的情況下使用命令編譯howdy,那么gcc會報錯並退出。On the other hand,編譯器在看到howdy需要這兩個文件(以及helper.h)后,make先看它們是否存在,如果不存在則根據規則命令生成它們,然后再根據第一條規則生成howdy。顯然地,如果helper.h不存在,也會報錯退出,因為Makefile文件中並沒有規則命令生成helper.h。
好了,大概明白make是怎么執行的了,那新的問題又來了,make怎么知道什么時候該重新編譯一個新的文件呢? 其實很簡單:如果指定的目標文件不存在,那么毫無疑問,make就會根據規則命令生成它;如果目標文件已經存在,那么make就會將目標文件和依賴文件的時間戳進行對比,如果有一個以上的依賴文件比目標文件新(就是依賴文件的修改時間比目標文件的時間新),那么make就會重新編譯生成新的目標體。
偽目標:
什么是偽目標?偽目標就是上面Makefile文件的clean就是偽目標,偽目標就是不對應實際的文件。但是,由於偽目標沒有依賴體,所以它的命令是不會自動被make執行的,如果要編譯執行這個目標體,那么只需 make clean,編譯器就會編譯執行clean的命令。但是,如果剛好有個名字叫做clean的文件存在,那么make就會發現它,但是clean沒有依賴體,所以make就會認為clean已經是最新的文件從而不會執行其命令。為了處理這種情況,就需要使用特殊的目標體.PHONY。.PHONY的依賴體文件的含義和通常的一樣,但是make不檢查是否存在有文件名和依賴體中的一個名字相匹配的文件,而是直接執行與之相關的命令。如果使用.PHONY,上述的Makefile文件就成了下面這樣
howdy:howdy.o helper.o helper.h gcc howdy.o helper.o -o howdy helper.o:helper.c helper.h gcc -c helper.c howdy.o:howdy.c gcc -c howdy.c #hello:hello.c # gcc hello.c -o hello #all:howdy hello .PHONY clean: rm howdy *.o
什么意思?一句話,意思就是:make在更新目標文件時,不管clean后面有沒有依賴,都會直接執行clean下面的命令。
變量:
為了簡化編輯和維護Makefile,make允許在Makefile中創建和使用變量。所謂的變量就是偽指定文本串在makefile中定義一個名字,這個文本串就是變量的值。
定義變量的一般方法:
VARNAME = some_text [...]
把變量名用括號擴起來,然后在前面加上“$”,就可以引用變量的值了:$(VARNAME)
將之前的Makefile文件中使用變量:
OBJS = howdy.o helper.o HDRS = helper.h howdy:$(OBJS) gcc $(OBJS) -o howdy helper.o:helper.c $(HDRS) gcc -c helper.c howdy.o:howdy.c gcc -c howdy.c clean: rm -rf *.o
OBJS和HDRS會在被引用的每個地方展開成它的值,編譯時也是如此。關於自動變量:
變量 說明 $@ 規則的目標文件對應的文件名 $^ 規則中依賴的文件名 $< 規則中第一個相關文件名 $? 規則中日期新於目標的所有相關文件列表,以空格為分隔符 $(@D) 目標文件的目錄部分(目標在子目錄中) $(@F) 目標文件的文件名部分(目標在子目錄中)
用於程序名和標志的預定義變量:
變量 說明 AR 歸檔維護程序,默認值=ar AS 匯編程序,默認值=as CC C編譯程序,默認值=cc CPP C預處理程序,默認值=cpp RM 文件刪除程序,默認值= rm -rf ARFLAS 傳給歸檔維護程序的標志,默認值 = rv ASFLAGS 傳給匯編程序的標志,沒有默認值 CPPFLAGS 傳給c預處理程序的標志,沒有默認值 CFLAGS 傳給c編譯器的標志,沒有默認值 LDFLAGS 傳給鏈接程序(ld)的標志,沒有默認值
模式規則:
通過用戶定義自己的隱式規則,模式規則提供了擴展make的隱式規則的一種方法。模式規則雷士與普通規則,但是它的目標中必定含有符號 “%s”,這個符號可以與任何非空字符串匹配;為與目標中的“%s”匹配,這個規則的相關部分文件也必須使用“%s”。例如: %.o:%.c
告訴make所有形式為somename.o的目標(object)文件都應該從源文件somename.c編譯而來。再將上面的Makefile文件修改:
CC = gcc OBJ = howdy FILES = howdy.o helper.o HDRS = helper.h O_FLAG = -o C_FLAG= -c RM = rm -rf howdy:$(FILES) $(HDRS) $(CC) $(FILES) $(O_FLAG) $(OBJ) %.o:%.c $(CC) $(C_FLAG) $^ $(O_FLAG) $@ clean: $(RM) $(FILES) $(OBJ)
以上內容為自己在《GNU/Linux》一書中學習Makefile時的一點理解,有錯還請留言指正。
