概述
——
什麼是makefile?或許不少Winodws的程序員都不知道這個東西,由於那些Windows的IDE都爲你作了這個工做,但我以爲要做一個好的和professional的程序員,makefile仍是要懂。這就好像如今有這麼多的HTML的編輯器,但若是你想成爲一個專業人士,你仍是要了解HTML的標識的含義。特別在Unix下的軟件編譯,你就不能不本身寫makefile了,會不會寫makefile,從一個側面說明了一我的是否具有完成大型工程的能力。
由於,makefile關係到了整個工程的編譯規則。一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,甚至於進行更復雜的功能操做,由於makefile就像一個Shell腳本同樣,其中也能夠執行操做系統的命令。
makefile帶來的好處就是——「自動化編譯」,一旦寫好,只須要一個make命令,整個工程徹底自動編譯,極大的提升了軟件開發的效率。make是一個命令工具,是一個解釋makefile中指令的命令工具,通常來講,大多數的IDE都有這個命令,好比:Delphi的make,Visual C++的nmake,Linux下GNU的make。可見,makefile都成爲了一種在工程方面的編譯方法。
如今講述如何寫makefile的文章比較少,這是我想寫這篇文章的緣由。固然,不一樣產商的make各不相同,也有不一樣的語法,但其本質都是在「文件依賴性」上作文章,這裏,我僅對GNU的make進行講述,個人環境是RedHat Linux 8.0,make的版本是3.80。必竟,這個make是應用最爲普遍的,也是用得最多的。並且其仍是最遵循於IEEE 1003.2-1992 標準的(POSIX.2)。
在這篇文檔中,將以C/C++的源碼做爲咱們基礎,因此必然涉及一些關於C/C++的編譯的知識,相關於這方面的內容,還請各位查看相關的編譯器的文檔。這裏所默認的編譯器是UNIX下的GCC和CC。
關於程序的編譯和連接
——————————
在此,我想多說關於程序編譯的一些規範和方法,通常來講,不管是C、C++、仍是pas,首先要把源文件編譯成中間代碼文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,這個動做叫作編譯(compile)。而後再把大量的Object File合成執行文件,這個動做叫做連接(link)。
編譯時,編譯器須要的是語法的正確,函數與變量的聲明的正確。對於後者,一般是你須要告訴編譯器頭文件的所在位置(頭文件中應該只是聲明,而定義應該放在C/C++文件中),只要全部的語法正確,編譯器就能夠編譯出中間目標文件。通常來講,每一個源文件都應該對應於一箇中間目標文件(O文件或是OBJ文件)。
連接時,主要是連接函數和全局變量,因此,咱們可使用這些中間目標文件(O文件或是OBJ文件)來連接咱們的應用程序。連接器並無論函數所在的源文件,只管函數的中間目標文件(Object File),在大多數時候,因爲源文件太多,編譯生成的中間目標文件太多,而在連接時須要明顯地指出中間目標文件名,這對於編譯很不方便,因此,咱們要給中間目標文件打個包,在Windows下這種包叫「庫文件」(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
總結一下,源文件首先會生成中間目標文件,再由中間目標文件生成執行文件。在編譯時,編譯器只檢測程序語法,和函數、變量是否被聲明。若是函數未被聲明,編譯器會給出一個警告,但能夠生成Object File。而在連接程序時,連接器會在全部的Object File中找尋函數的實現,若是找不到,那到就會報連接錯誤碼(Linker Error),在VC下,這種錯誤通常是:Link 2001錯誤,意思說是說,連接器未能找到函數的實現。你須要指定函數的Object File.
好,言歸正傳,GNU的make有許多的內容,閒言少敘,仍是讓咱們開始吧。
Makefile 介紹
———————
make命令執行時,須要一個 Makefile 文件,以告訴make命令須要怎麼樣的去編譯和連接程序。
首先,咱們用一個示例來講明Makefile的書寫規則。以便給你們一個感興認識。這個示例來源於GNU的make使用手冊,在這個示例中,咱們的工程有8個C文件,和3個頭文件,咱們要寫一個Makefile來告訴make命令如何編譯和連接這幾個文件。咱們的規則是:
1)若是這個工程沒有編譯過,那麼咱們的全部C文件都要編譯並被連接。
2)若是這個工程的某幾個C文件被修改,那麼咱們只編譯被修改的C文件,並連接目標程序。
3)若是這個工程的頭文件被改變了,那麼咱們須要編譯引用了這幾個頭文件的C文件,並連接目標程序。
只要咱們的Makefile寫得夠好,全部的這一切,咱們只用一個make命令就能夠完成,make命令會自動智能地根據當前的文件修改的狀況來肯定哪些文件須要重編譯,從而本身編譯所須要的文件和連接目標程序。
1、Makefile的規則
在講述這個Makefile以前,仍是讓咱們先來粗略地看一看Makefile的規則。
target ... : prerequisites ...
command
...
...
target也就是一個目標文件,能夠是Object File,也能夠是執行文件。還能夠是一個標籤(Label),對於標籤這種特性,在後續的「僞目標」章節中會有敘述。
prerequisites就是,要生成那個target所須要的文件或是目標。
command也就是make須要執行的命令。(任意的Shell命令)
這是一個文件的依賴關係,也就是說,target這一個或多個的目標文件依賴於prerequisites中的文件,其生成規則定義在command中。說白一點就是說,prerequisites中若是有一個以上的文件比target文件要新的話,command所定義的命令就會被執行。這就是Makefile的規則。也就是Makefile中最核心的內容。
說到底,Makefile的東西就是這樣一點,好像個人這篇文檔也該結束了。呵呵。還不盡然,這是Makefile的主線和核心,但要寫好一個Makefile還不夠,我會之後面一點一點地結合個人工做經驗給你慢慢到來。內容還多着呢。:)
2、一個示例
正如前面所說的,若是一個工程有3個頭文件,和8個C文件,咱們爲了完成前面所述的那三個規則,咱們的Makefile應該是下面的這個樣子的。
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
反斜槓(\)是換行符的意思。這樣比較便於Makefile的易讀。咱們能夠把這個內容保存在文件爲「Makefile」或「makefile」的文件中,而後在該目錄下直接輸入命令「make」就能夠生成執行文件edit。若是要刪除執行文件和全部的中間目標文件,那麼,只要簡單地執行一下「make clean」就能夠了。
在這個makefile中,目標文件(target)包含:執行文件edit和中間目標文件(*.o),依賴文件(prerequisites)就是冒號後面的那些 .c 文件和 .h文件。每個 .o 文件都有一組依賴文件,而這些 .o 文件又是執行文件 edit 的依賴文件。依賴關係的實質上就是說明了目標文件是由哪些文件生成的,換言之,目標文件是哪些文件更新的。
在定義好依賴關係後,後續的那一行定義瞭如何生成目標文件的操做系統命令,必定要以一個Tab鍵做爲開頭。記住,make並無論命令是怎麼工做的,他只管執行所定義的命令。make會比較targets文件和prerequisites文件的修改日期,若是prerequisites文件的日期要比targets文件的日期要新,或者target不存在的話,那麼,make就會執行後續定義的命令。
這裏要說明一點的是,clean不是一個文件,它只不過是一個動做名字,有點像C語言中的lable同樣,其冒號後什麼也沒有,那麼,make就不會自動去找文件的依賴性,也就不會自動執行其後所定義的命令。要執行其後的命令,就要在make命令後明顯得指出這個lable的名字。這樣的方法很是有用,咱們能夠在一個makefile中定義不用的編譯或是和編譯無關的命令,好比程序的打包,程序的備份,等等。 程序員