在 範例中, 當我們開始使用標頭檔時, 有個棘手問題會開始困惱我們. 雖然在範例中我們可以在 makefile 檔中手動加入目的檔與 C 標頭檔的依存關係, 但在大數程式中, 這幾乎是不可能的事, 因為大多數的標頭檔還會包含其他標頭檔所形成的複雜樹狀結構.
考慮電腦擅長搜尋以及樣式比對, 讓我們使用一個程式找出這些檔案之間的關係, 我們甚至可以使用此程式以 makefile 的語法撰寫出這些依存關係. 在 gcc 中這是一個選項, 許多的其他C/C++ 編譯器也都會讀進原始檔並寫出 makefile 的依存關係, 例如下面是一個簡單範例尋找 stdio.h 的依存關係 :
傳統上會有兩種方法可用來自動產生依存關係納入 makefile . 第一種也是最古老的方法就是在 makefile 結尾加上下面這一列 :
然後撰寫一支命令稿以便加入這些自動產生的命令稿. 這麼做當然比手動加入的好, 但還不夠好. 第二種方法就是為 make 加入一個 include 指令. 而今大多數的 make 版本都支援 include 指令, 當然 GNU make 一定可以這麼做. 因此訣竅就是撰寫一個 makefile 工作目標, 此工作目標的動作就是以 -M 選項對所有原始檔案執行 gcc, 並將結果存入一個依存檔 (dependency file), 然後重新執行 make 以便把剛才所產生的依存檔引入 makefile , 這樣就可以觸發我們所需要的更新動作. 在 GNU make 中, 你可以使用以下規則來達成此目的 :
- depend: count_words.c lexer.c counter.c
- $(CC) -M $(CPPFLAGS) $^ > $@
- include depend
執行 make 以建造程式之前, 你首先應該執行 make depend 以產生依存關係. 這麼做雖然不錯, 但是當人們對原始檔案加入或移除依存關係時, 通常不會重新產生 depend 檔. 這會造成無法重新編譯原始檔案, 整個工作又會變成一團糟. 在 GNU make 中你可以透過一個很酷的功能以及一個簡單的演算法來解決此問題. 首先介紹這個簡單的演算法. 如果我們為每個原始檔建立依存關係, 以及將之擺入相應的依存檔 (一個副檔名為 .d 的檔案) 並以該 .d 檔為工作目標, 加入此依存規則 (dependency rule) , 這樣當原始檔遭到變更, make 就會知道需要更新該 .d 檔 (以及目的檔) :
你可以使用如下的樣式規則以及命令稿來產生這項規則 :
- %.d: %.c
- $(CC) -M $(CPPFLAGS) $< > $@.$$$$;\
- sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' <$@.$$$$ > $@;\
- rm -f $@.$$$$
現在介紹這個很酷的功能. make 將會把 include 指令所指名的檔案視為一個需要更新的工作目標. 所以如果我們表明我們要引入 .d 檔, 則 make 將會在讀進 makefile 檔時自動建立這些 .d 檔. 我們的 makefile 加入了自動產生依存關係的功能之後會變成 :
* makefile_2-7 檔案內容 :
- VPATH = src include
- vpath %.l %.c src
- vpath %.h include
- CPPFLAGS = -I include
- SOURCES = count_words.c \
- lexer.c \
- counter.c
- count_words: count_words.o counter.o lexer.o -lfl
- count_words.o: count_words.c counter.h
- counter.o: counter.c counter.h lexer.h
- lexer.o: lexer.c lexer.h
- lexer.c: lexer.l
- include $(subst .c,.d,$(SOURCES))
- %.d: %.c
- $(CC) -M $(CPPFLAGS) $< > $@.$$$$;\
- sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' <$@.$$$$ > $@;\
- rm -f $@.$$$$
include 指令總是應該放在手動撰寫之依存關係的後面, 這樣預定目標才不會被某個依存檔搶走了. include 指令可用來指定一串檔案 (檔名之中可以包含通配符). 我們在此處使用了一個 make 函式 subst 來將一串原始檔的檔名換成一串依存檔的檔名. 現在你只要知道 subst 可用來將 $(SOURCES) 裡的文字從 .c 字串換成 .d 字串.
如果針對此 makefile 以 --just-print 選項來執行 make , 則會的到如下的結果 :
make 一開始的錯誤訊息不用擔心, 這只是一個警告訊息. 起先 make 會搜尋引入擋, 但是找不到他們, 所以 make 會在搜尋 "用來建立這些檔案的規則" 之前發出 No such file or directory 的警告. 若不想看到這個警告, 只要為 include 指令前置一個連字號 (-) 即可. 警告之後可以看到 make 以 -M 選項調用 cc 以及執行 sed 命令的動作. 請注意 make 必須調用 flex 以建立 lexer.c , 然後在開始滿足預定目標前刪除 lexer.c 這個臨時檔.
補充說明 :
* [Linux命令] sed : 檔案內容修改
沒有留言:
張貼留言