通用makefile是如何煉成的(IV)

現在我們的主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. 如果我是多模塊編譯,怎麼辦呢?

路漫漫其修遠兮,吾將上下而求索...


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章