参考:
http://www.itpub.net/thread-219475-1-1.html
最近要开始学习kernel代码,在Unix/linux下工作当让也要学会写makefile,makefile其实就是一个shell脚本,完成自动化编译的过程。
首先我们写出来的是一个针对特定工程的makefile
edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o main.o : main.c defs.h cc -c main.c kbd.o : kbd.c defs.h command.h cc -c kbd.c command.o : command.c defs.h command.h cc -c command.c display.o : display.c defs.h buffer.h cc -c display.c insert.o : insert.c defs.h buffer.h cc -c insert.c search.o : search.c defs.h buffer.h cc -c search.c files.o : files.c defs.h buffer.h command.h cc -c files.c utils.o : utils.c defs.h cc -c utils.c .PHONY:clean clean : -rm edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
反斜杠(\)是换行符的意思。这样比较便于Makefile的易读。我们可以把这个内容保存在文件为“Makefile”或“makefile”的文件中,然后在该目录下直接输入命令“make”就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下“make clean”就可以了。
下面我们来说明如何写出万能的makefile,其中wildcard函数主要用于获取工作目录的下的特定文件。
SOURCES = $(wildcard *.c)
这行会产生一个所有以 ‘.c’ 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。
另一个有用的函数是 patsubst ( patten substitude, 匹配替 换的缩写)函数。它需要3个参数——第一个是一个需要匹配的式样,第二个表示用什么来替换它,第三个是一个需要被处理的 由空格分隔的字列。例如,处理那个经过上面定义后的变量,
OBJS = $(patsubst %.c,%.o,$(SOURCES))
这行将处理所有在 SOURCES 字列中的字(一列文件名),如果它的 结尾是 ‘.c’ ,就用 ‘.o’ 把 ‘.c’ 取代。注意这里的 % 符号将匹 配一个或多个字符,而它每次所匹配的字串叫做一个‘柄’(stem) 。 在第二个参数里, % 被解读成用第一参数所匹配的那个柄。
dependence=$(sources:.c=.d)
这里,dependence是所有.d文件的列表.即把串sources串里的.c换成.d
$@:目标文件
$<:首个依赖文件
$*这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make
$@表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
我们举个例子:
g++ -MM main.c > main.d.temp sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' < main.d.temp > main.d
这条sed命令的结构是s/match/replace/g
第1段是\(main\),在sed命令里把main用\(和\)括起来,使接下来的replace中可以用\1引用main。
第2段是\.o,表示匹配main.o,(这里\不知何意,去掉也是可以的)。
第3段是正则式[ :]*,表示若干个空格或冒号,(其实一个.d里只会有一个冒号,如果这里写成[ ]*:,即匹配若干个空格后跟一个冒号,也是可以的)。
总体来说match用来匹配’main.o :’这样的串。
这里的replace是\1.o main.d :,其中\1会被替换为前面第1个\(和\)括起的内容,即main,这样replace值为main.o main.d :
这样该sed命令就实现了把main.o :替换为main.o main.d :的目的。
最后我们导出一个全自动化脚本
define gen_dep set -e; rm -f $@; \ $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ endef #指示如何由.c生成其依赖规则文件.d #这段使用make的模式规则,指示对每个.c文件,如何生成其依赖规则文件.d,调用上面的命令包即可 %.d: %.c $(gen_dep) #同上,指示对每个.cpp,如何生成其依赖规则文件.d %.d: %.cpp $(gen_dep)
写makefile 要求理解前面makefile几个关键点:
1-多目标
2-隐含规则
3-定义模式规则
4-自动生成依赖性
这样才能写出万能的makefile文件,而不是仅针对特殊的project。