Makefile 自動生成頭文件的依賴關係 .

最近在看一本書《Windows遊戲編程大師技巧》 (Tricks of Windows Game Programming Gurus). 第一章給出了一個打磚塊小遊戲的示例程序. 包括三個文件: blackbox.h, blackbox.cpp和freakout.cpp (600行代碼, 對於Windows C++程序來講還好, 沒有讓我freak out…). blackbox.cpp封裝了部分DirectDraw, 提供了一些更傻瓜化的初始化DirectDraw, 畫點, 畫方框的工具函數. blackbox.h包括了這些函數的聲明. freakout.cpp引用了blackbox.h文件, 包括WinMain主函數和主要的遊戲邏輯.html

How to build?程序員

和我同樣還在使用N年前的垃圾電腦的看官們, 多半也不肯意用Visual Studio來挑戰本身的耐心. 可是因此讓咱們選擇小米加步槍: GCC (MinGW) + Makefile. 寫個簡單的makefile例如:編程

all:    freakout.exe
 
freakout.exe: freakout.o blackbox.o
    g++ freakout.o blackbox.o -o stupid.exe -L D:gamedevdx81sdkDXFDXSDKlib -lddraw -mwindows
 
freakout.o: freakout.cpp blackbox.h
    g++ -c freakout.cpp -o freakout.o -I D:gamedevdx81sdkDXFDXSDKinclude
 
blackbox.o: blackbox.cpp blackbox.h
    g++ -c blackbox.cpp -o blackbox.o -I D:gamedevdx81sdkDXFDXSDKinclude

Problem?小程序

上面的代碼固然有不少問題, 好比」-L <DX lib path>」, 「-I <DX inc path>」能夠被定義在相似$(LIB), $(INC)的變量裏面, 好比我沒有按照makefile的習慣寫一個clean目標清理生成的東東…不過個人重點是在紅色高亮的blackbox.h: 兩個.o文件都須要依賴於blackbox.h.windows

假設咱們修改freakout.cpp引用更多的頭文件, 每加一條#include 「somefile.h」指令, 就須要相應地在makefile裏爲freakout.o增長一個依賴項somefile.h.函數

假設咱們修改stupid.h文件, 讓stupid.h也引用一個頭文件someotherfile.h, 那麼咱們也須要相應地在makefile裏爲全部依賴stupid.h的.o文件 (在這個例子中是freakout.o和blackbox.o) 增長依賴項someotherfile.h.工具

因此問題是: 在.cpp和.h被修改的狀況下, 怎麼維護.o目標文件和.h頭文件的依賴關係.ui

Solution – 自動生成依賴關係spa

Google一下」makefile 頭文件 依賴」會發現大多數編譯器都提供了一個選項生成.o目標文件所依賴的文件列表. 好比GCC的」-MM」選項. 運行GCC –MM freakout.cpp blackbox.cpp <庫文件和頭文件選項>獲得輸出:htm

freakout.o: freakout.cpp blackbox.h D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h
blackbox.o: blackbox.cpp blackbox.h D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h

因此一個簡單的處理依賴關係的辦法是把這些編譯器生成的依賴關係寫入一個文件裏, 而後在makefile中用include指令包含這個文件:

CPP = g++
OBJ = freakout.o blackbox.o
LIB = -L D:gamedevdx81sdkDXFDXSDKlib -mwindows -l ddraw
INC = -I D:gamedevdx81sdkDXFDXSDKinclude
 
all: freakout.exe
 
freakout.exe: ${OBJ}
    ${CPP} ${OBJ} -o freakout.exe ${LIB}
 
include depend
 
# note the symbols: $< and '$@
%.o: %.cpp
    ${CPP} -c $< -o $@ ${INC}
 
# generate depend file
depend:
    ${CPP} -MM ${OBJ:.o=.cpp} ${INC} > depend

注意紅色的部分, depend是GCC生成的包含了依賴關係的文件 (也注意上面的makefile中有」$<」, 「$@」這種perl風格的bt匹配字符…) . 有了這樣的makefile, 咱們就能用兩個步驟來build項目:

1) 在須要更新依賴關係的時候 (好比在某個文件裏多加了一條#include指令) 運行make depend.

2) 運行make.

Makefile Auto Dependency – 只運行一次make

懶惰是程序員的一大美德, 因此GNU make的手冊裏提供了一個運行一次make就能更新全部依賴關係而且按照依賴關係build的辦法 (http://www.gnu.org/software/make/manual/make.html#Automatic-Prerequisites): 與總體引入一個depend目標不一樣, 咱們爲每個.o/.cpp文件引入一個.d依賴關係文件. 依賴關係文件由GCC的」-MM」選項生成, 而且在GCC輸出的基礎上把本身自己加入到目標列表中. 好比:

# freakout.d
freakout.o freakout.d: freakout.cpp D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h blackbox.h
 
# blackbox.d
blackbox.o blackbox.d: blackbox.cpp D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h blackbox.h

注意紅色的部份: .d文件被加入到了目標列表, 依賴相關的.h和.cpp文件. 那麼怎樣自動生成這些.d文件呢? 相似的, makefile:

 

CPP = g++
OBJ = freakout.o blackbox.o
LIB = -L D:gamedevdx81sdkDXFDXSDKlib -mwindows -l ddraw
INC = -I D:gamedevdx81sdkDXFDXSDKinclude
all: freakout.exe
freakout.exe: ${OBJ}
    ${CPP} ${OBJ} -o freakout.exe ${LIB}
include ${OBJ:.o=.d} 
%.o: %.cpp
    ${CPP} -c $< -o $@ ${INC} 
%.d: %.cpp
    rm -f $@ & 
    ${CPP} -MM $< ${INC} > $@.$$ & 
    insertdfile.exe $< $@.$$ > $@ & 
    rm -f $@.$$

這個makefile和上一節給出的makefile大體差很少, 不一樣的是兩個紅色的部分: 第一部分咱們用include指令include相關的全部.d文件; 第二部分定義了生成.d文件的規則 (又是$@, $<符號…): 第一行首先刪除原有的.d文件; 第二行運行g++ -MM生成依賴關係寫入一個臨時文件($@.$$)裏; 第三行把.d文件加入到第二行生成的依賴關係中並把最終結果寫到.d文件中; 第四行刪除臨時文件.

P.S. insertdfile.exe是我本身寫的一個小程序, 用來把.d文件加入到第二行生成的依賴關係中, 例如把」blackbox.o: blackbox.cpp blackbox.h」轉換爲」blackbox.o blackbox.d: blackbox.cpp blackbox.h」. 我爲何要本身寫一個字符串替換的程序? 由於我沒有裝sed工具…若是有, 固然用官方推薦的辦法, 把第三行換成神奇的符咒: sed ’s,($*).o[ :]*,1.o $@ : ,g’ < $@.$$ > $@ .

好了, 運行make:

1) make嘗試去包含blackbox.d和freakout.d文件, 沒有發現, 因此檢查有沒有規則能夠生成這些.d文件, 發現咱們%.d: %.cpp的這條規則, 因而運行這條規則生成全部的.d文件而且包含進來.
2) 按照正常規則生成.o文件, 而後生成.exe.

而後咱們作一些修改, 好比增長一個stupid.h文件, 而後修改blackbox.h文件以包含stupid.h. 運行make:

1) make把第一個目標all加入到須要生成的目標列表中.
2) make包含blackbox.d和freakout.d文件. 注意在這個過程當中這兩個.d文件也會被加入到make的須要生成的目標列表中 – 由於可能有規則能更新這兩個.d文件.
3) 根據blackbox.d和freakout.d包含的規則: .d文件 (已經被入到了make的須要生成目標列表中) 須要被更新, 因而相應地運行%.d: %.cpp規則更新.d文件: 新加入的 stupid.h文件被加入到兩個.d文件的依賴項中.
4) make發現包含的兩個.d文件在第2)步中都被更新, 因而從新包含這兩個.d文件.5) make根據在第2)步生成在第3)步被包含進來的規則, 發現stupid.h的日期比兩個.o文件的日期都要新, 因而從新生成.o文件.6) 根據規則生成.exe.

相關文章
相關標籤/搜索