1. 程式人生 > 實用技巧 >Makefile的介紹與使用(一)

Makefile的介紹與使用(一)

Makefile是Linux系統中必不可缺的一部分,Makefile可以簡單理解為一份規則,也就是一個系統的執行指南,其規定了你所要執行的程式如何去做,需要什麼檔案參與等等。這部分就簡單瞭解一下子目錄下的Makefile。

一、最簡單的Makefile

最簡單的Makefile所需要的只有三個:target(目標檔案)、prerequisites(所依賴的檔案)、command(此目標檔案所需要執行的命令);

舉一個例子:

hello : hello.o
    gcc hello.o -o hello

上例中,左上角的hello便是目標檔案(target),在其:後面的hello.o便是所依賴的.o檔案(prerequisites),而下一行的gcc hello.o -o hello便是此目標檔案所需執行的命令(command)。

gcc的格式並沒有固定,例如也是可行的,但是hello.o必須在hello的前面,即是錯誤的。(可以看成‘ -o hello ’必須以前移動)

接下來舉一個完整的例子:

hello : hello.o
    gcc -o hello hello.o

hello.o : hello.c
    gcc -c hello.c

clean :
    rm *.o hello

簡單說明一下上面程式碼的意思:  1、生成一個hello程式,此程式依賴於hello.o檔案,gcc...為其執行命令的方法。

                 2、hello.o檔案依賴於hello.c檔案。(一開始,hello.o檔案是不存在的,因為gcc -chello.c這個命令才會生成)

                 3、清除命令:清除(rm)所有的.o檔案(*.o)以及hello程式。

需要注意的是: 1、gcc...前面的一大片空白是Tab鍵,Makefile的編寫時非常嚴肅的,多一個空格都會出錯,這裡是Tab鍵就不要用空格隔開,否則就會出錯。

        2、依賴關係是每一組檔案都會有的,而依賴關係的實質即說明了目標檔案是由哪些檔案生成的,亦或者是由哪些檔案更新的。

        3、clean不是一個檔案而是一個動作,其冒號後什麼都沒有的時候,make就不會去找其依賴,也就不會執行其命令。

        4、make會對target和prerequisite的修改日期進行比較,如果prerequisite檔案的日期比target檔案的日期要新,亦或者target檔案不存在,那麼make才會執行後續的命令。

接下來,我們執行一下此Makefile。

我們需要先編寫一個.c檔案,要求其在終端顯示“hello!!”

1 #include <stdio.h>
2 
3 int main(void)
4 {
5     printf("hello!!\n");
6     return 0;
7 }

此時我們這個資料夾內便有了hello.c和Makefile。

開啟終端進行make。

可以看出終端執行了兩條command命令,同時在此資料夾中生成了一個hello程式及hello.o檔案。

接下來我們跑一下所生成的hello程式。

可以看出hello程式其實就是hello.c的執行。

二、make的工作情況

上面演示時用了make執行,所以這裡簡單說明一下make的工作情況,看看make是如何工作的。

可以看出,我們在執行make時,出現了gcc -c hello.c以及gcc -o hello hello.o兩行程式碼,而這兩行是和我們所輸入command是一模一樣的。而我們所屬入的command是target找到prerequisite的路徑。由此也就可以看出make的工作情況為:通過command找到prerequisite,並以此生成target。如果在尋找依賴的過程中出現了問題(例如依賴找不到),那麼make就會退出並報錯。例如的執行就會找不到檔案,從而報錯。

而在make過程中,它會對prerequisite和target二者的日期進行比較,並以二者時間判斷是否進行重新連結。假如我們對.c檔案進行了更改,那麼.c檔案的時間就會在.o檔案之後,你再次make的時候它就會重新連結.c生成新的.o檔案,以此類推所生成的程式也就會更新。

通過“一”中的例子可以看出,clean並沒有被執行,因為其冒號後面是沒有東西的。而我們使用make也可以強制執行它,只要在終端輸入makeclean就可以強制執行clean命令,使得生成的hello.o檔案和hello程式全部清除。

三、在Makefile中使用變數

在我們編寫小程式的時候我們可以直接這樣將target、prerequisite以及command寫上去,沒什麼問題,也容易排查錯誤。但是在我們編寫一些比較大的程式的時候,我們這樣直接編寫,就會看起來很亂,並且容易敲錯,到時候再回來找錯就會非常困難。所以我們一般都會使用變數來將系統的一些定義,以後再次使用這個值的時候就會方便很多,而且也不易出錯。

Makefile中的變數有四種:自定義變數,自動變數,預定義變數,環境變數。

而我們一般情況下使用的都是自定義變數和自動變數,我也就對這兩種變數進行分析。

自定義變數:定義變數使用的變數名 := 變數值 使用變數 $(變數名)

仍然以hello為例:

HEL := hello.o
hel := hello

$(hel) : $(HEL)
    gcc -o $(hel) $(HEL)

hello.o : hello.c
    gcc -c hello.c

clean :
    rm *.o hello

可以看到這時我將HEL設為hello.o的變數名;將hel設為hello的變數名,然後將下面對應的hello和hello.o部分全部修改為$(變數名)的形態。 (可以看見,我有部分.o檔案改動,有部分維持原樣)

嘗試make,會發現此時和原來沒有改變的時候是一樣的。

自動變數:特殊巨集‘$@’,‘$^’,‘$<’等等

$@:規則的目標檔名 $^:規則的所有依賴檔案列表 $<:規則的第一個依賴檔名   (如果想知道更多的特殊巨集的作用可以自行百度)

所謂的自動變數便是系統已經定義好的一種使用規則,它並不會單獨特指某一變數,而是根據情況使用。

HEL := hello.o
hel := hello

$(hel) : $(HEL)
    gcc -o $@ $^

hello.o : hello.c
    gcc -c $^

clean :
    rm *.o hello

這樣寫的情況下,make的情況和上面是一樣的,是gcc -o hello.c和gcc -o hello hello.o