GNU Make 相容的 Makefile 教學:無經驗的 Makefile

我們這裡展示一個初階的 (naive) 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 語法,仍然可以寫出可用的 Makefile。

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

comments powered by Disqus