1. 程式人生 > >makefile筆記

makefile筆記

entos 編譯 pat back ID 沒有 c-c 導致 ron

最近在看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.h
  
6 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筆記