Makefile学习10————为依赖关系文件建立依赖关系
现在我们再对complicated项目做一些更改,增加程序文件间依赖关系的复杂度。
/× main.c ×/ #include"foo.h" int main(void) { foo(); return 0; } /* foo.c */ #include<stdio.h> #include"foo.h" void foo(void) { printf("%s,This is foo()!\n",HELLO); } /* foo.h */ #ifndef __FOO_H #define __FOO_H #include "define.h" void foo(void); #endif /*__FOO_H*/ /* define.h */ #ifndef __DEFINE_H #define __DEFINE_H #define HELLO "hello" #endif/*__DEFINE_H*/
在之前的Makefile不做更改的情况下,我们make一下:
在这次成功编译的基础上,我们再做一些改动,注意在这之前不要执行make clean,否则不能发现新问题。
/× define.h */ #ifndef __DEFINE_H #define __DEFINE_H #include "other.h" #endif/*__DEFINE_H*/ /* other.h */ #ifndef __OTHER_H #define __OTHER_H #define HELLO "hello" #endif /*__OTHER_H*/
从结果看,尽管foo.c和main.c都被重新编译了,但依赖关系却没有重新构建。运行complicated结果,其打印结果是我们所希望的“hello”。
现在,我们对other.h文件进行修改把hello改成hi,从下面运行结果来看,项目并没有因为更改了other文件而重新编译。
#ifndef __OTHER_H #define __OTHER_H #define HELLO "hi" #endif /*__OTHER_H*/
更改Makefile如下,更改部分红色标出了,其实只增加了一个$@:
.PHONY: all clean MKDIR = mkdir RM = rm RMFLAGS = -rf CC=gcc DIR_OBJS=objs DIR_EXES=exes DIR_DEPS=deps DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) EXE=complicated EXE:=$(addprefix $(DIR_EXES)/,$(EXE)) SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o) OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS)) DEPS=$(SRCS:.c=.dep) DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS)) ifeq ($(wildcard $(DIR_OBJS)),) DEP_DIR_OBJS :=$(DIR_OBJS) endif#dir_objs ifeq ($(wildcard $(DIR_EXES)),) DEP_DIR_EXES :=$(DIR_EXES) endif#dir_exes ifeq ($(wildcard $(DIR_DEPS)),) DEP_DIR_DEPS :=$(DIR_DEPS) endif#dir_deps all: $(EXE) include $(DEPS) $(DIRS): $(MKDIR) $@ $(EXE):$(DEP_DIR_EXES) $(OBJS) $(CC) -o $@ $(filter %.o,$^) $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c $(CC) -o $@ -c $(filter %.c,$^) $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c @echo "Creating $@ ..." @set -e;\ $(RM) $(RMFLAGS) $@.tmp;\ $(CC) -E -MM $(filter %.c,$^) > $@.tmp;\ sed 's,\(.*\)\.o[:]*,objs/\1.o $@:,g' <$@.tmp >$@;\ $(RM) $(RMFLAGS) $@.tmp clean: $(RM) $(RMFLAGS) $(DIRS)
这样之后,Makefile就可以知道other.h的更改了。因为$@表示的是依赖关系的文件名,这个问题(Makefile 6随笔中要把foo.h加在依赖关系中的更改一样)和之前的那个问题解决方案一样,用sed命令可以做到,问题的根本在于,我们应该为依赖文件增加依赖关系,这样才能将整个项目全局联系在一起。
但是,这样之后,连续执行两次make clean:
第一次clean时,没什么问题,也是我们期望的,第二次clean时,make会先构建依赖项,紧接着又把目录删除。为什么第二次clean时,make会重新构建依赖文件?因为我们有一个include指令,他会优先于目标执行!!!
为了解决这个问题,我们再运用条件语法,并且用到我们之前提到的MAKECMDGOALS变量。更改后的Makefile如下:
.PHONY: all clean MKDIR = mkdir RM = rm RMFLAGS = -rf CC=gcc DIR_OBJS=objs DIR_EXES=exes DIR_DEPS=deps DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) EXE=complicated EXE:=$(addprefix $(DIR_EXES)/,$(EXE)) SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o) OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS)) DEPS=$(SRCS:.c=.dep) DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS)) ifeq ($(wildcard $(DIR_OBJS)),) DEP_DIR_OBJS :=$(DIR_OBJS) endif#dir_objs ifeq ($(wildcard $(DIR_EXES)),) DEP_DIR_EXES :=$(DIR_EXES) endif#dir_exes ifeq ($(wildcard $(DIR_DEPS)),) DEP_DIR_DEPS :=$(DIR_DEPS) endif#dir_deps all: $(EXE) ifneq ($(MAKECMDGOALS),clean) include $(DEPS) endif#clean $(DIRS): $(MKDIR) $@ $(EXE):$(DEP_DIR_EXES) $(OBJS) $(CC) -o $@ $(filter %.o,$^) $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c $(CC) -o $@ -c $(filter %.c,$^) $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c @echo "Creating $@ ..." @set -e;\ $(RM) $(RMFLAGS) $@.tmp;\ $(CC) -E -MM $(filter %.c,$^) > $@.tmp;\ sed 's,\(.*\)\.o[:]*,objs/\1.o $@:,g' <$@.tmp >$@;\ $(RM) $(RMFLAGS) $@.tmp clean: $(RM) $(RMFLAGS) $(DIRS)
这样就解决了。
再看一个例子:
all: @echo "command of rule" all: dep dep: @echo "prerequisite of rule"
这个make的特性说明了一个问题:在生成的依赖关系文件中,其中的规则只描述了依赖关系,而没有任何的命令,make是怎么知道使用哪些命令进行目标构建的呢?
当一个Makefile中存在构建同一目标的不同规则时,make会将这些规则合在一起,合并的内容包括先决条件和命令。尽管在自动生成的依赖关系文件中只存在目标和先决条件,但是由于Makefile中已经定义了.o 和.dep文件的生成规则,因此make会将这两部分结合在一起,从而形成最终针对一个(类)构建目标的规则。上面的那个例子可以很好地帮助我们理解这个make特性。
欢迎加入作者的小圈子
扫描下方左边二维码加入QQ交流群,扫描下方右边二维码关注个人微信公众号并,获取更多隐藏干货,QQ交流群:859800032 微信公众号:Crystal软件学堂
作者:Liu_Jing bilibili视频教程地址:https://space.bilibili.com/5782182 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在转载文章页面给出原文连接。 如果你觉得文章对你有所帮助,烦请点个推荐,你的支持是我更文的动力。 文中若有错误,请您务必指出,感谢给予我建议并让我提高的你。 |