歡迎您光臨本站 註冊首頁

linux編程初步之跟我學之Makefile的舒爽瘦身

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply:0

make 是一個神奇的東西阿,裡邊有很多高級的用法,如果不會用的話,寫出來的Makefile會很麻煩。 下面結合一個實際的例子來看看 make 的 (1)implicit pattern rule 的重新定義 (2) 自動依賴關係的生成 (3) make 的嵌套調用。

1. 假設一個工程 project 分為兩個子工程 gtrans 和 isgen

~> ls project
GTRANS ISGEN Makefile gtrans isgen // gtrans 與 isgen 都是生成的程序

project > ls GTRANS/
Makefile findGraph.d findGraph.o gtrans.d isgen.cpp preProcess.d preProcess.o
findGraph.cpp findGraph.hpp gtrans.cpp gtrans.o preProcess.cpp preProcess.hpp


.d 文件存儲的是 .o 文件的依賴關係, .hpp 是頭文件



2. 先看一下GRRANS / Makefile

SOURCES = gtrans.cpp findGraph.cpp preProcess.cpp

LIB = "/home/dab/cadlib"

INCLUDE = "/home/dab/cadinc"

VPATH = ..

%.o: %.cpp
$(CXX) -g3 -c $< -Wall -I $(INCLUDE) -o $@

%.d: %.cpp
$(CXX) -MM -w $< > $@; sed 's/\($*\)\.o[ :]*/\1.o $@: /g' -i $@

gtrans: $(SOURCES:.cpp=.o)
$(CXX) *.o -o ../gtrans -Wl,-R,$(LIB) -L$(LIB) -lagraph -lcdt

include $(SOURCES:.cpp=.d)

.PHONY: clean
clean:
rm ../gtrans *.o


如果不使用上面的三個功能, Makefile 將會很胖。 比如如果不重新定義 implicit pattern rule 的話, 我們要為每一個 *.o: *.cpp 的規則添加一條編譯命令, 源文件越多, 就會越麻煩。 如果重新定義一條隱晦模式規則 (Implicit pattern rule) , 就可以一次工作,受用無窮。

%.o: %.cpp
$(CXX) -g3 -c $< -Wall -I $(INCLUDE) -o $@

這一條很簡單, 變數 CXX 的默認值是 g++, $< 和 $@ 都是自動化變數, $@ is the file name of the target of the rule; $< is the name of the first dependency.



3. 在Makefile中,我們的依賴關係可能會需要包含一系列的頭文件。 如果是一個比較大型的工程,你必需清楚哪些 C 文件包含了哪些頭文件,並且,你在加入或刪除頭文件時,也需要小心地修改 Makefile,這是一個很沒有維護性的工作。為了避免這種繁重而又容易出錯的事情,我們可以使用 C / C++ 編譯的一個功能。大多數的 C / C++ 編譯器都支持一個「-M」的選項,即自動找尋源文件中包含的頭文件,並生成一個依賴關係。 比如

GTRANS > cc -MM gtrans.cpp
gtrans.cpp:4:20: warning: agraph.h: No such file or directory
gtrans.o: gtrans.cpp findGraph.hpp preProcess.hpp

GTRANS > g++ -MM gtrans.cpp
gtrans.cpp:4:20: warning: agraph.h: No such file or directory
gtrans.o: gtrans.cpp findGraph.hpp preProcess.hpp


由於 gtrans.cpp 使用到了一個庫文件 agraph, 所以上面會提示一個警告, 另外如果用到了非標準庫的話, 要使用兩個 MM 選項。 我們可以使用一個極端選項 -w (另一個是 -Wall) 來關閉所有警告。

GTRANS > g++ -MM -w gtrans.cpp
gtrans.o: gtrans.cpp findGraph.hpp preProcess.hpp

關於該條隱晦模式規則重定義稍微複雜一些,因為它使用到了 sed, 而用到 sed 又難免會用到 Unix 正則表達式

%.d: %.cpp
$(CXX) -MM -w $< > $@; sed 's/\($*\)\.o[ :]*/\1.o $@: /g' -i $@

$(CXX) -MM -w $< > $@; 將結果重定向於 .d 文件中, 以 gtrans.d 為例, 重定向後為 gtrans.o: gtrans.cpp findGraph.hpp preProcess.hpp; sed 'script' -i [inputfile] 輸入文件為 .d 文件, 改寫后的結果也直接保存到該文件中。 sed 的腳本中 's/\($*\)\.o[ :]*/\1.o $@: /g' 's/pattern_1/pattern_2/g' 表示將 pattern_1 全部替換為 pattern_2。 那麼 \($*\)\.o[ :]* 與 \1.o $@: 這兩個 pattern 分別表示什麼呢?

\( \) 這兩個之間的匹配可以保存使用,一共可以存儲9個, 比如上面的例子, 第二個 pattern 中用到了一個 \1 就是使用第一次 \( \) 之間存儲匹配的東西, 也就是上面的 $*。 而 $* 也是 make 中的一個自動化變數, $* is the stem with which an implicit rule matches, stem 就是比如 %.d 那麼這個 % 就是 stem, 匹配 gtrans.d 那麼 gtrans 就是它的 stem。 後面就簡單了 \. 就是 escape '.' 字元 (因為 . 是可以匹配任何字元的特殊字元), [ :]* 就是空格與 * 的任意數目集。



4. 最後 make 的嵌套使用很簡單, 看一下總 Makefile 例子就懂了

.PHONY: all gtrans isgen cleanall cleanGtrans cleanIsgen

all:
cd GTRANS && make && cd ..; cd ISGEN && make && cd ..

gtrans:
cd GTRANS && make

isgen:
cd ISGEN && make

cleanall:
cd GTRANS && make clean && cd ..; cd ISGEN && make clean && cd ..

cleanGtrans:
cd GTRANS && make clean

cleanIsgen:
cd ISGEN && make clean

[火星人 ] linux編程初步之跟我學之Makefile的舒爽瘦身已經有428次圍觀

http://coctec.com/docs/linux/show-post-185044.html