Linux 環境下的程序員若是不會使用GNU make來構建和管理本身的工程,應該不能算是一個合格的專業程序員,至少不能稱得上是 Unix程序員。在 Linux(unix )環境下使用GNU 的make工具可以比較容易的構建一個屬於你本身的工程,整個工程的編譯只須要一個命令就能夠完成編譯、鏈接以致於最後的執行。不過這須要咱們投入一些時間去完成一個或者多個稱之爲Makefile 文件的編寫。
make是一個命令工具,它解釋Makefile 中的指令(應該說是規則)。在Makefile文件中描述了整個工程全部文件的編譯順序、編譯規則。Makefile 有本身的書寫格式、關鍵字、函數。像C 語言有本身的格式、關鍵字和函數同樣。並且在Makefile 中可使用系統shell所提供的任何命令來完成想要的工做。Makefile(在其它的系統上多是另外的文件名)在絕大多數的IDE 開發環境中都在使用,已經成爲一種工程的編譯方法。
下面都用C語言舉例子程序員
C語言的編譯和連接就是要把編寫的C程序轉換成可執行程序。編譯是把文本形源代碼翻譯爲機器語言形式的目標文件的過程。連接是把目標文件、操做系統的啓動代碼和用到的庫文件進行組織造成最終生成可執行代碼的過程。shell
編譯是讀取源程序,並進行詞法和語法的分析,將高級語言指令轉換爲功能等效的彙編代碼,編譯程序所要做得工做就是經過詞法分析和語法分析,在確認全部的指令都符合語法規則以後,將其翻譯成等價的中間代碼表示或彙編代碼。函數
連接程序的主要工做就是將有關的目標文件彼此相鏈接,也即將在一個文件中引用的符號同該符號在另一個文件中的定義鏈接起來,使得全部的這些目標文件成爲一個可以被操做系統裝入執行的統一總體。工具
make執行命令的時候須要一個Makefile文件,來告訴make命令如何編譯連接程序。
這裏先給一個例子來簡單的理解Makefile的書寫規則,在示例中,工程有11個C文件,和3個頭文件。咱們要寫一個Makefile來告訴make命令如何編譯連接這幾個文件。編譯規則以下:
一、若是全部的C文件都沒有編譯過,那麼要將全部C文件都編譯和連接。
二、若是有一個以上的C文件被修改,那麼咱們只編譯被修改的C文件,並連接目標程序。
二、若是頭文件被修改,那麼編譯引用頭文件的C文件,並連接目標程序。
咱們只要寫好Makefile文件,告訴make命令如何編譯連接,咱們就能夠很簡單的用一個make命令完成這個工做。ui
咱們仍是先大致的看一下Makefile的基本編寫規則。
操作系統
target ... : prerequisites ... command ...
target 是一個目標文件,這個目標文件能夠是Object File,也能夠是可執行文件,還能夠是一個標籤(僞目標)。
prerequisites 是要生成 target 所須要的文件或是目標。
command 是要執行的命令(任意Shell命令)
這其實就是一個依賴關係, target 依賴於prerequisites 中的文件,它生成的規則定義在 command 中。若是 prerequisites 有被修改的文件,那麼command 中的命令就會被執行。翻譯
咱們前面提到要舉一個例子,這個例子中有11個C文件和兩個頭文件,爲了可以實現前面提到的規則,咱們編寫了一個Makefile。unix
wordSystem : wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o cc -o wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o wordSysPat.o : wordSysPat.c wordSystem.h cc -c wordSysPat.c watchRecite.o : watchRecite.c mydil.h cc -c watchRecite.c wordSysSetPlan.o : wordSysSetPlan.c mydil.h cc -c wordSysSetPlan.c wordSysShow.o : wordSysShow.c wordSystem.h cc -c wordSysShow.c wordRecite.o : wordRecite.c mydil.h cc -c wordRecite.c wordSysLogin.o : wordSysLogin.c wordSystem.h mydil.h cc -c wordSysLogin.c wordSysMain.o : wordSysMain.c wordSystem.h cc -c wordSysMain.c clean : rm wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o
只要在保存文件的目錄中執行
make
就能夠生成最終的可執行文件 wordSystem ,執行make clean
能夠刪除全部可執行文件和中間目標文件。
Makefile 中的 \ 是換行的意思,這樣讓 Makefile 看起來整潔了一些,wordSystem 是最終生成的可執行文件,冒號後面的code
wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o
是它生成所須要的依賴文件,其實就是 target 是由 prerequisites 中的文件生成的。 command 就是生成 target 要執行的命令。這裏還要說一下書寫的格式,
command 的前面必需要有一個Tab鍵做爲開頭(不能夠是其餘的空白符,用空格湊出來一個相似Tab鍵的效果是不能夠的),不然就會出現相似這樣的提示,Makefile:10: *** 遺漏分隔符 。 中止。
同時不是命令的前面不能夠用Tab鍵,若是使用了Tab鍵開頭會出現相似這樣的提示,Makefile:1: *** recipe commences before first target。 中止。
Makefile 最後的 clean 它並非一個文件,只是一個動做的名稱,它並有依賴的文件,因此冒號後面沒有 prerequisites ,它只有要執行的命令,可是這個命令並不會自動的執行,這個命令要在make命令的後面寫出這個標籤來執行命令,
這樣就很是有用了,Makefile 中能夠寫不少與編譯無關或者不用編譯的命令,好比程序的打包,備份,清除等等。ip
當你輸入 make ,會在當前目錄尋找名字叫 makefile 或 Makefile 的文件,通常仍是使用 Makefile 這樣更容易區分,
固然若是你使用其它的名稱也能夠,只要加上-f
選項,如:make -f Linux_make
若是找到了文件,它會找到第一個目標做爲最終的生成目標,例子中的就是 wordSystem ,其實最後生成的文件的名稱是和第一目標生成的命令中的文件名決定的,以下:
wordSystem : wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o cc -o my_wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o ......
最後生成可執行文件的名稱是 my_wordSystem ,這也就說明其實 target 真的就只是一個目標 ,只是編譯連接的中間的一個過程而已,再以下:
wordSystem : wordSysPat_new.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o cc -o wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o wordSysPat_new.o : wordSysPat.c wordSystem.h cc -c wordSysPat.c watchRecite.o : watchRecite.c mydil.h cc -c watchRecite.c wordSysSetPlan.o : wordSysSetPlan.c mydil.h cc -c wordSysSetPlan.c wordSysShow.o : wordSysShow.c wordSystem.h cc -c wordSysShow.c wordRecite.o : wordRecite.c mydil.h cc -c wordRecite.c wordSysLogin.o : wordSysLogin.c wordSystem.h mydil.h cc -c wordSysLogin.c wordSysMain.o : wordSysMain.c wordSystem.h cc -c wordSysMain.c clean : rm wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o
我將 wordSysPat.o 的目標改爲了 wordSysPat_new.o ,最後的效果都是同樣,生成的中間文件也是 wordSysPat.o 而不是 wordSysPat_new.o ,
固然不要這麼寫,否則你本身也可能寫蒙了,只是理解一下標籤的實際效果。
繼續說工做的過程,若是 wordSystem 不存在那麼就尋找它的依賴,並執行命令,若是 wordSystem 已經存在了,而且它依賴的文件的修改時間要新,那麼就執行命令。
若是頭文件被修改了,那麼全部依賴了這個頭文件的目標都會被從新編譯,而後再和目標進行連接。
若是這個依賴文件也不存在,那麼就去找這個依賴文件的目標,並重復這個操做。
make 只會在文件中一層一層的去尋找文件依賴關係,而編譯的錯誤就不去關心,若是尋找依賴關係出現問題,那麼它就會直接退出並報錯。
咱們再看一下 wordSystem 的依賴
wordSystem : wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o cc -o wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o
咱們看到這裏不少的 [.o] 的文件重複的出現了兩次,這個例子中的文件還比較少,並且重複的次數比較少,可是若是是一個大的工程,咱們就不能保證重複屢次這樣的事情不會出錯,並且顯得很繁瑣,那麼有沒有簡化的方法那?
固然是有的,這就使用到了 Makefile 中的變量。就是定義一段固定的字符串,其實就像是C語言的宏定義。
那麼咱們就要聲明這個變量,這個變量你能夠給它起各類你喜歡的名稱了,只有不發生衝突就好,咱們就暫時給這個變量起名叫 objects 。
咱們用 $(objects) 來使用這個變量,那麼咱們的將 Makefile 就能夠改良成下面這樣。
objects = wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o wordSystem : $(objects) cc -o wordSystem $(objects) wordSysPat_new.o : wordSysPat.c wordSystem.h cc -c wordSysPat.c watchRecite.o : watchRecite.c mydil.h cc -c watchRecite.c wordSysSetPlan.o : wordSysSetPlan.c mydil.h cc -c wordSysSetPlan.c wordSysShow.o : wordSysShow.c wordSystem.h cc -c wordSysShow.c wordRecite.o : wordRecite.c mydil.h cc -c wordRecite.c wordSysLogin.o : wordSysLogin.c wordSystem.h mydil.h cc -c wordSysLogin.c wordSysMain.o : wordSysMain.c wordSystem.h cc -c wordSysMain.c clean : rm wordSystem $(objects)
是否是看起來就簡化了一些,之後在增減或減小一些目標文件都簡單了不少,只要更改變量就能夠了。
GNU的make仍是很強大的,它可以自動推導文件和依賴關係後的命令,因此咱們就沒有必要在每一個目標後面都寫的那麼複雜,因而就能夠修改爲以下:
objects = wordSysPat.o watchRecited.o wordSqlLogin.o \ wordSysSetPlan.o wordSysExit.o wordSysShow.o \ my_dil.o wordRecite.o wordSysLogin.o \ wordSysMain.o wordTest.o wordSystem : $(objects) cc -o wordSystem $(objects) wordSysPat_new.o : wordSystem.h watchRecite.o : mydil.h wordSysSetPlan.o : mydil.h wordSysShow.o : wordSystem.h wordRecite.o : mydil.h wordSysLogin.o : wordSystem.h mydil.h wordSysMain.o : wordSystem.h clean : rm wordSystem $(objects)
這就是make的 「隱晦規則」 ,因此以前那種玩笑的寫法是萬萬不可的。
每個 Makefile 都應該寫一個清除目標文件和可執行文件的規則,這不只便於重編譯,也很利於保持文件的清潔。
通常的風格都是這樣的:
clean : rm wordSystem $(objects)
更穩妥的方法:
.PHONY : clean clean : -rm wordSystem $(objects)
.PHONY
的意思是 clean 是個 ‘僞目標’ ,而 rm 命令前面的-
是若是某些文件出現問題先無論它,繼續向後執行,clean 有一個不成文的規則 「clean歷來都放在文件最後」 。
這些就是Makefile的簡單的概述,都是一些基礎,若是須要更多的細節請見: