GNU Make
最新版文檔請參考github:
https://github.com/loverszhaokai/GNUMakeManual_CN
歡迎大家提出修改意見!謝謝!自由加油!
原文:https://www.gnu.org/software/make/manual/
參考:
1. 徐海兵 http://www.yayu.org/book/gnu_make/
2. 陳皓 http://blog.csdn.net/haoel/article/details/2886
關於本手冊的聲明:
本文瑾獻給所有熱愛Linux的程序員!本文檔版權所有,禁止用於任何商業行為。(向徐海兵致敬!)
1 make概覽
在一個龐大的程序中,make命令自動決定了哪些文件需要重新編譯,和重新編譯它們的步驟。本手冊介紹GNU make,它是由Richard Stallman和Roland McGrath實現的。開發工作從3.76版就由Paul D.Smith接手了。 GNU make遵循IEEE 標准1003.2-1992(POSIX.2)的第6.2章。
我們的示例采用的是C語言程序,因為程序語言基本都相似,凡是能夠在shell執行編譯的程序設計語言,都可以使用make。實際上,make不局限於程序。你可以使用make來描述任何任務,只要該任務滿足一下規則,當文件A依賴的文件BCD…變化時,文件A就需要被更新。
在使用make之前,你需要編寫一個文件名為makefile的文件,這個makefile文件負責描述程序中文件間的關系,並且提供更新文件所需要的命令。很明顯的,在一個程序中,object文件的更新需要很多源文件,可執行文件的更新需要使用很多object文件。
一旦存在一個適當的makefile文件,每一次你改變源文件,只需要在shell輸入make就可以重新編譯整個程序:
# make
make通過使用makefile中文件的關系以及文件最近修改時間來決定是否需要更新。對於每一個需要更新的文件,都會執行makefile中對應的命令。
你也可以通過命令行參數指定重新編譯哪些文件,或者如何編譯。參考第9章[如何執行make],第99頁。
1.1 如何閱讀該手冊
如果你是make新手,或者你正在尋找一個綜合的介紹,那么你可以閱讀每一章的前面幾個小節,略過其余小節。在每一章,前幾節包括簡介和綜合信息,后幾節包括專用的信息。第2章[Makefile簡介],第3頁是例外,該章全都是簡介。
如果你熟悉其他make工具,請看第13章[GNU make特性],第143頁,這一章列舉了GNU make的加強,還有第14章[不兼容點和不支持的特性],第147頁,
這一章說明了僅有的一些GNU make不支持但其它make工具支持的特性。
快速總結,請看第9.7節[選項總覽],第104頁,附錄A[快速索引],第165頁,第4.8節[特殊目標],第32頁。
1.2 問題和Bugs
對於GNU make,如何你有問題或你認為你發現一個bug,請把它提交給開發人員;我們不能保證解決你提出的問題或bug,但是我們會竭盡全力。
提交bug前,請確定你確實發現了一個bug。仔細地閱讀文檔,查看你是否能這樣做。如果你還不確定是否可以這樣做,請提交給我們,因為這是文檔的bug。
在你提交bug或嘗試自己修復之前,盡可能的分離出最小的makefile文件,該文件可以重現bug。然后將該makefile和make的結果發送給我們,包括任何錯誤信息和警告信息。請不要改變這些信息:最好是通過剪切和黏貼的方式。在分離出最小的makefile文件的過程中,請不要使用任務收費的或者不正常的工具:你總是可以使用shell命令來完成相同的功能的。最后,請解釋清楚你預期的效果,這將是我們確定這個問題是否已經出現在文檔中。
一旦你有一個明確的問題,你可以通過以下途徑提交。發送電子郵件到:
或者使用基於瀏覽器的項目管理工具:
http://savannah.gnu.org/projects/make/
除了上面這些信息,請指明你所使用的make的版本號,你可以使用’make --version’命令來獲得。還有,你使用的機器的型號以及操作系統的信息。通過’make --help’的最后一行,你可以獲得操作系統的信息。
2 Makefile簡介
你需要一個名字是”makefile”的文件來指定make如何工作。通常makefile指定make如何編譯和鏈接一個程序。
在這一章中,我們將會討論一個簡單的makefile文件如何編譯和鏈接edit工程,該工程包含8個C語言源文件和3個頭文件。makefile還可以指定make如何執行各種各樣的命令(例如,make clean,執行刪除指定文件的操作)。這里有makefile更復雜的示例,請看附錄C[復雜的Makefile],第177頁。
當make重新編譯edit,每一個改變過的C語言源文件都必須重新編譯。為了安全,如果頭文件發生改變,每一個包含該頭文件的C語言源文件必須重新編譯。每一次編譯,都會產生與源文件關聯的object文件。最后,只要任何一個object文件被重新編譯了,所有的object文件不管是否是新的還是舊的,都必須被重新鏈接,以生成新的執行文件edit。
2.1 Makefile規則
一個簡單的makefile包含如下規則:
target … : prerequisites …
recipe
…
…
一個target通常是指程序生成的文件的名字;targets可以是執行文件的名字或者object文件的名字。target還可以是動作的名字,例如’clean’(請看第4.5節)[偽造的target],第29頁)。
prerequisites是指用來生成target的一些文件,一個target通常依賴一些文件。
recipe是指一個操作,該操作由make來執行。一個recipe可能含有多個命令,這些命令可以都在同一行,也可以各自獨立一行。請注意:recipe的每一行開頭都要有tab鍵!這是為了防止有人粗心大意。如果你仍堅持在recipes前面加入除tab外的字符,你可以設置.RECIPEPREFIX變量為你想要的字符。(請看第6.14節[特殊變量],第73頁)。
通常,如果prerequisites發生變化,那么make就會執行對應的recipe以重新生成target。但是,target可以沒有prerequisites,例如target ’clean’就沒有prerequisites。
一個規則指明了如何以及何時重新編譯確定的target。make通過對prerequisites執行recipe來生成或者更新target。一個規則還可以指明如何以及何時執行一個操作。請看第4章[編寫規則],第21頁。
一個makefile可能包含除了規則的其他信息,但是一個簡單的makefile僅僅只需要包含規則。規則可能看起來比這個模板的復雜,但是這些規則都多少遵循這種形式。
2.2 一個簡單的Makefile
這是一個簡單的makefile,它描述了執行文件edit依賴的8個object文件,轉而,依賴於8個C語言源文件和3個頭文件。
在這個示例中,所有的C語言源文件都包含’defs.h’,但是只有定義了編輯命令的文件包含’command.h’,只有改變編輯器的文件包含’buffer.h’。
edit: main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o main.o: main.c defs.h cc -c main.c kbd.o: kbd.c defs.h command.h cc -c kbd.c command.o: command.c defs.h command.h cc -c command.c display.o: display.c defs.h buffer.h cc -c display.c insert.o: insert.c defs.h buffer.h cc -c insert.c search.o: search.c defs.h buffer.h cc -c search.c files.o: files.c defs.h buffer.h command.h cc -c files.c utils.o: utils.c defs.h cc -c utils.c clean: rm edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
我們使用反斜杠’\’將一行分割成兩行,這就像是一行一樣,但是更利於閱讀。請看第3.11節[分割長行]第12頁。
使用該makefile創建名為edit的執行文件,輸入:
# make
使用該makefile刪除執行文件和所有的object文件,輸入:
# make clean
在makefile示例中,targets包括執行文件’edit’,和object文件’main.o’, ‘kbd.o’。prerequisites包括文件’main.c’, ‘defs.h’。事實上,每一個’.o’文件既是target又是prerequisite。recipes包括’cc –c main.c’和’cc –c kbd.c’。
當target是一個文件,如果對應的任何一個prerequisites發生變化,target都需要被重新編譯或鏈接。另外,任何本身是自動生成的prerequisites都應該先被更新。在這個示例中,’edit’依賴這8個源文件中的每一個;’main.o’依賴於源文件’main.c’和頭文件’defs.h’。
recipe可能在包含target和prerequisites的每一行之后出現。它描述了如何更新target。一個tab字符(或者其他由.RECIPEPREFIX定義的字符)必須出現在recipe每行的開頭,這樣做是為了區分recipe與makefile文件中的其它行。(銘記於心,make根本不知道recipes是如何工作的。它取決於你提供的能夠更新target文件的recipes。make所做的只是在target文件需要被更新的時候,去執行你描述的recipe。)
target ‘cean’ 並不是一個文件,它只是一個操作的名字。正常情況下,因為你不會去執行這個規則中的操作,所以’clean’不是任何其它規則的prerequisite。因此,除非你指定make去執行’clean’,否則’clean’永遠不會被自動執行。注意:這個規則不僅不是其它target的prerequisite,而且它自己也沒有任何prerequisites,所以這個規則的唯一目的就是執行recipes。類似這樣的沒有涉及prerequisites,並且只有recipes的targets稱為假的targets。具體信息請看第4.5節[假的targets],第29頁。如何是make忽略rm或其它命令引起的錯誤,請看第5.5節[recipes中的錯誤],第49頁。
2.3 make如何處理Makefile文件
默認情況下,make從第一個target(不會是以’.’開頭的targets)開始執行。我們稱第一個target為默認目標(目標是指那些make最終努力去更新的targets。你可以通過命令行參數(請看第9.2節[指定目標的參數],第99頁)或者設置.DEFAULT_GOAL具體值(請看第6.14節[其他具體變量],第73頁),來重寫這個行為)。
在上一節的簡單示例中,默認的目標就是更新執行文件’edit’;因此,我們把這個規則放在第一行。
因此,當你輸入命令:
# make
make讀取當前目錄下的makefile文件,開始處理第一個規則。在示例中,這個規則就是重新鏈接’edit’;但是在make完全處理這個規則之前,make必須先處理’edit’依賴的object文件的規則。這些object文件中的每一個都需要按照自己的規則進行處理。這些規則指明每一個’.o’文件都需要編譯它的源文件。出現如下情況必須重新編譯,存在任何一個prerequisites的文件修改時間比object文件新,或者object文件不存在。
其它規則會被處理因為它們的targets作為目標的prerequisites。如果一些規則沒有被目標依賴(或者任何目標依賴的文件等),那么這些規則不會被處理,除非你指定make去這樣做(例如,make clean)。
在重新編譯object文件之前,make會考慮更新它的prerequisites,也就是源文件和頭文件。這個makefile中沒有指明為源文件和頭文件做任何事情,因為’.c’和’.h’文件不是任何規則的targets,所以make不會為這些文件做任何事情。但是make可以通過規則更新由Bison或者Yacc自動生成的C語言程序。
在重新編譯需要的object文件之后,make決定是否重新鏈接’edit’。如下情況下就需要重新鏈接,存在任何一個修改時間比’edit’新的object文件,或者’edit’不存在。如果一個object文件剛被重新編譯,那么它就比’edit’新,所以’edit’就會被重新鏈接。
因此,如果我們改變’insert.c’,並且執行make,make則會編譯’insert.c’以更新’insert.o’,然后鏈接生成’edit’。如果我們改變’command.h’並且執行make,make則會重新編譯’kbd.o’, ’command.o’, ’files.o’,然后鏈接生成’edit’。
2.4 變量使Makefiles更簡單
在我們的示例中,我們不得不在’eidt’的規則中兩次列舉所有的object文件:
edit: main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
這樣的副本是易於出錯的;如果一個新的文件被添加到系統中,我們就要把它添加到其中一個序列中,很可能會忘記添加到另一個序列中。我們可以通過使用variables去除這種風險,並且簡化makefile。variables允許一個文本字符串被定義一次,並在之后的多個地方使用(請看第6章[如何使用variables],第59頁)。
這是一個慣例在每一個makefile文件中含有一個variable名為objects, OBJECTS, objs, OBJS, obj, 或者 OBJ,以上這些全都是object文件的名字。我們可以在makefile中定義一個這樣的variable objects:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
然后,每一個我們想使用object文件名字序列的地方,都可以用variable來替換,只要寫’$(objects)’( 請看第6章[如何使用variables],第59頁)。
下面是用變量替換后的makefile文件:
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o edit: $(objects) cc -o edit $(objects) main.o: main.c defs.h cc -c main.c kbd.o: kbd.c defs.h command.h cc -c kbd.c command.o: command.c defs.h command.h cc -c command.c display.o: display.c defs.h buffer.h cc -c display.c insert.o: insert.c defs.h buffer.h cc -c insert.c search.o: search.c defs.h buffer.c cc -c search.c files.o: files.c defs.h buffer.h command.h cc -c files.c utils.o: utils.c defs.h cc -c utils.c clean: rm edit $(objects)
2.5 讓make來推斷recipes
不一定要為單個的C語言源文件編寫recipes來編譯它,因為make可以進行如下推斷:它有隱含規則指明通過相關聯的’.c’文件,使用’cc -c’命令,來更新一個’.o’文件。例如,make使用’cc –c main.c –o main.o’ 命令來將’main.c’ 編譯成’main.o’文件。因此,我們可以省略在object文件的規則中省略recipes。請看第10章[使用隱含規則],第111頁。
如果一個’.c’文件使用隱含規則的時候,該’.c’文件還會自動地加入到prerequisites中,因此當我們不寫recipes時,可以在prerequisites中省略’.c’文件。
下面是全部的示例,包括以上的所有改變:
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o edit: $(objects) cc -o edit $(objects) main.o: defs.h kbd.o: defs.h command.h command.o: defs.h command.h display.o: defs.h buffer.h insert.o: defs.h buffer.h search.o: defs.h buffer.h files.o: defs.h buffer.h command.h utils.o: defs.h .PHONY: clean clean: rm edit $(objects)
以上就是我們實踐中編寫的makefile。(關於’clean’的描述請看第4.5節[假的targets],第29頁和第5.5節[recipes中的錯誤],第49頁。)
因為隱含規則如此方便,所以這些規則很重要。你將會經常看到它們的應用。
2.6Makefile的另一種樣式
當使用隱含規則創建object文件的時候,你就可以使用makefile的另一種樣式。這種樣式的makefile,你可以按照prerequisites進行分組而不是targets。下面是這種樣式的makefile:
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o edit: $(objects) cc -o edit $(objects) $(objects): defs.h kbd.o command.o files.o: command.h display.o insert.o search.o files.o: buffer.h .PHONY: clean clean: rm edit $(objects)
上面的makefile中,’defs.h’是所有object文件的prerequisite;’command.h’和’buffer.h’是部分object文件的prerequisite。
這種風格的makefile是否更好取決於個人的偏好:它看起來更緊湊,但是一些人不喜歡它,因為他們覺得把每個target的信息單獨放在一起看起來更清晰。
2.7 清除目錄的規則
你編寫規則可能不僅是想編譯程序。Mafile通常可以完成除編譯程序外的一些事情:例如,如何刪除所有的object文件和執行文件,以使目錄變得干凈。
這里我們編寫一個規則來清空我們示例中edit工程:
clean:
rm edit $(objects)
實際中,我們可能想要編寫更復雜的規則來處理意料之外的情況。我們可以這樣寫:
.PHONY: clean
clean:
rm edit $(objects)
通過.PHONY可以避免當前目錄存在名為’clean’的文件帶來的混淆,並且忽略rm的錯誤繼續執行。(請看第4.5節[假的targets],第29頁和第5.5節[recipes中的錯誤],第49頁。)
一個類似clean的規則不能放在makefile的開頭,因為我們不想讓這個規則默認執行。因此,就像示例中的makefile,我們想讓重新編譯editor的規則仍然為默認目標。
因為clean並不是edit的prerequisite,所以只要我們提供make命令行參數,這個規則永遠都不會執行。為了執行這個規則,我們需要輸入’make clean’。請看第9章[如何執行make],第99頁。