位元詩人 [GNU Make] Makefile 教學:如何在 Makefile 中設置變數

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

在前文的例子中,我們將所有的指令都寫死在 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 即可。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。