現在我們的主makefile已經是這樣的
# main.mk
#
include build/systems/system.mk
## 定義產品信息。這裏BUILD_SPECS設置爲spec.mk
PRODUCT_SPECS :=build/spec.mk
include build/products/product.mk
## 定義平臺相關的編譯命令。這裏PLATFORM_SPECS爲空,表示我們不做額外定製化。
PLATFORM_SPECS :=
include build/platforms/platform.mk
## 將產品信息中的定義的feature與平臺定義的編譯選項混合
CXXFLAGS+=$(OPTIONS)
OBJS:=hello.o main.o
TARGET:=hello.exe
## 定義obj文件的編譯規則
hello.o : hello.cpp hello.h
$(CXX) $(CXXFLAGS) -c $< -o $@
main.o : main.cpp hello.h
$(CXX) $(CXXFLAGS) -c $< -o $@
## 定義可執行文件hello.exe的編譯規則
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
all: ...
在此之際,回顧一下一開始提到的最原始的makefile的幾個毛病
地方
1. makefile內容依賴於源文件。如果再增加一個world.cpp, 則必須再修改makefile後才能編譯。只好期望源文件不要太多了
2. 生成obj文件時,依賴的源文件和頭文件是手動添加的,不會自動推導。這就導致以後修改源文件時,必須同步維護makefile中頭文件依賴關係。太累了
3. g++ 以及相應的編譯選項都是寫死的,如果想要交叉編譯一個arm平臺上運行的可執行文件,哦,又要修改makefile
4. 沒有編譯產品的信息,好歹給個立牌坊的機會
想現在毛病3,4已經基本漂亮的解決了(“基本”的含義是?,請看後文),地也圈了,樓也蓋了不少(products目錄,platforms目錄,systems目錄),本來坐等發財,結果發現就因爲毛病2,集團內容溝通不利。隨便改改源文件,可以就要同步調整makefile了。要解決這個頭文件依賴關係的自動推導問題,呵呵,GNU中直接有尚方寶劍MMD,MF, MT。
SRC_FILES:= hello.cpp main.cpp
OBJS:=hello.o main.o
TARGET:=hello.exe
## -MMD 可以用於自動生成頭文件依賴關係
%.o : %.cpp
$(CXX) $(CXXFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$@" -MT"$(@:%.o=%.d)" -c "$<" -o "$@"
DEPS := hello.d main.d
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEPS)
endif
## 定義可執行文件hello.exe的編譯規則
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
關於hello.d, main.d,他們的內容就是具體的依賴關係,然後通過include將依賴關係導入,如此就大功告成。
## hello.d
hello.o hello.d: hello.cpp hello.h
hello.h:
詳細的,hello.d具體怎麼生成的,或者其他更多自動生成頭文件依賴關係的,就“不要問我從哪裏來,我的答案在度娘”
現在依賴關係是自動生成了,不過obj文件列表,還有那些.d文件列表,都是手動填寫的,可以自動化嗎?
答案是肯定的,要不然通用makefile就煉不成了。(就像是看電視劇或者動漫的,都知道主角光環,無論前面如何複雜艱險,主角總是能化險爲夷的)
makefile中有一種類似於shell腳本中的變量替換一樣,我們可以直接從源文件列表導出obj文件列表,以及.d文件列表。不多說,直接上代碼
SRC_FILES:= hello.cpp main.cpp
OBJS:=$(SRC_FILES:.cpp=.o)
DEPS:=$(OBJS:.o=.d)
TARGET:=hello.exe
## -MMD 可以用於自動生成頭文件依賴關係
%.o : %.cpp
$(CXX) $(CXXFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$@" -MT"$(@:%.o=%.d)" -c "$<" -o "$@"
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEPS)
endif
## 定義可執行文件hello.exe的編譯規則
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
如果你願意,源文件列表也是可以自動推導的:給定源文件路徑,通過$(foreach dir, $(LOCAL_SRC_PATHS), $(wildcard $(dir)/*.cpp))命令,可以自動將指定目錄下的.cpp文件全部找出來。
今天的工作做得很漂亮,以後只要配置好makefile,以及列出源文件列表,就可以直接編譯了,其他什麼也不用改。甚至對於源文件列表,也可以通過指定目錄以及命令wildcard自動推導出來。
走到這裏,感覺人生已經無慾無求了,通用makefile真的已經通用了。However,我又是看得太遠,看這段makefile,我想到兩個問題
1. 上面的生成obj文件和.exe文件的代碼是很形式化的,如果可以應該同product一樣,單獨歸類到某個文件夾中,防止在修改makefile時不小心改了這些代碼
2. 如果我是多模塊編譯,怎麼辦呢?
路漫漫其修遠兮,吾將上下而求索...