開始我會插播一段我如何學習makefile的廢話,如果不想聽的話,請直接跳到我的makefile教程。
首先得先說明學習makefile真是一個痛苦的過程,尤其是用干巴巴的看書來學習的過程,簡直可以用如坐針氈來形容了……不過作為一個想成為真正程序員的人這又算得了什麼呢?為了不被人诟病編程只會用IDE,你得硬著頭皮來學習這個讓人痛苦的東西,好在有一句話,痛苦是進步的標識,這至少說明了你在進步,也挺好的。
通過這幾天的痛苦學習,我覺得學習makefile得分這麼幾個步驟:
1、熟悉大概的makefile語句,寫幾個簡單的makefile–只需要很簡單的,能編譯出幾個文件的demo程序即可,可以嚴重提高自信心;—done
2、讀這本書《GNU Make項目管理》的1、2、3、6、8章;–done
3、然後著手寫一個較大型系統的makefile;–done
4、goto 2;–todo
5、在平時的項目中提醒自己用makefile來管理,不斷的查找遇到的新知識點,記錄,回顧;–todo
我得說,走完第2步給我的感覺不是我學會了makefile,實則是我依然寫不了太大的makefile,只能說我再看到大型的makefile不再害怕了,我自信我能看得懂這個makefile所要表現的編譯系統。這也算是對makefile的入門吧。
—-—————————–—-———————————-
當我在著手完成上面的第3步的時候,真的還是什麼都不會,最痛苦的是不知道怎麼下手,苦於沒有找到錯綜復雜的困境中的“繩頭”,我當時能想到的一件事就是google,我用了這樣的關鍵字:the best way of wrighting makefile,然後我得到了這個地址。這裡面一個很重要的信息就是避免使用傳統上的嵌套makefile,具體用什麼方式來構建一個大的工程呢?我有找到了這篇論文《Recursive make considered harmful》,其中講述的很清楚,我查了下被引用的次數還是不少的,關於這篇文章,請參考這裡,不再贅述。
當我讀完這篇論文之後,我開始重新思考我該怎樣完成上面的步驟3,然後慢慢的嘗試完成其中一個靜態庫的編譯,最後寫成這個摸樣:
# include some common definitions of make include common.mk MODULES := asyn_frame rsa platform torrent_parser utility CFLAGS += $(patsubst %,-I%,$(MODULES)) CFLAGS += -I. -I./include CFLAGS += -DLINUX -DNDEBUG CFLAGS += -O2 SRC := $(wildcard *.c) # It's used for debug makefile. MODULES_MK := $(patsubst %,src/%/module.mk,$(MODULES)) include $(MODULES_MK) OBJ := $(patsubst %.c,%.o,$(SRC)) LIB := libcommon.a $(LIB) : $(OBJ) @echo 'enter produce the lib files*************' $(AR) $(LIB) $(OBJ) $(RM) $(OBJ) @echo 'leave produce the lib files*************' all : $(LIB) %d : %c @set -e; $(RM) $@; $(CC) -M $(CFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; $(RM) $@.$$$$ -include $(SRC:.c=.d) .PHONY : clean all test : @echo 'make test enter' @echo 'the src of asyn_frame:' @echo $(SRC) @echo 'the obj of asyn_frame:' @echo $(OBJ) @echo 'the mk file of common' @echo $(MODULES) @echo $(MODULES_MK) @echo $(CFLAGS) @echo 'make test leaving' clean : $(RM) $(LIB) $(OBJ) find . -name "*.d" | xargs rm -f
看起來不算復雜,但是從無到有的過程還是比較曲折的,可以看到這裡面有一個偽目標test,這是我用來測試make中的基本變量的,不斷地嘗試終於出來這個還算能用的東西。所以總算是完成了步驟3,當然是在預計的時間內,心中很是舒服,至少可以說我的知識庫中又多了一項武器。
走完第3步之後我沒有再繼續走第4步,因為我覺得目前看來我還沒有到需要非常熟悉makefile的地步,學習它的初衷也是為了能在日常的工作實踐中幫助自己做實際的項目,如果過於沉浸在理論上就與我的初衷相違背了,非我所欲也,更何況目前的項目大都是跨平台的,所以CMake才是我的菜。此刻我選擇“淺嘗辄止”,但是我還是回顧了一遍makefile的基礎知識,我的參考資料是網上看的比較多的陳皓的《跟我一起寫makefile》,總共14章,我覺得這個系列也不是特別好,很多東西都是GNU manual的摘錄,當然也還有作者自己的理解,不過作為參考幫助回顧一些常見的知識點還是很有幫助的。這也就是我為啥沒有找那本大而全的教科書,而是看了這種網上的精簡tutorial。
前面基本上是我學習makefile的簡單歷程,耗費的時間大約在3周左右,畢竟工作了、有小孩了,真沒有太多自己的時間了,不過有了這件事讓我得到一個很好的習慣就是每天早上能在5點半左右起來,看兩個小時的書再去上班,這真是無心插柳的結果,因為晚上實在沒有時間做這些事。。。前面雖然有很多的廢話,但是我覺得學習知識不是最關鍵的,了解怎樣獲取知識才是重要的而且通常是最難的,我試圖用比較簡單的語言來描述這個獲取知識的思考過程,但細細讀來還是覺得啰嗦滿地。。嘿嘿,就這樣吧,反正就是這麼一回事,我也不想做什麼事後諸葛亮。
下面我會轉入正文,寫一個我自己的makefile教程:
目錄:
1、構建多級目錄結構的makefile最佳實踐 — The Best Way
2、一個真實項目的makefile及其解釋
3、makefile最常用的知識點整理
4、參考資料
具體的內容請移步這裡《我的makefile教程》。
自行編寫的在Linux下的編譯文件。其作用如下:
makefile關系到了整個工程的編譯規則。一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些文件需要先編譯,哪些文件需要後編譯,哪些文件需要重新編譯,甚至於進行更復雜的功能操作,因為makefile就像一個Shell腳本一樣,其中也可以執行操作系統的命令。
makefile帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件開發的效率
參考資料:how to Makefile
看<<跟我一起寫makefile>>
表 13-2 GNU make 的主要預定義變量
預定義變量
含義
$*
不包含擴展名的目標文件名稱。
$+
所有的依賴文件,以空格分開,並以出現的先後為序,可能包含重復的依賴文件。
$<
第一個依賴文件的名稱。
$?
所有的依賴文件,以空格分開,這些依賴文件的修改日期比目標的創建日期晚。
$@
目標的完整名稱。
$^
所有的依賴文件,以空格分開,不包含重復的依賴文件。
$%
如果目標是歸檔成員,則該變量表示目標的歸檔成員名稱。例如,如果目標名稱為
mytarget.so(image.o),則 $@ 為 mytarget.so,而 $% 為 image.o。
AR
歸檔維護程序的名稱,默認值為 ar。
ARFLAGS
歸檔維護程序的選項。
AS
匯編程序的名稱,默認值為 as。
ASFLAGS
匯編程序的選項。
CC
C 編譯器的名稱,默認值為 cc。
CFLAGS
C 編譯器的選項。
CPP
C 預編譯器的名稱,默認值為 $(CC) -E。
CPPFLAGS
C 預編譯的選項。
CXX
C++ 編譯器的名稱,默認值為 g++。
CXXFLAGS
C++ 編譯器的選項。
FC
FORTRAN 編譯器的名稱,默認值為 f77。
FFLAGS
FORTRAN 編譯器的選項。