Makefile 9——为依赖关系文件建立依赖关系

现在我们再对complicated项目做一些更改,增加程序文件间依赖关系的复杂度。

 1 /×    main.c   ×/
 2 #include"foo.h"
 3 int main(void)
 4 {
 5     foo();
 6     return 0;
 7 }
 8 
 9 /*    foo.c    */
10 #include<stdio.h>
11 #include"foo.h"
12 void foo(void)
13 {
14     printf("%s,This is foo()!\n",HELLO);
15 }
16 
17 
18 /*   foo.h     */
19 #ifndef __FOO_H
20 #define __FOO_H
21 #include "define.h"
22 void foo(void);
23 
24 #endif /*__FOO_H*/
25 
26 /*    define.h    */
27 #ifndef __DEFINE_H
28 #define __DEFINE_H
29 
30 #define HELLO "hello"
31 
32 #endif/*__DEFINE_H*/

在之前的Makefile不做更改的情况下,我们make一下:

 

 在这次成功编译的基础上,我们再做一些改动,注意在这之前不要执行make clean,否则不能发现新问题。

 

 1 /×    define.h    */
 2 #ifndef __DEFINE_H
 3 #define __DEFINE_H
 4 
 5 #include "other.h"
 6 
 7 #endif/*__DEFINE_H*/
 8 
 9 
10 /*   other.h    */
11 #ifndef __OTHER_H
12 #define __OTHER_H
13 
14 #define HELLO "hello"
15 
16 #endif /*__OTHER_H*/

 

 

从结果看,尽管foo.c和main.c都被重新编译了,但依赖关系却没有重新构建。运行complicated结果,其打印结果是我们所希望的“hello”。

现在,我们对other.h文件进行修改把hello改成hi,从下面运行结果来看,项目并没有因为更改了other文件而重新编译。

1 #ifndef __OTHER_H
2 #define __OTHER_H
3 
4 #define HELLO "hi"
5 
6 #endif /*__OTHER_H*/

更改Makefile如下,更改部分红色标出了,其实只增加了一个$@:

 1 .PHONY: all clean
 2 
 3 MKDIR = mkdir
 4 RM = rm
 5 RMFLAGS = -rf
 6 
 7 CC=gcc
 8 
 9 DIR_OBJS=objs
10 DIR_EXES=exes
11 DIR_DEPS=deps
12 
13 DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
14 
15 EXE=complicated
16 EXE:=$(addprefix $(DIR_EXES)/,$(EXE))
17 SRCS=$(wildcard *.c)
18 OBJS=$(SRCS:.c=.o)
19 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
20 DEPS=$(SRCS:.c=.dep)
21 DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS))
22 
23 ifeq ($(wildcard $(DIR_OBJS)),)
24 DEP_DIR_OBJS :=$(DIR_OBJS)
25 endif#dir_objs
26 ifeq ($(wildcard $(DIR_EXES)),)
27 DEP_DIR_EXES :=$(DIR_EXES)
28 endif#dir_exes
29 ifeq ($(wildcard $(DIR_DEPS)),)
30 DEP_DIR_DEPS :=$(DIR_DEPS)
31 endif#dir_deps
32 all: $(EXE)
33 
34 include $(DEPS)
35 
36 $(DIRS):
37     $(MKDIR) $@
38 $(EXE):$(DEP_DIR_EXES) $(OBJS)
39     $(CC) -o $@ $(filter %.o,$^)
40 $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c 
41     $(CC) -o $@ -c $(filter %.c,$^)
42 $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c
43     @echo "Creating $@ ..."
44     @set -e;\
45     $(RM) $(RMFLAGS) $@.tmp;\
46     $(CC) -E -MM $(filter %.c,$^) > $@.tmp;\
47     sed 's,\(.*\)\.o[:]*,objs/\1.o $@:,g' <$@.tmp >$@;\
48     $(RM) $(RMFLAGS) $@.tmp
49 
50 clean:
51     $(RM) $(RMFLAGS) $(DIRS)

这样之后,Makefile就可以知道other.h的更改了。因为$@表示的是依赖关系的文件名,这个问题(Makefile 6随笔中要把foo.h加在依赖关系中的更改一样)和之前的那个问题解决方案一样,用sed命令可以做到,问题的根本在于,我们应该为依赖文件增加依赖关系,这样才能将整个项目全局联系在一起。

但是,这样之后,连续执行两次make clean:

第一次clean时,没什么问题,也是我们期望的,第二次clean时,make会先构建依赖项,紧接着又把目录删除。为什么第二次clean时,make会重新构建依赖文件?因为我们有一个include指令,他会优先于目标执行!!!

为了解决这个问题,我们再运用条件语法,并且用到我们之前提到的MAKECMDGOALS变量。更改后的Makefile如下:

 1 .PHONY: all clean
 2 
 3 MKDIR = mkdir
 4 RM = rm
 5 RMFLAGS = -rf
 6 
 7 CC=gcc
 8 
 9 DIR_OBJS=objs
10 DIR_EXES=exes
11 DIR_DEPS=deps
12 
13 DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
14 
15 EXE=complicated
16 EXE:=$(addprefix $(DIR_EXES)/,$(EXE))
17 SRCS=$(wildcard *.c)
18 OBJS=$(SRCS:.c=.o)
19 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
20 DEPS=$(SRCS:.c=.dep)
21 DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS))
22 
23 ifeq ($(wildcard $(DIR_OBJS)),)
24 DEP_DIR_OBJS :=$(DIR_OBJS)
25 endif#dir_objs
26 ifeq ($(wildcard $(DIR_EXES)),)
27 DEP_DIR_EXES :=$(DIR_EXES)
28 endif#dir_exes
29 ifeq ($(wildcard $(DIR_DEPS)),)
30 DEP_DIR_DEPS :=$(DIR_DEPS)
31 endif#dir_deps
32 
33 all: $(EXE)
34 ifneq ($(MAKECMDGOALS),clean)
35 include $(DEPS)
36 endif#clean
37 
38 $(DIRS):
39     $(MKDIR) $@
40 $(EXE):$(DEP_DIR_EXES) $(OBJS)
41     $(CC) -o $@ $(filter %.o,$^)
42 $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c 
43     $(CC) -o $@ -c $(filter %.c,$^)
44 $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c
45     @echo "Creating $@ ..."
46     @set -e;\
47     $(RM) $(RMFLAGS) $@.tmp;\
48     $(CC) -E -MM $(filter %.c,$^) > $@.tmp;\
49     sed 's,\(.*\)\.o[:]*,objs/\1.o $@:,g' <$@.tmp >$@;\
50     $(RM) $(RMFLAGS) $@.tmp
51 
52 clean:
53     $(RM) $(RMFLAGS) $(DIRS)

这样就解决了。

再看一个例子:

1 all:
2     @echo "command of rule"
3 
4 all: dep
5 
6 dep:
7     @echo "prerequisite of rule"

这个make的特性说明了一个问题:在生成的依赖关系文件中,其中的规则只描述了依赖关系,而没有任何的命令,make是怎么知道使用哪些命令进行目标构建的呢?

当一个Makefile中存在构建同一目标的不同规则时,make会将这些规则合在一起,合并的内容包括先决条件和命令。尽管在自动生成的依赖关系文件中只存在目标和先决条件,但是由于Makefile中已经定义了.o 和.dep文件的生成规则,因此make会将这两部分结合在一起,从而形成最终针对一个(类)构建目标的规则。上面的那个例子可以很好地帮助我们理解这个make特性。

 

posted @ 2017-05-10 17:45  Crystal_Guang  阅读(2593)  评论(0编辑  收藏  举报