【學記】Makefile 語法與變數示範
What is Makefile?
甚麼是 Makefile 呢?
舉例而言,像是在這份 Project 中,可以透過 make 指令編譯前端與開啟後端,在下達 make 指令時 make 就會去查找一個叫 Makefile(或者 makefile)的檔案,從其中定義的規則去執行相對應的命令。
Benefits of Makefile?
使用 Makefile 有許多的好處:
- 管理 files 間的 dependency:
假設 a.c depends on b.c,此時若 b.c 修改了則 a.c 也應該要重新編譯(或 link)。 - 建立常用指令的捷徑:
在同樣的一份 Project 中,會需要透過一些 commands 跟程式互動,若 command 很長的話每次都要重打就會很麻煩,此時就可以透過 makefile 來加速流程。
Grammar
說了這麼多,那具體而言要怎麼寫 Makefile 呢?Makefile 語法基本可以分成以下幾部分:
- Rule
- Variable
- Assignment
- Special character
Rule
如同上面說的,在下達 make 指令時 make 會到 Makefile 中查找規則,一般 Makefile rule 如下:
1 | target: target.c |
target就是目標檔案。- 在
target後可以看到一個冒號,在冒號後的就是 dependency,當 dependency 改變時,make 會偵測到並重新 compile dependency 再 compile target。 $(CC) target.c就是要執行的命令。- 命令前必須為 tab,也就是 \t 字元。
所以當執行 make target 的時候,實際是執行 gcc target.c。
$(CC)將CC這個變數展開,變數定義會在下文介紹。
Variable
Makefile 中有許多特殊的 variable,像是上面有用到的 CC,而這些 variable 可以大致分為以下這幾種:
Compile related variables
- CC:指定要使用的 c compiler,e.g. gcc
- CXX:指定要使用的 c++ compiler,e.g. g++
- LD:指定要使用的 linker,e.g. ld
Automatic variables
- $@:target 檔名,如果 makefile rule 如下:則 $@ 就代表
1
2target: target.c
$(CC) target.ctarget。 - $^:所有的 dependency,如果 makefile rule 如下:則 $^ 會 expand 成
1
2target: target.c dependency.c
$(CC) target.ctarget.c, dependency.c。 - $<:第一個 dependency,如果 makefile rule 如下:則 $< 為
1
2target: target.c dependency.c
$(CC) target.ctarget.c。 - $*:使用在 pattern rule,將在 special character 的章節說明。
Common flag variables
- CFLAG:c files 的 compile flags。
- CXXFLAGS:c++ files 的 compile flags。
- LDFLAGS:linker 的 flags。
Target specific variables
可以在 Makefile 中針對特定的 target 定義 variable,如下:
1 | hw2a: CFLAGS += -pthread |
若 Makefile 如下:
1 | hw2a: hw2a.cc |
則在執行 hw2a 的 rule 時,CFLAGS 就會多上 -pthread 的 flag。因此,可以透過此方法設定對於某些 target file 加上特別的 compile flag。
Assignment
在 Makefile 中可以自定義 variables,而 variable 有很多種 assign 的方法:
- Simple assignment (=):要注意的是,
1
CC = gcc
=這種 assignment 屬於 defer assignment,也就是會等到該變數被使用時才會展開右手邊的內容取得最終的值。 - Immediate assignment (:=):
1
DATE := $(shell date)
$(shell ...)可以執行 shell command,上面就是透過 shell 取得時間後存在DATE變數中
與=不同的是,:=這種 asignment 會立即展開右邊取得最終的值,所以在底下的 Makefile 中:1
2DATE := $(shell date)
DATE_DELAY = $(shell date)DATE與DATE_DELAY時間將會不同,DATE_DELAY的時間為該變數被用到時,而DATE則為 Makefile parse 到這一行的當下。 - Appending assignment (+=):
例如 CFLAGS 本來為 -O3則1
CFLAGS += -pthread
CFLAGS會變成-O3 -pthread。通常可以配合上述的 Target specifi variable 使用,對於特定的 target 使用+=給定特別的 flags。 - Conditional assignment (?=):
若變數未定義則指定新的值,否則採用原有的值。與=同為 defer assignment。
Special character
在 Makefile 中也有很多的特殊字元,每個都有特別的意義,如下:
- @:make 一般會 print 出要執行的指令,在 command 前加上 @ 則不會 print 出
1
2
3
clean:
@rm -f $(TARGETS) $(TARGETS:=.o) - %:wildcard character,也就是可以 match 各種的 pattern,使用方式如下:假設下
1
2%.o: %.c
$(CC) $(CFLAGS) -c $< -o $*.omake target.o,此時會執行gcc -c target.c -o target.o,可以看到$*會自動替換成 % 所 match 到的字元。 - -:makefile 只要遇到任何錯誤都會中斷執行,加
-在 command 前面,使得就算 command 出錯,也不影響後續的 commands,如下:1
2
3
clean:
-rm *.o
其他
PHONY
這是一個很重要的 kerword。在前文中很常看到 PHONY 的身影,這個 keyword 是用來宣告這些 rule 是一個指令,而不是一個檔案,如下:
1 |
|
如果沒有加 .PHONY: clean,此時資料夾中又有 clean 這個檔案,則 makefile 會認為 clean 這個 target up to date,因此 clean 下的指令將永遠不會被執行。而加上 .PHONY: clean 可以解決這樣的問題。
Makefile print
Makefile 內部其實有很多預設的變數,可以透過以下指令查看:
1 | make -p > make_print |
Compiler
有時候會見到以下這樣的 Makefile:
1 | CC = gcc |
此時或許會懷疑又沒有說明 make hw2a 的 rule,make 要怎麼知道使用哪個 compiler 呢?
可以透過前面說的 make -p 查看,其實 make 有很多預設的使用情境,在沒有說明 compiler 的情況下,對於 .c file make 會優先使用 CC 這個 variable 內的 compiler,而對於 .cc or .cpp 則會使用 CXX compiler。
Set variable in CLI
variable 也可以直接在下 make command 時設定,如下:
1 | make CC=clang CXX=clang++ |





