参考 跟我一起写Makefile http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=408225&extra=&authorid=10610714&page=1
首先回顾《跟我一起写Makefile》里有一节 自动生成依赖性。利用gcc中的一个编译选项 -MM,得到源文件的依赖文件,然后将其写入一个.d文件,每次make命令执行时,利用include 命令将.d文件内的依赖关系包含进makefile里,这样每个源文件是否需要重新编译可根据.d依赖文件的情况判定。
%.d: %.c set -e; rm -f $@; \ $(CC) -M $(CPPFLAGS) $< >; $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$
这段是《跟我一起写Makefile》里生成依赖文件.d的代码。
set -e ; rm -f $@; 第一句将shell设置为当命令输出非0时则退出,第二句删除原有.d文件
$(CC) -M $(CPPFLAGS) $< >; $@.$$$$;该命令对源文件生成 依赖文件,并将其保存为$@.$$$$,后面的一串$$$$表示文件的后缀是一个随机码(听说是表示进程号),反正是为了不可能与其他文件重复,只是作为一个临时文件而已
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;该命令是利用shell里的sed命令中的 sed 's/原有内容/新内容/g' ,完成一个内容替换,其中“原有内容”和“新内容”可以用模式表达,'/'也可以用','代替。
而后面的 "< $@.$$$ > $@"则是个shell命令的一个流导向,作用是将上一个命令生成的$@.$$$$文件内容导入到sed命令来进行替换处理(该处理是以行为单位进行的),接着将替换后的结果保存到 $@文件中
最后一行则是将临时生成的文件删除。
怎样将这一段代码插入到makefile中实现自动推导依赖关系,使我们写makefile时可以不用事先知道每个源文件具体包含哪些头文件?
这个可以通过include命令来实现,下面通过一个具体实例来说明
代码文件结构
main.cpp 包含 hello.h
hello.cpp 包含 hello.h
这时我们可以这样写makefile(此时所有头文件和源文件都在同一个目录,且makefiel也在该目录)
ifndef GXX GXX := g++ endif ifndef CXXFLAGS CXXFLAGS := -g endif CxxSources := $(shell find -iname *.cpp ) #自动寻找当前目录下的源文件 Objs := $(CxxSources:.cpp=.o) execfile:$(Objs) $(GXX) -o $@ $(CXXFLAGS) $(Objs) -include $(CxxSources:.cpp=.d) %.d : %.cpp rm -f $@; \ $(GXX) -MM $(INCLUDE) $< > $@.$$$$; \ sed -e 's,^.*:,$*.o $@:,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ .PHONY : clean clean: @echo $(CxxSources) rm execfile $(CxxSources:.cpp=.d) $(CxxSources:.cpp=.o)
通过include命令,在每次执行make时,都会先将*.d文件内各个目标文件.o的依赖关系导入,这样就像自己显示方式在makefile里添加目标的依赖关系一样。
上面这个方式是所有源文件和头文件在同一个目录下,那么当工程复杂时需要头文件可能和源文件在不同的目录下,并且makefile与头文件和源文件在不同目录下,上面的方法就不行了。在上面生成.d文件文件过程中,我们可以继续进一步添加内容(源文件的编译过程),使编译不是按隐式规则进行。
这时代码结构改变为
工程目录ex1,在该目录下有head和src目录,分别存放头文件和源文件,makefile文件在ex1下。头文件和源文件的依赖关系跟上面一样。这时makefile为
ifndef GXX GXX := g++ endif ifndef CXXFLAGS CXXFLAGS := -g endif INCLUDE := -Ihead CxxSources := $(shell find -iname *.cpp ) Objs := $(CxxSources:.cpp=.o) execfile:$(Objs) @echo "CXXSources:" $(CxxSources) @echo "Objs:" $(Objs) $(GXX) -o $@ $(CXXFLAGS) $(Objs) -include $(CxxSources:.cpp=.d) %.d : %.cpp rm -f $@; \ $(GXX) -MM $(INCLUDE) $< > $@.$$$$; \sed -e 's,^.*:,$*.o $@:,g' -e 's,$$,; $(GXX) -c $(CXXFLAGS) $(INCLUDE) $<,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ .PHONY : clean clean: @echo $(CxxSources) rm execfile $(CxxSources:.cpp=.d) $(CxxSources:.cpp=.o)通过这种方式,生成的每个.d文件内不但包含了.o目标的依赖关系,还包含了一条编译命令生成.o
$(GXX) -c $(CXXFLAGS) $(INCLUDE) $<,g' < $这条命令指定了编译时自动搜索head文件夹的内容,如果不加这个内容,采用隐式规则生成时会提示找不到.cpp源文件包含的头文件。
后来,我发现一个更简单的方法,即CXXFLAGS += INCLUDE ,这样不要在.d文件里添加自定义的编译规则了。