总结(一)中实验3的Makefile有一个缺点,Makefile中的通用规则中目标文件/.o只依赖源文件%.c。如果源文件中使用了头文件中定义的宏,在头文件修改后,执行Make并不会重新编译目标。
本节的目标是解决头文件依赖,实现一个实际工作中可用的Makefile。
为了增加目标头文件的依赖,可以简单地在Makefile中增加一条依赖。
a.o : xxx.h
make命令会判断通用规则%.o:%c和新增加的规则,认为a.o这个目标即依赖于a.c,又依赖于头文件xxx.h。如此一来,头文件xxx.h修改后,目标a.o就会重新编译。
手工添加依赖无法满足实际工作需求。一般工程中有许多源文件,每个源文件通常包含不止一个头文件,这样在Makefile中手工添加规则是不现实的。为了解决这个问题,需要使用gcc的某些特殊选项和Makefile的函数。
首先,在编译源文件时自动解析依赖并生成源文件的依赖。
gcc的-M选项可以查看.c文件的依赖
gcc -M a.c
在Makefile中需要使用gcc的-MD -MF选项,在编译源文件时生成依赖规则。
%.o : %.c
gcc -c -o $@ $< -MD -MF $@.d
这样在编译a.c文件时,就会在目录下生成目标文件a.o和依赖文件a.o.d
接下来,在Makefile中根据需要把依赖文件引入到Makefile中。
定义一个变量objs,这个变量用来保存那些参与链接app的文件的
OBJS := a.o b.o
app : $( OBJS)
gcc -o $@ $^
这样,如果新增加了c.c文件,只需要在变量OBJS后增加c.o,就可以把c.c文件添加到工程中。
接着,定义一个变量保存所有的依赖文件。
如果变量OBJS是a.o b.o,那么依赖文件应该是a.o.d b.o.d。这可以通过以下两种方式实现。
方式1,DEP := $(foreach var,$( OBJS),$(var).d)
方式2,DEP:= $(patsubst % %.d $( OBJS))
然后,判断变量DEP中的文件是否真的存在,得到真实存在的文件。
DEP := $(wildcard $(DEP))
最后,判读DEP变量是否为空,如果不为空,就把变量DEP中的依赖引入到当前Makefile中
ifneq ($(DEP),)
include $(DEP)
endif
在Makefile中添加以上内容之后,就可以实现自动引入头文件的依赖。
需要注意的是,如果要把app作为默认目标,需要把上面最后的判断放在app这条规则之后。