[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 即可。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk