手工编写依赖关系不只工做量大并且极易出现遗漏,更新也很难及时,修改源或头文件后makefile可能忘记修改。为了解决这个问题,能够用gcc的-M选项自动生成目标文件和源文件的依赖关系。-M选项会把包含的系统头文件以及其所包含的其余系统头文件也找出来了,若是咱们不须要输出系统头文件的依赖关系时,能够用-MM选项。ui
下面咱们以一个简单的例子来讲明如何自动生成依赖关系:atom
exm/spa
main.ccode
s.c进程
s.hclass
makefile文件内容以下:gcc
all:ased
src=$(wildcard *.c)file
obj:=$(patsubst %.c,%.o,$(src))gc
ifneq($(MAKECMDGOALS),clean)
-include$(src:.c=.d)
endif
a:$(obj)
gcc$(obj)-o $@
%.d:%.c
set-e;rm -f $@; \
gcc-MM$(CPPFLAGS) $< > $@.
; \
sed's,$∗\.o[:]*,\1.o $@ : ,g' < $@.
> $@; \
rm-f$@.
%.o:%.c
@echo'Buildingfile: $<'
@echo'Invoking:GCC C Compiler'
gcc-O0-g3 -Wall -c -o "$@" "$<"
@echo'Finishedbuilding: $<'
@echo''
其中wildcard做用就是将指定目录下.c文件所有找出,因此这里src=main.cs.c
patsubst做用是把$(src)中的.c所有换为.o,因而obj=main.os.o
include$(src:.c=.d)至关于includemain.ds.d
因为此时这两个文件并不存在,因此会出现下面提示:
makefile:6:main.d:没有那个文件或目录
makefile:6:s.d:没有那个文件或目录
若是不想要这个提示,能够将include替换为-include
尽管一开始找不到.d文件,因此make会报警告。可是make会把include的文件名也看成目标来尝试更新,而这些目标适用模式规则%.d:%c
注意,虽然在
Makefile中这个命令写了四行,但实际上是一条命令,
make只建立一个
Shell进程执行这条命令,这条命令分为
5个子命令,用
;号隔开,而且为了美观,用续行符
\拆成四行来写。执行步骤为:
1)set-e命令设置当前
Shell进程为这样的状态:若是它执行的任何一条命令的退出状态非零则马上终止,再也不执行后续命令。
@表示
makefile执行这条命令时不显示出来
2)把原来的.d文件
删掉。
3)$<依赖的目标集(即*.c), -MM:表示生成文件依赖关系,$@:表示生成的目标文件(即*.d),$$:表示自己的ProcessID。注意,在Makefile中$有特殊含义,若是要表示它的字面意思则须要写两个$,因此Makefile中的四个$传给Shell变成两个$,两个$在Shell中表示当前进程的id,通常用它给临时文件起名,以保证文件名惟一。
4)这个sed命令比较复杂,就不细讲了,主要做用是查找替换,并加入.d的依赖关系。
5)最后把临时文件删掉。
不论是
Makefile自己仍是被它包含的文件,只要有一个文件在
make过程当中被更新了,
make就会从新读取整个
Makefile以及被它包含的全部文件,如今
main.d、
stack.d和
maze.d都生成了,就能够正常包含进来了,至关于在
Makefile中添了下面规则:
main.omain.d : main.c s.h
s.os.d : s.c s.h
当源或头文件修改时,若是依赖关系发生变化,执行
makefile时将更新具备依赖关系的
.d文件,而
.d文件的更新又促使
make从新读取
makefile文件,把新的
.d文件包括进来,因而新的依赖关系被创建。
除了上面方法外,还可以使用GCC的-MMD-MP -MF -MT选项,以下,可起到一样目的:
all:a
src=$(wildcard *.c)
obj:=$(patsubst %.c,%.o,$(src))
ifneq($(MAKECMDGOALS),clean)
-include$(src:.c=.d)
endif
a:$(obj)
gcc$(obj)-o $@
%.o:%.c
@echo'Buildingfile: $<'
@echo'Invoking:GCC C Compiler'
gcc-O0-g3 -Wall -c -fmessage-length=0 -MMD -MP-MF"$(@:%.o=%.d)"-MT"$(@:%.o=%.d)" -o "$@""$<"
@echo'Finishedbuilding: $<'
@echo''