makefile筆記
最近在看makefile相關內容,寫這篇文章記錄總結最近的學習內容。
首先創建測試目錄makefileTest以及相關文件:
makefile中比較重要的內容就是依賴目標、生成規則,指明了要生成的目標以及如何生成,指定好以後makefile便會根據指定的規則去做。
編寫第一個版本的makefile,寫出每個生成目標及其依賴規則。由於剛安裝centos7,所以顯示g++用不了,搜索進行安裝,發現剛安裝好的系統處於安全性考慮是沒有聯網的,導致獲取安裝包失敗,那首先聯網吧。網上搜索,發現需要修改系統的配置文件,進入以下目錄:
cd /etc/sysconfig/network-scripts/
找到配置文件名,此處是ifcfg-ens33,打開此文件將ONBOOT=no修改為yes
使用命令 yum install gcc-c++ 下載G++安裝包,這一步操作需要使用su - 命令切換到root用戶獲取權限進行操作。
寫出了第一個版本的makefile:
1 testobj:main.o student.o dog.o family.o 2 g++ -o testobj main.o student.o dog.o family.o 3 main.o:main.cpp student.h dog.h family.h 4 g++ -c main.cpp 5 student.o:student.cpp student.h6 g++ -c student.cpp 7 dog.o:dog.cpp dog.h 8 g++ -c dog.cpp 9 family.o:family.cpp family.h 10 g++ -c family.cpp 11 12 clean: 13 rm -rf testobj *.o
生成的最終可執行目標文件是testobj,依賴於所有的.o文件,在寫最終目標生成規則的時候,需要指定最終可執行文件名以及依賴的文件名,在生成.o文件的時候,只需要指出依賴的文件名即可,makefile會自動生成與源文件同名的.o文件。
在終端執行make,輸出如下:
可以看到make將執行的命令回顯在終端,在當前目錄下生成了源文件同名的.o文件以及終極目標,執行此目標文件可以看到測試輸出:
雖然這個簡單版本的makefile能夠正確的編譯,但是有許多地方出現了同名的文件列表重復,例如第一個依賴規則中的.o文件,如果添加一個源文件,就需要修改很多地方的文件列表,很麻煩。在makefile中可以使用變量,因此可以將重復的部份用變量表示,在使用的地方引用這個變量即可。修改如下:
1 OBJECTS:=main.o student.o dog.o family.o 2 3 testobj:$(OBJECTS) 4 g++ -o testobj $(OBJECTS) 5 main.o:main.cpp student.h dog.h family.h 6 g++ -c main.cpp 7 student.o:student.cpp student.h 8 g++ -c student.cpp 9 dog.o:dog.cpp dog.h 10 g++ -c dog.cpp 11 family.o:family.cpp family.h 12 g++ -c family.cpp 13 14 clean: 15 rm -rf testobj *.o
可以看到將所有的.o文件用變量OBJECTS來表示,使用的時候用$(OBJECTS)來引用即可。
接下來考慮makefile中每添加一個源文件就需要寫出其生成規則,也是很麻煩的,那麽可以考慮使用make的隱含生成規則,只寫出目標.o文件依賴的.h文件,讓make自己去推導生成規則找對應的源文件,修改如下:
OBJECTS:=main.o student.o dog.o family.o 2 3 testobj:$(OBJECTS) 4 g++ -o testobj $(OBJECTS) 5 main.o:student.h dog.h family.h 6 student.o:student.h 7 dog.o:dog.h 8 family.o:family.h 9 10 clean: 11 rm -rf testobj *.o
至此,看著makefile變得簡單多了,那麽每添加一個源文件,只需要在OBJECTS變量中添加對應的.o文件,寫出對應的.o文件的依賴規則即可。
到這裏,還是覺得一個文件的添加就需要改東西很麻煩,這些依賴規則能不能交給強大的make來自己實現呢?當然可以,於是接下來就要使用makefile的自動生成依賴規則了,到這裏好喜歡makefile了。在開始之前需要學習幾個系統函數,因為接下來會用到這些函數。
(1)wildcard
用法:$(wildcard PATTREN...) 輸出與給定模式匹配的所有文件列表
那麽我們可以用此函數獲取當前目錄下的所有源文件:$(wildcard *.cpp)
此函數會列出當前目錄下所有的.cpp源文件。
(2) patsubst
用法:$(patsubst PATTERN,REPLACEMENT,TEXT) 模式替換函數
用此函數我們可以根據.cpp文件得到.o文件:$(patsubts %.cpp,%.o,$(wildcard *.cpp))
Tips:在這裏*.cpp使用了通配符,表示後綴為.cpp的所有文件
那麽,用上系統函數的makefile修改如下:
1 SRCFILE:=$(wildcard *.cpp) 2 OBJECTS:=$(patsubst %.cpp,%.o,$(SRCFILE)) 3 4 testobj:$(OBJECTS) 5 g++ -o testobj $(OBJECTS) 6 main.o:student.h dog.h family.h 7 student.o:student.h 8 dog.o:dog.h 9 family.o:family.h 10 11 clean: 12 rm -rf testobj *.o
接下來考慮自動生成依賴規則。首先可以使用g++編譯器的-M選項來列出文件的依賴關系,使用-M選項會輸入所有的文件依賴關系,包括標準庫文件,因此這裏使用-MM選項只輸出我們自己需要的依賴關系。
要讓makefile自動生成依賴規則,需要使用上述的命令,在makefile中為每個源文件.cpp,為其生成一個同名.d文件,用來描述此源文件的依賴關系,例如對於main.cpp,為其生成main.d文件,描述了main.o文件的依賴關系。還需要學習一些自動化變量:
(1)$< 規則的第一個依賴文件名
(2)$@ 規則的目標文件名
(3)$^ 規則的所有依賴文件
修改為自動生成依賴關系後的makefile如下:
1 SRCFILE:=$(wildcard *.cpp) 2 OBJECTS:=$(patsubst %.cpp,%.o,$(SRCFILE)) 3 G++:=g++ 4 5 testobj:$(OBJECTS) 6 g++ -o testobj $(OBJECTS) 7 8 %.d:%.cpp 9 $(G++) -MM $< > $@.$$$$; 10 sed ‘s,\($*\)\.o[ :]*,\1.o $@ :,g‘ < $@.$$$$ > $@; 11 rm -rf $@.$$$$ 12 13 sinclude $(SRCFILE:.cpp=.d) 14 15 clean: 16 rm -rf testobj *.o
首先使用g++ -MM 將每一個.cpp文件的依賴關系輸出重定向到一個文件中,這個文件為依賴目標文件名.進程號,特殊變量$$$$表示當前進程號,舉個具體的例子展開就是:g++ -MM main.cpp > main.d.123456。接下來使用sed做字符替換,將第一行命令生成的文件進行處理,加入.d文件,比如將main.o:main.cpp student.h dog.h family.h處理為 main.o mina.d:main.cpp student.h dog.h family.h,接下來最後一步刪除產生的臨時文件。在使用時,需要將這些.d文件包含進當前的makefile中,sinclude $(SRCFILE:.cpp=.d)命令,表示將當前的.cpp源文件替換問.d文件包含進來,這樣就可以使用生成的.d依賴文件了。
執行make後的結果如下:
可以看到對於每個.o文件有同名.d文件生成,打開任意一個.d文件查看其內容:
可以看到main.d中是main.o的依賴關系。
最後介紹偽目標,偽目標只是一個標簽,沒有依賴任何文件,可以將clean寫成偽目標,聲明一個偽目標的方法是將其作為特殊目標.PHONY的依賴,當輸入make clean時,一些定義的指令會被執行,現修改makefile如下:
1 SRCFILE:=$(wildcard *.cpp) 2 OBJECTS:=$(patsubst %.cpp,%.o,$(SRCFILE)) 3 G++:=g++ 4 5 testobj:$(OBJECTS) 6 g++ -o testobj $(OBJECTS) 7 8 %.d:%.cpp 9 $(G++) -MM $< > $@.$$$$; 10 sed ‘s,\($*\)\.o[ :]*,\1.o $@ :,g‘ < $@.$$$$ > $@; 11 rm -rf $@.$$$$ 12 13 sinclude $(SRCFILE:.cpp=.d) 14 15 .PHONY:clean 16 clean: 17 rm -rf testobj *.o
可以看到定義了clean偽目標,當輸入make clean時,clean定義的指令會被執行,刪除終極目標以及所有的.o文件。有時候在執行偽目標的時候不想關註是否執行成功,也不關註出錯的提示消息,這時候就可以在命令前面加-來告訴make你只管執行,結果我不關註,比如:
15 .PHONY:clean 16 clean: 17 -rm -rf testobj *.o
以上就是近期對於makefile的學習筆記,沒有涉及到不同目錄下文件的編譯以及高級用法,算是初級學習吧。
參考資料:
(1)GUN make中文手冊:https://pan.baidu.com/s/1BwlD910FXWA9mOrL9uAlSA
(2)makefile教程(中文版):https://pan.baidu.com/s/1EITITRS6P0Dk41YwlMD5_g
makefile筆記