1. 程式人生 > >寫一個簡單的makefile

寫一個簡單的makefile

一個簡單的Makefile教程


Makefiles是組織程式碼編譯的一種簡單方法。本教程甚至沒有描述使用make的可能性,
而是作為初學者指南,以便您可以快速輕鬆地為中小型專案建立自己的makefile。
一個簡單的例子


讓我們從以下三個檔案開始:hellomake.c,hellofunc.c和hellomake.h,它們
分別代表一個典型的主程式,一些單獨的檔案中的某些功能程式碼和一個包含檔案。
hellomake.c hellofunc.c hellomake.h


#include "hellomake.h"


int main() {
myPrintHelloMake();
return 0;
}


#include <stdio.h>
#include "hellomake.h"
void myPrintHelloMake()
{
printf("hello make!\n");
}




/ *
例如包含檔案
* /
#ifndef HELLOMAKE_H
#define HELLOMAKE_H


void myPrintHelloMake();


#endif //HELLOMAKE_H


通常情況下,您可以通過執行以下命令來編譯該程式碼集合:


gcc -o hellomake hellomake.c hellofunc.c -I .


這編譯了兩個.c檔案並將可執行檔案命名為hellomake。 -I .是包含的,所以gcc會在
當前目錄(.)中查詢包含檔案hellomake.h。如果沒有makefile,測試/修改/除錯
週期的典型方法是使用終端中的向上箭頭返回到上一個編譯命令,所以不必每次都輸
入,特別是一旦新增一些更多的.c檔案混合。


不幸的是,這種彙編方法有兩個缺點。首先,如果你失去了編譯命令或切換電腦,
你必須從頭開始重新輸入,這是最好的低效率。其次,如果您只是對一個.c檔案
進行更改,則每次重新編譯它們也是耗時且效率低下的。所以,現在是時候看看
我們能用makefile做什麼了。


你可以建立的最簡單的makefile如下所示:
Makefile 1


hellomake:hellomake.c hellofunc.c
     gcc -o hellomake hellomake.c hellofunc.c -I .


如果你把這個規則放到一個名為Makefile或makefile的檔案中,然後在命令列
中輸入make,它將執行編譯命令,就像你在makefile中寫的一樣。請注意,不
帶引數的make執行檔案中的第一條規則。而且,通過在下列命令之後放置命令
依賴於第一行的檔案列表,如果知道這些檔案中的任何一個發生更改,hellomake規則
就需要立即執行。你已經解決了問題#1,可以避免重複使用向上的箭頭,
尋找你最後的編譯命令。但是,這個系統在編譯最新變化方面仍然不夠高效。


一個非常重要的要注意的是在makefile中的gcc命令之前有一個選項卡。任何命
令開始時都必須有一個標籤


為了更高效一些,我們來嘗試一下:
Makefile 2


CC=gcc
CFLAGS=-I.


hellomake:hellomake.o hellofunc.o
     $(CC)-o hellomake hellomake.o hellofunc.o -I.


所以現在我們定義了一些常量CC和CFLAGS。事實證明,這些是特殊的常量,
使我們想要編譯hellomake.c和hellofunc.c檔案。特別是,巨集CC是要使用
的C編譯器,而CFLAGS是傳遞給編譯命令的標誌列表。通過將物件檔案hellomake.o和
hellofunc.o放入依賴列表和規則中,make知道它必須先單獨編譯.c版本,然後構
建可執行檔案hellomake。
(執行make命令時,卻報如下錯誤:
Makefile ...2 ... 遺漏分隔符...停止


經過調查,發現是這樣的:


Makefile的 hellomake: 行被稱為rule。
第二行,是具體的編譯動作。開頭不可以有空格,留白是由 按tab鍵形成的。


去掉空格,改為tab鍵後,再執行make命令,成功。)




使用這種形式的makefile對於大多數小型專案來說已經足夠了。但是,有一件事
是缺少的:依賴於包含檔案。例如,如果要對hellomake.h進行更改,make將不
會重新編譯.c檔案,即使它們需要。為了解決這個問題,我們需要告訴make所有
的.c檔案都依賴於某些.h檔案。我們可以通過編寫一個簡單的規則並將其新增到
生成檔案來完成。
Makefile 3


CC=gcc
CFLAGS=-I.
DEPS = hellomake.h


%.o: %.c $(DEPS)
$(CC) -c -o
[email protected]
$< $(CFLAGS)


hellomake: hellomake.o hellofunc.o 
gcc -o hellomake hellomake.o hellofunc.o -I.


首先在此新增建立巨集DEPS,這是.c檔案所依賴的.h檔案集。然後我們定義一個適用
於以.o字尾結尾的所有檔案的規則。該規則指出.o檔案依賴於.c版本的檔案和
DEPS巨集中包含的.h檔案。然後,規則說要生成.o檔案,需要使用CC巨集中定義的
編譯器編譯.c檔案。 -c標誌表示生成目標檔案,-o $ @表示將編譯的輸出
放在:左邊的檔案中,$ <是依賴項列表中的第一個專案,而CFLAGS巨集定義如上。


作為最後的簡化,讓我們分別使用特定的巨集$ @和$ ^(分別是:的左側和右側)
來使整體編譯規則更加通用。在下面的例子中,所有的包含檔案應該被列為
巨集DEPS的一部分,所有的目標檔案都應該被列為巨集OBJ的一部分。
Makefile 4


CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
OBJ = hellomake.o hellofunc.o 


%.o: %.c $(DEPS)
$(CC) -c -o
[email protected]
$< $(CFLAGS)


hellomake: $(OBJ)
gcc -o [email protected] $^ $(CFLAGS)


那麼,如果我們想開始將.h檔案放在include目錄下,我們的原始碼放
在src目錄下,並且在lib目錄下放置了一些本地庫檔案呢?另外,我們可
以以某種方式隱藏遍佈各處的煩人的.o檔案嗎?答案當然是肯定的。以下makefile定
義了include和lib目錄的路徑,並將目標檔案放在src目錄下的obj子目錄中。
它還為要包含的任何庫定義了一個巨集,如數學庫-lm。這個makefile應該位於src目
錄下。請注意,如果您輸入make clean,它還包含一個清理源和目標目錄的規則。 
.PHONY規則保持使用一個名為clean的檔案做某事。
Makefile 5


IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)


ODIR=obj
LDIR =../lib


LIBS=-lm


_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))


_OBJ = hellomake.o hellofunc.o 
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))




$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o
[email protected]
$< $(CFLAGS)


hellomake: $(OBJ)
gcc -o [email protected] $^ $(CFLAGS) $(LIBS)


.PHONY: clean


clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)


ODIR=obj
LDIR =../lib


LIBS=-lm


_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))


_OBJ = hellomake.o hellofunc.o 
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))




$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o [email protected] $< $(CFLAGS)


hellomake: $(OBJ)
gcc -o [email protected] $^ $(CFLAGS) $(LIBS)


.PHONY: clean


clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~


所以現在你有一個完美的makefile,你可以修改來管理中小型的軟體專案。
你可以新增多個規則到一個生成檔案;你甚至可以建立呼叫其他規則的規則。
有關makefile和make函式的更多資訊,請檢視GNU Make Manual,
它會告訴你比你想知道的更多的東西。