在前文的例子中,我們將所有的指令都寫死在 Makefile 中,這樣的做法雖然直觀,但不一定是最方便的做法。例如,筆者所用的某個雲端環境有 GCC-4.8、GCC-4.9、Clang 三套 C 編譯器,如果我們想在編譯時更換編譯器,按照先前的思維,勢必要頻繁修改 Makefile 或是撰寫三套 Makefile,讀者應該可以察覺到這不是很好的解決方法。
比較好的方法是將共通的部分以變數 (variable) 將 Makefile 參數化 (parameterization),之後只要修改變數即可。我們將先前的例子參數化後改寫如下:
CC=gcc
CFLAGS_DEBUG=-Wall -Wextra -g -std=c99
CFLAGS_RELEASE=-Wall -Wextra -O2 -std=c99
OBJS=test_deque_int.o deque_int.o
TEST_PROG=test_deque_int.out
all: dynamic
dynamic:
$(CC) $(CFLAGS_RELEASE) -fPIC -c deque_int.c
$(CC) $(CFLAGS_RELEASE) -shared -o libalgodeque.so deque_int.o
static:
$(CC) $(CFLAGS_RELEASE) -c deque_int.c
ar rcs -o libalgodeque.a deque_int.o
test: $(TEST_PROG)
./$(TEST_PROG)
echo $$?
$(TEST_PROG): $(OBJS)
$(CC) $(CFLAGS_DEBUG) -o $(TEST_PROG) $(OBJS)
test_deque_int.o:
$(CC) $(CFLAGS_DEBUG) -c test_deque_int.c
deque_int.o:
$(CC) $(CFLAGS_DEBUG) -c deque_int.c
clean:
$(RM) $(TEST_PROG) *.o *.so *.a
在 Makefile 中,會將變數設為全大寫,可和一般的系統指令區別;引用變數的格式是 $(...)
,將變數名稱塞入其中即可。
在 dynamic 任務中,我們使用了兩個參數,一個是 CC,一個是 CFLAGS_RELEASE,前者表示 C 編譯器 (C compiler),後者表示編譯器所用的參數:
CC=gcc
CFLAGS_RELEASE=-Wall -Wextra -O2 -std=c99
dynamic:
$(CC) $(CFLAGS_RELEASE) -fPIC -c deque_int.c
$(CC) $(CFLAGS_RELEASE) -shared -o libalgodeque.so deque_int.o
若我們想用 Clang 編譯專案,只要將 CC 的值設為 clang
即可。由於 Clang 在參數上刻意和 GCC 保持相容,有時候 GCC 的參數可以原封不動地套用在 Clang 上,在本例中剛好也可行。
接著,我們來看 test 任務的部分:
CC=gcc
CFLAGS_DEBUG=-Wall -Wextra -g -std=c99
OBJS=test_deque_int.o deque_int.o
TEST_PROG=test_deque_int.out
test: $(TEST_PROG)
./$(TEST_PROG)
echo $$?
$(TEST_PROG): $(OBJS)
$(CC) $(CFLAGS_DEBUG) -o $(TEST_PROG) $(OBJS)
test_deque_int.o:
$(CC) $(CFLAGS_DEBUG) -c test_deque_int.c
deque_int.o:
$(CC) $(CFLAGS_DEBUG) -c deque_int.c
由這段例子可看出,任務名稱和相依性也可以設置為變數。
最後來看 clean 任務的部分:
clean:
$(RM) $(TEST_PROG) *.o *.so *.a
細心的讀者可發現在本例的 Makefile 中,我們沒有設置 RM 變數,但專案卻可正常運作。這是因為 make
中有所謂的內隱變數 (implicit variable),也就是有預設值的變數。在 GNU Make 中,RM 的預設值是 rm -f
,剛好是強制刪除檔案的指令。
如果有使用 Windows 的 cmd 環境的讀者應該知道 cmd 環境中沒有 rm
指令,相對應的指令是 del
。這是由於 GNU Make 是 Unix 文化的產物,自然會以類 Unix 系統的常見情境做為預設值。這不代表在 Windows 上無法使用 make
管理專案,只是要略為修改 Makefile 的寫法。
即使我們僅僅用了一小部分 Makefile 的語法,這個版本的 Makefile 的通用性比先前的版本更好一些了。學 Makefile 就像學程式設計般,並不是學完全部的特性才開始撰寫 Makefile,而是在一邊學習的過程中就可以開始寫簡單的 Makefile,隨著我們技巧增加,再逐步修改 Makefile 即可。