建立Makefile
此篇文章十分清楚易懂,可作為初學之用
使用make好處
- 方便專案管理
- 會透過檔案比對,依照相依性來編譯,不會全都編浪費時間
- 可以同時編譯函式庫或是檔案
make常用指令
make -k
: 會讓make在遇到錯誤的時候仍然運行,而不會在第一個問題中斷make -n
: 只印出將會進行的工作,而不會真的執行make -f makefile_name
: make預設執行名為makefile的檔案,此命令可指定makefile檔案名稱和位置#
: 註解
make指令格式
預設的target是"all", 若makefile中沒有all, 則是第一個target
1 | make [option] [target] |
撰寫makefile檔案
makefile是由一堆「目標」和其「相依性檔案」還有「法則」所組成的
- [target] 目標 - 產生出來的東西
- 用了
.PHONY
來指定 clean 為 fake 項目,所以 make 不會去檢查目錄中是否存在了一個名為 clean 的檔案。如此也可以提昇 make 的執行效率- 常用的偽target
- 用了
1 | .PHONY: all clean install |
- [dependency] 相依性項目 - 若 dependency 的檔案有改動過,則重新產生 target
- [rule] 法則 - 如何產生目標
- 使用Tab作為開頭
- 使用 Shell Script 語法
1 | [target]: [dependency] [dependency] |
註1: makefile的命令和shell不同的地方: 每行命令在分開的shell中獨立執行
1 | wrongClean: |
~如果寫錯的話,至少錯誤的makefile被刪除了~
註2: 在命令行首加上@
,代表執行程式但不顯示在螢幕上。在命令行首加上-
,代表執行命令時回傳非零值仍然繼續執行()
例:要產生all的話,需要兩個檔案myapp和app.doc(主程式和說明檔),make開始會去找尋如何產生myapp和 app.doc的方法,所以myapp會成為下一個要產生出來的目標。用gcc main.o a.o b.o -o myapp來產生myapp……,以此類推
1 | all: myapp app.doc |
在makefile中, 相依性順序是很重要的
Makefile的變數和巨集(macro)
設定變數的方法
- 將export命令放在shell啟動script
.bashrc
或.zshrc
…- 永遠都有效
- 在shell中設定變數
export CC=gcc
- 終端機開啟期間有效
- 在執行命令前設定
CC=gcc | make
- 針對特定命令的變數
- 設定變數需要在實際命令之前
make
可以直接設定變數make CFLAGS="-g -Wall"
CFLAGS="-g -Wall" make
- 在makefile中設定
?=
:若變數未定義,則替它指定新的值。否則,採用原有的值。FOO ?= bar
: 若 FOO 未定義,則 FOO = bar;若 FOO 已定義,則 FOO 的值維持不變。
指定時,等號兩側不可有空格
改變副檔名
- SRC=a.c b.c
- OBJ=$(SRC:.c=.o) # equals to a.o b.o
有幾個特別的內部巨集,讓makeifle更加簡明
$?
代表需要重建的相依性項目(檔案有被更新過),也就是 dependencies 中,比 targets 的修改日期還新檔案。$@
目前的target$*
不含副檔名的target$<
第一個 dependency
還有兩個有用的特別字元,可以加在要執行的命令之前
-
即使該行指令出錯,也不會中斷執行@
不會在terminal顯示該行命令
1 | CC = gcc |
Makefile 隱性法則(implicit rule)
1 | CC = gcc |
makefile會在main.o自動產生規則 gcc -Wall -ansi -g -c -o main.o main.c
- 若dependency為c:
$(CC) $(CFLAGS) $(LDFLAGS) [dependencies]
- 若dependency為c++:
$(CXX) $(CXXFLAGS) $(LDFLAGS) [dependencies]
- 若dependency為object(建立執行檔):
$(CC) $(LDFLAGS) [dependencies] $(LDLIBS)
隱性法則列表
Makefile 檔尾法則
使用檔尾的延伸檔名作為法則,格式 .[old_suffix].[new_suffix]
1 | .c.o: |
檔尾法則只可以用在本目錄
這目錄下面所有的.c檔變成.o檔,而法則就是去編譯它,而如果你想更懶一點的話還可以完全不寫,直接使用內建的法則,這樣也可以直接把目錄下面的所有檔都編好,為什麼呢?因為你要編出myapp的時候需要使用到$(OBJS)所以,就算你不寫.c.o或是任何的法則,make預設都會自己產生.o檔讓你可以連結出主程式。
makefile也有支援萬用字元
1 | %.o: %.c |
萬用字元法則比較適合用於編譯一個大型的函式庫,而檔尾法則適合編譯一個目錄下面所有的檔案。
專案討論
Make Makefile with Release and Debug build
simple-makefile-with-release-and-debug-builds-best-practices
how-can-i-configure-my-makefile-for-debug-and-release-builds
參考資料
- 撰寫Makefile教學
- Makefile 語法簡介