红联Linux门户
Linux帮助

linux编程初步之跟我学之Makefile的舒爽瘦身

发布时间:2009-04-13 17:14:04来源:红联作者:kevin_2009
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
文章评论

共有 6 条评论

  1. whileyu 于 2011-11-09 17:40:25发表:

    初学者,不懂。学习中...

  2. lanlanlangzi 于 2011-11-03 14:31:57发表:

    支持楼主{:3_122:}

  3. wenhao1129 于 2011-11-01 21:26:03发表:

    mark一下,很好的东西

  4. ai0909 于 2011-11-01 14:21:47发表:

    0:w(5(

  5. ai0909 于 2011-11-01 14:21:36发表:

    {:2_93:}

  6. 花开花败2011 于 2011-02-12 19:43:46发表:

    我顶