[GNU Make] Makefile 教學:即使是新手也可以馬上開始寫 Makefile

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    我們這裡展示一個初階的 (naive) Makefile,本例摘自筆者先前練資料結構的微型程式。在這裡,我們刻意地少用 Makefile 特有的語法,只用先前提到的基本概念來寫 Makefile,讓整個 Makefile 看起來更單純:

    all: dynamic
    
    dynamic:
    	gcc -fPIC -c deque_int.c
    	gcc -shared -o libalgodequei.so deque_int.o
    
    static: deque_int.o
    	ar rcs -o libalgodequei.a deque_int.o
    
    test: test_deque_int.out
    	./test_deque_int.out
    	echo $$?
    
    test_deque_int.out: deque_int.o test_deque_int.o
    	gcc -o test_deque_int.out test_deque_int.o deque_int.o
    
    test_deque_int.o:
    	gcc -c test_deque_int.c
    
    deque_int.o:
    	gcc -c deque_int.c
    
    clean:
    	rm -f test_deque_int.out *.o *.so *.a
    

    本專案的使用方式如下:

    • makemake dynamic:製作動態函式庫
    • make static:製作靜態函式庫
    • make test:執行測試程式
    • make clean:清除由編譯器生成的檔案

    預設的任務是 *all*,而 all 相依於 *dynamic*,故我們不加入任何參數時,預設動作為製作動態函式庫。節錄程式碼如下:

    all: dynamic
    
    dynamic:
    	gcc -fPIC -c -o deque_int.o deque_int.c
    	gcc -shared -o libalgodequei.so deque_int.o
    

    製作靜態函式庫的任務為 *static*,而 static 相依於 *deque_int.o*。節錄程式碼如下:

    static: deque_int.o
    	ar rcs -o libalgodequei.a deque_int.o
    
    deque_int.o:
    	gcc -c deque_int.c
    

    使用檔案做為任務名稱的好處在於,當我們第二次執行相關的任務時,make 會略過該任務。這樣的設計是由於早期的電腦運行速度較慢,編譯中大型程式往往需花數十分鐘至數小時,若能略去不必要的任務,就可以省下一些編譯程式的時間。

    執行測試程式的任務為 *test*,該任務有兩層的相依性。節錄程式碼如下:

    test: test_deque_int.out
    	./test_deque_int.out
    	echo $$?
    
    test_deque_int.out: deque_int.o test_deque_int.o
    	gcc -o test_deque_int.out test_deque_int.o deque_int.o
    
    test_deque_int.o:
    	gcc -c test_deque_int.c
    
    deque_int.o:
    	gcc -c deque_int.c
    

    看起來比先前的任務略長,但其實只是以 GCC 依序編譯 C 程式碼,如果讀者從任務相依性的源頭追蹤任務流程,就知道這段程式碼所代表的意義。(可由下向上閱讀此段程式碼。)

    此處唯一比較有點小技巧的地方在於 echo $$?。在類 Unix 系統中,$? 是一個內建環境變數,表示最後一個程式執行後所回傳的錯誤碼,若為 0 表示程式沒有錯誤,若為其他數字表示程式有某種錯誤。echo $? 表示印出上一個程式的錯誤碼;由於在 Makefile 中,$ (錢字號) 有特殊意義,所以要用 $$ 表示該符號為一個 $ 字串而非 Makefile 的特殊符號。

    清理檔案的任務為 *clean*,讀者應該很容易就知道其意義:

    clean:
    	rm -f test_deque_int.out *.o *.so *.a
    

    一般在網路上看到的 Makefile 都充滿著各種難以理解的符號和規則,越強的高手寫的 Makefile 越難理解;甚至有些 Makefile 是用 Autotools 或 CMake 自動生成的,而自動生成的 Makefile 往往很冗長而難以閱讀。但即使我們不用特殊的 Makefile 語法,仍然可以寫出可用的 Makefile。

    現在的軟體專案幾乎都有上版本控制系統,寫 Makefile 時並不需要追求一步到位。即使一開始寫的 Makefile 沒那麼漂亮,只要能夠正確運作,之後再將 Makefile 的語法重構 (refactoring) 得漂亮一些即可。Makefile 畢竟是軟體專案的設定檔而非文藝作品,最重要的是能夠正確地編譯專案,而不用過度追求語法上的美感或技巧。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk
    標籤: GNU MAKE, MAKE, MAKEFILE