GNU Make中文手冊(一)


GNU Make

翻譯loverszhaokai

最新版文檔請參考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 StallmanRoland 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。然后將該makefilemake的結果發送給我們,包括任何錯誤信息和警告信息。請不要改變這些信息:最好是通過剪切和黏貼的方式。在分離出最小的makefile文件的過程中,請不要使用任務收費的或者不正常的工具:你總是可以使用shell命令來完成相同的功能的。最后,請解釋清楚你預期的效果,這將是我們確定這個問題是否已經出現在文檔中。

     一旦你有一個明確的問題,你可以通過以下途徑提交。發送電子郵件到:

              bug-make@gnu.org

  或者使用基於瀏覽器的項目管理工具:

              http://savannah.gnu.org/projects/make/

  除了上面這些信息,請指明你所使用的make的版本號,你可以使用’make --version’命令來獲得。還有,你使用的機器的型號以及操作系統的信息。通過’make --help’的最后一行,你可以獲得操作系統的信息。

2       Makefile簡介

  你需要一個名字是”makefile”的文件來指定make如何工作。通常makefile指定make如何編譯和鏈接一個程序。

      在這一章中,我們將會討論一個簡單的makefile文件如何編譯和鏈接edit工程,該工程包含8C語言源文件和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

       一個規則指明了如何以及何時重新編譯確定的targetmake通過對prerequisites執行recipe來生成或者更新target。一個規則還可以指明如何以及何時執行一個操作。請看第4[編寫規則],第21頁。

       一個makefile可能包含除了規則的其他信息,但是一個簡單的makefile僅僅只需要包含規則。規則可能看起來比這個模板的復雜,但是這些規則都多少遵循這種形式。

2.2    一個簡單的Makefile

  這是一個簡單的makefile,它描述了執行文件edit依賴的8object文件,轉而,依賴於8C語言源文件和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又是prerequisiterecipes包括’cc –c main.c’’cc –c kbd.c’

       target是一個文件,如果對應的任何一個prerequisites發生變化,target都需要被重新編譯或鏈接。另外,任何本身是自動生成的prerequisites都應該先被更新。在這個示例中,’edit’依賴這8個源文件中的每一個;’main.o’依賴於源文件’main.c’和頭文件’defs.h’

       recipe可能在包含targetprerequisites的每一行之后出現。它描述了如何更新target。一個tab字符(或者其他由.RECIPEPREFIX定義的字符)必須出現在recipe每行的開頭,這樣做是為了區分recipemakefile文件中的其它行。(銘記於心,make根本不知道recipes是如何工作的。它取決於你提供的能夠更新target文件的recipesmake所做的只是在target文件需要被更新的時候,去執行你描述的recipe。)

       target ‘cean’ 並不是一個文件,它只是一個操作的名字。正常情況下,因為你不會去執行這個規則中的操作,所以’clean’不是任何其它規則的prerequisite。因此,除非你指定make去執行’clean’,否則’clean’永遠不會被自動執行。注意:這個規則不僅不是其它targetprerequisite,而且它自己也沒有任何prerequisites,所以這個規則的唯一目的就是執行recipes。類似這樣的沒有涉及prerequisites,並且只有recipestargets稱為假的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’,並且執行makemake則會編譯’insert.c’以更新’insert.o’,然后鏈接生成’edit’。如果我們改變’command.h’並且執行makemake則會重新編譯’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去除這種風險,並且簡化makefilevariables允許一個文本字符串被定義一次,並在之后的多個地方使用(請看第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並不是editprerequisite,所以只要我們提供make命令行參數,這個規則永遠都不會執行。為了執行這個規則,我們需要輸入’make clean’。請看第9[如何執行make],第99頁。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM