Makefile自动生成头文件依赖是很经常使用的功能,本文的目的是想尽可能详细说明其中的原理和过程。html
首先给出一个本人在小项目中经常使用的Makefile模板,支持自动生成头文件依赖。c++
makefileCC = gcc CFLAGS = -Wall -O INCLUDEFLAGS = LDFLAGS = OBJS = seq.o TARGETS = test_seq .PHONY:all all : $(TARGETS) test_seq:test_seq.o $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) %.o:%.c $(CC) -o $@ -c $< $(CFLAGS) $(INCLUDEFLAGS) %.d:%.c @set -e; rm -f $@; $(CC) -MM $< $(INCLUDEFLAGS) > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ -include $(OBJS:.o=.d) .PHONY:clean clean: rm -f $(TARGETS) *.o *.d *.d.*
在进行下一步以前,首先须要了解make的执行步骤:正则表达式
从上面make的执行过程当中可看出,要动态生成依赖关系,只能利用第2步读入其它Makefile的机制。那么,咱们是否能够先把生成的依赖关系保存到文件,而后再把该文件的内容包含进来?
答案是Yes! 只要利用include的机制。shell
include关键字是用于读入其它Makefile文件。当该文件不存在时,make会寻找是否有生成它的规则,若是有,则执行其生成命令,而后再尝试读入。在include前加减号"-"能够上make忽略其产生的错误,并不输出任何错误信息。编程
便是说,咱们须要提供生成规则文件的规则。例如,咱们能够这样动态生成头文件依赖关系:code
makefileseq.d : seq.c @echo “seq.o seq.d : seq.c seq.h" > $@ -include seq.d
当make执行时,Makefile中的内容将是这样子(指内存上的数据):htm
makefileseq.d : seq.c @echo “seq.o seq.d : seq.c seq.h" > $@ seq.o seq.d : seq.c seq.h
特别注意的是,因为对seq.c和seq.h的修改须要更新seq.d的内容(由于依赖关系可能已变化),所以seq.d也要在依赖关系的目标列表中。进程
基于上面的例子,如今能够开始讨论如何自动生成头文件依赖。内存
大多数c/c++编译器提供了-M选项,可自动寻找源文件依赖的头文件,并生成依赖规则。对于gcc,须要使用-MM选项,不然它会把系统依赖的头文件也包含进来。例如执行下面一个命令:文档
shellgcc -MM seq.c
将输出:
makefileseq.o : seq.c seq.h
但咱们须要结果是seq.d也要包含在目标列表中,因此还须要对它进行文本处理。所以,上面的例子可改成:
makefileseq.d : seq.c @set -e; \ gcc -MM $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ -include seq.d
第一个命令@set -e
。@关键字告诉make不输出该行命令;set -e的做用是,当后面的命令的返回值非0时,当即退出。
那么为何要把几个命令写在”同一行“(是对于make来讲,由于\的做用就是链接行),并用分号隔开每一个命令?由于在Makefile这样作才能使上一个命令做用于下一个命令。这里是想要set -e做用于后面的命令。
第二个命令gcc -MM $< > $@.$$$$
, 做用是根据源文件生成依赖关系,并保存到临时文件中。内建变量$<
的值为第一个依赖文件(那seq.c),$$$$
为字符串"$$"
,因为makefile中全部的$字符都是特殊字符(即便在单引号之中!),要获得普通字符$,须要用$$
来转义; 而$$
是shell的特殊变量,它的值为当前进程号;使用进程号为后缀的名称建立临时文件,是shell编程经常使用作法,这样可保证文件惟一性。
第三个命令做用是将目标文件加入依赖关系的目录列表中,并保存到目标文件。关于正则表达式部分就不说了,惟一要注意的是内建变量$*
,$*
的值为第一个依赖文件去掉后缀的名称(这里便是seq)。
第四个命令是将该临时文件删除。
若是把内建变量都替换成其值后,实际内容是这样子:
makefileseq.d : seq.c @set -e; \ gcc -MM seq.c > seq.d.$$$$; \ sed 's,\(seq\)\.o[ :]*,\1.o seq.d : ,g' < seq.d.$$$$ > seq.d; \ rm -f seq.d.$$$$ -include seq.d
最后,再把Makefile的模式匹配应用上,就完成自动生成头文件依赖功能了:
makefile%.d : %.c @set -e; \ gcc -MM $@ > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ -include seq.d
<跟我一块儿写Makefile> by 陈晧
GNU make官方文档 http://www.gnu.org/software/make/manual/make.html