Makefile簡易教程

2021-08-25 08:15:58 字數 4306 閱讀 3350

本文部分內容引用:

中文維基百科。

乙個簡單的makefile教程。

在軟體開發中,make通常被視為一種軟體構建工具。該工具主要經由讀取一種名為「makefile」或「makefile」的檔案來實現軟體的自動化建構。它會通過一種被稱之為「target」概念來檢查相關檔案之間的依賴關係,這種依賴關係的檢查系統非常簡單,主要通過對比檔案的修改時間來實現。在大多數情況下,我們主要用它來編譯源**,生成結果**,然後把結果**連線起來生成可執行檔案或者庫檔案。

與大多數古老的unix工具一樣,make也分別有著人數眾多的擁護者和反對者。它在適應現代大型軟體專案方面有著許許多多的問題。但是,依然有很多人堅定地認為(包括我)它能應付絕大多數常見的情況,而且使用非常的簡單,功能強大,表達清楚。無論如何,make如今仍然被用來編譯很多完整的作業系統,而且它的那些「更為現代」的替代品們在基本操作上與它沒有太大差別。

當然,隨著現代的整合開發環境(ide)的誕生,特別是非unix的平台上,很多程式設計師不再手動管理依靠關係檢查,甚至不用去管哪些檔案是這個專案的一部分,而是把這些任務交給了他們的開發環境去做。類似的,很多現代的程式語言有自己專屬的、能高效配置依賴關係的方法(譬如ant)。

make程式經歷過各方多次的改寫與重寫,各方都依據自己的需要做了一些特定的改良。目前市面上主要流行有以下幾種版本:

我們可以用k&r c中4.5那個例子來做個說明。在這個例子中,我們會看到乙份主程式**(main.c)、三份函式**(getop.c、stack.c、getch.c)以及乙個標頭檔案(calc.h)。通常情況下,我們需要這樣編譯它:

gcc -o calc main.c getch.c getop.c stack.c
如果沒有makefile,在開發+除錯程式的過程中,我們就需要不斷地重複輸入上面這條編譯命令,要不就是通過終端的歷史功能不停地按上下鍵來尋找最近執行過的命令。這樣做兩個缺陷:

一旦終端歷史記錄被丟失,我們就不得不從頭開始;

任何時候只要我們修改了其中乙個檔案,上述編譯命令就會重新編譯所有的檔案,當檔案足夠多時這樣的編譯會非常耗時。

那麼makefile又能做什麼呢?我們先來看乙個最簡單的makefile檔案:

calc: main.c getch.c getop.c stack.c

gcc -o calc main.c getch.c getop.c stack.c

現在你看到的就是乙個最基本的makefile語句,它主要分成了三個部分,第一行冒號之前的calc,我們稱之為目標(target),被認為是這條語句所要處理的物件,具體到這裡就是我們所要編譯的這個程式calc。冒號後面的部分(main.c getch.c getop.c stack.c),我們稱之為依賴關係表,也就是編譯calc所需要的檔案,這些檔案只要有乙個發生了變化,就會觸發該語句的第三部分,我們稱其為命令部分,相信你也看得出這就是一條編譯命令。現在我們只要將上面這兩行語句寫入乙個名為makefile或者makefile的檔案,然後在終端中輸入make命令,就會看到它按照我們的設定去編譯程式了。

請注意,在第二行的「gcc」命令之前必須要有乙個tab縮排。語法規定makefile中的任何命令之前都必須要有乙個tab縮排,否則make就會報錯。

接下來,讓我們來解決一下效率方面的問題,先初步修改一下上面的**:

cc = gcc

prom = calc

source = main.c getch.c getop.c stack.c

$(prom): $(source)

$(cc) -o $(prom) $(source)

如你所見,我們在上述**中定義了三個常量cc、prom以及source。它們分別告訴了make我們要使用的編譯器、要編譯的目標以及原始檔。這樣一來,今後我們要修改這三者中的任何一項,只需要修改常量的定義即可,而不用再去管後面的**部分了。

請注意,很多教程將這裡的cc、prom和source稱之為變數,個人認為這是不妥當的,因為它們在整個檔案的執行過程中並不是可更改的,作用也僅僅是字串替換而已,非常類似於c語言中的巨集定義。或者說,事實上它就是乙個巨集。

但我們現在依然還是沒能解決當我們只修改乙個檔案時就要全部重新編譯的問題。而且如果我們修改的是calc.h檔案,make就無法察覺到變化了(所以有必要為標頭檔案專門設定乙個常量,並將其加入到依賴關係表中)。下面,我們來想一想如何解決這個問題。考慮到在標準的編譯過程中,原始檔往往是先被編譯成目標檔案,然後再由目標檔案連線成可執行檔案的。我們可以利用這一點來調整一下這些檔案之間的依賴關係:

cc = gcc

prom = calc

deps = calc.h

obj = main.o getch.o getop.o stack.o

$(prom): $(obj)

$(cc) -o $(prom) $(obj)

main.o: main.c $(deps)

$(cc) -c main.c

getch.o: getch.c $(deps)

$(cc) -c getch.c

getop.o: getop.c $(deps)

$(cc) -c getop.c

stack.o: stack.c $(deps)

$(cc) -c stack.c

這樣一來,上面的問題顯然是解決了,但同時我們又讓**變得非常囉嗦,囉嗦往往伴隨著低效率,是不祥之兆。經過再度觀察,我們發現所有.c都會被編譯成相同名稱的.o檔案。我們可以根據該特點再對其做進一步的簡化:

cc = gcc

prom = calc

deps = calc.h

obj = main.o getch.o getop.o stack.o

$(prom): $(obj)

$(cc) -o $(prom) $(obj)

%.o: %.c $(deps)

$(cc) -c $< -o $@

在這裡,我們用到了幾個特殊的巨集。首先是%.o:%.c,這是乙個模式規則,表示所有的.o目標都依賴於與它同名的.c檔案(當然還有deps中列出的標頭檔案)。再來就是命令部分的$《和$@,其中$《代表的是依賴關係表中的第一項(如果我們想引用的是整個關係表,那麼就應該使用$^),具體到我們這裡就是%.c。而$@代表的是當前語句的目標,即%.o。這樣一來,make命令就會自動將所有的.c原始檔編譯成同名的.o檔案。不用我們一項一項去指定了。整個**自然簡潔了許多。

到目前為止,我們已經有了乙個不錯的makefile,至少用來維護這個小型工程是沒有什麼問題了。當然,如果要進一步增加上面這個專案的可擴充套件性,我們就會需要用到一些makefile中的偽目標和函式規則了。例如,如果我們想增加自動清理編譯結果的功能就可以為其定義乙個帶偽目標的規則;

cc = gcc

prom = calc

deps = calc.h

obj = main.o getch.o getop.o stack.o

$(prom): $(obj)

$(cc) -o $(prom) $(obj)

%.o: %.c $(deps)

$(cc) -c $< -o $@

clean:

rm -rf $(obj) $(prom)

有了上面最後兩行**,當我們在終端中執行make clean命令時,它就會去刪除該工程生成的所有編譯檔案。

另外,如果我們需要往工程中新增乙個.c或.h,可能同時就要再手動為obj常量再新增第乙個.o檔案,如果這列表很長,**會非常難看,為此,我們需要用到makefile中的函式,這裡我們演示兩個:

cc = gcc

prom = calc

deps = $(shell find ./ -name "*.h")

src = $(shell find ./ -name "*.c")

obj = $(src:%.c=%.o)

$(prom): $(obj)

$(cc) -o $(prom) $(obj)

%.o: %.c $(deps)

$(cc) -c $< -o $@

clean:

rm -rf $(obj) $(prom)

其中,shell函式主要用於執行shell命令,具體到這裡就是找出當前目錄下所有的.c和.h檔案。而$(src:%.c=%.o)則是乙個字元替換函式,它會將src所有的.c字串替換成.o,實際上就等於列出了所有.c檔案要編譯的結果。有了這兩個設定,無論我們今後在該工程加入多少.c和.h檔案,makefile都能自動將其納入到工程中來。

Makefile簡易教程

在軟體開發中,make通常被視為一種軟體構建工具。該工具主要經由讀取一種名為 makefile 或 makefile 的檔案來實現軟體的自動化建構。它會通過一種被稱之為 target 概念來檢查相關檔案之間的依賴關係,這種依賴關係的檢查系統非常簡單,主要通過對比檔案的修改時間來實現。在大多數情況下,...

Makefile簡易教程

makefile簡介 在軟體開發中,make通常被視為一種軟體構建工具。該工具主要經由讀取一種名為 makefile 或 makefile 的檔案來實現軟體的自動化建構。它會通過一種被稱之為 target 概念來檢查相關檔案之間的依賴關係,這種依賴關係的檢查系統非常簡單,主要通過對比檔案的修改時間來...

自己寫makefile簡易教程

前言 由於個人精力原因,我只寫makefile的內容,c語言以及shell指令又不了解的同學,請先學習c語言以及shell指令。寫單個檔案的makefile時,我們需要了解最基本的格式 target prerequistes.command target 是指生成的專案。prerequistes 是...