你就想像你在寫 Bash 或 Python 這種腳本語言,只不過 make 更針對編譯這個情境設計。
既然是腳本語言,那肯定以下幾個是不能少的:
- 指令
- 變數
- 函式
- 條件判斷
四大要件
指令
就是你在命令列(Command Line)會打的那些命令。但請注意,指令前面必須要是以 <Tab> 縮排,不能用空白鍵!
變數
在 Makefile 習慣變數名稱用大寫,要取變數值必須用 $(VAR) 或 ${VAR}。
變數賦值
:=(立即賦值 / Simple Assignment):定義的當下就會把值並確定下來,最常用且最推薦。=(延遲賦值 / Recursive Assignment):用到該變數時,才會去尋找並展開它當下的值。這可能導致無窮迴圈或非預期結果。?=(條件賦值 / Conditional Assignment):如果這個變數前面還沒有被定義過,才會賦予它這個值;如果已經有值了,這行就會被忽略。+=(附加賦值 / Append):將後面的字串接在原本變數內容的後面(會自動用一個空白字元隔開)。
自動化變數
$@工作目標檔名$*工作目標的主檔名$<第一個必要條件的檔名$^所有必要條件的檔名,並以空格隔開這些檔名$?所有比工作目標還要新的必要條件檔名
其他符號修飾
%代表所有字串,像是 Regex 的*@不要顯示當前執行的命令-就算當前命令出錯,也不要中斷後續的執行
函式
在 Makefile 中叫做目標(Target),和函式一樣可以被呼叫和依賴:
all:
# Compile Commands
clean:
# Clean Commands
我們可以像呼叫函式那樣呼叫任意區塊,那當然也可能會有參數(依賴條件):
target: <obj1> <obj2>
# Commands
Makefile 如果在呼叫 make 不接任何參數,就會執行第一個區塊。而 make 會先確認參數裡的每個區塊都準備完畢,才會執行該區塊:
FILE = main.l
NAME = $(basename $(FILE))
run: $(NAME)
./$(NAME)
$(NAME): lex.yy.c
cc -o $(NAME) -O lex.yy.c -ll
lex.yy.c: $(FILE)
lex $(FILE)
這就是 run → $(NAME) → lex.yy.c → $(FILE),也就是只要有 FILE,其實就可以執行 run 了。因此要運行上述腳本,只須 make FILE=test.l 即可!
真實的內建函式
除了把 Target 當作函式,Makefile 也有提供真正的字串與檔案處理函式,語法通常是 $(function_name arguments):
$(wildcard *.c):抓取當前目錄下所有.c結尾的檔案。$(patsubst %.c,%.o,$(SRCS)):字串替換,把$(SRCS)裡所有.c結尾的字串換成.o。$(shell ls -la):執行外部的 Shell 指令並捕捉其輸出結果。
條件判斷
Makefile 也支援條件判斷(ifeq, ifneq, ifdef, ifndef),最常用來判斷不同的作業系統或編譯環境:
OS := $(shell uname)
ifeq ($(OS), Darwin)
CC = clang
CFLAGS += -D MAC_OS
else ifeq ($(OS), Linux)
CC = gcc
CFLAGS += -D LINUX_OS
else
CC = cc
endif
build:
$(CC) $(CFLAGS) main.c -o main