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
whileyu 于 2011-11-09 17:40:25发表:
初学者,不懂。学习中...
lanlanlangzi 于 2011-11-03 14:31:57发表:
支持楼主{:3_122:}
wenhao1129 于 2011-11-01 21:26:03发表:
mark一下,很好的东西
ai0909 于 2011-11-01 14:21:47发表:
0:w(5(
ai0909 于 2011-11-01 14:21:36发表:
{:2_93:}
花开花败2011 于 2011-02-12 19:43:46发表:
我顶