当修改一个非子目录下的.h的时候
因为修改头文件,可以把新的头文件包含在文件中,需要重新生成.o的依赖关系,但是因为.depend只依赖了cpp所以不会因为头文件的修改而修改.depend文件,实际上这样是错误的。
因为 aode.o: aode.h 所以它就会根据.o的依赖关系重新生成.o 速度十分快
对于naomi文件夹下的.depend里的对象是
naominbaode.o: naomi/naominbaode.cpp naomi/naominbaode.h \
naomi/../incrementalLearner.h naomi/../learner.h \
naomi/../instanceStream.h naomi/../instance.h naomi/../capabilities.h \
naomi/../xxxxyDist.h naomi/../xxxyDist.h naomi/../xxyDist.h \
naomi/../xyDist.h naomi/../smoothing.h naomi/../utils.h \
naomi/../mtrand.h naomi/../FILEtype.h naomi/../crosstab.h
并不存在的一个目标,正确的路径是naomi/ naominbaode.o
首先按照道理来说 naominbaode.o根本不存在,那么每次编译的时候就会重新产生naomiaode.o,但是事实并非如此:
比如我删除了aode.o就会重新生成.o,因为petal的依赖的.o不存在
但是我修改了aode.cpp之后,并没有重新生成naominbaode.o,仅仅重新生成了aode.o
注:不可以去掉OBJ = naomi/naomiaode.o naomi/naominbaode.o 里的naomi
然后修改
改naominbaode.cpp会自动地重新生成.o 和.depend
但是依旧能正确地产生
g++ -c -o naomi/naominbaode.o naomi/naominbaode.cpp
但是如果改的是naominbaode.h就 不同了,
并没有重新生成g++ -c -o naomi/naominbaode.o naomi/naominbaode.cpp
而只修改了learnerRegistry.cpp
如果真的能找到naominbaode.o的话,它依赖了naomi/naominbaode.h,那么头文件被修改,.o应该重新生成,但是没有,如果真的找不到naominbaode.o的话,为什么修改了naomi/naominbaode.cpp会重新生成 naomi/naominbaode.o ,而且并不是每次都会重新生成naomi/naominbaode.o的,这样不一致的表现,为子目录版带来了巨大的困难,
说明这是路径问题,检查naominbaode.o是否需要重新产生的时候,根本没有找到它在哪里,它不在根目录下。
只要改成naomi/ naominbaode.o 修改naomi/naominbaode.h时就会连同naominbaode.o一起更新
naomi/naomiaode.o: naomi/naomiaode.cpp naomi/naomiaode.h \
naomi/../incrementalLearner.h naomi/../learner.h \
naomi/../instanceStream.h naomi/../instance.h naomi/../capabilities.h \
naomi/../xxxxyDist.h naomi/../xxxyDist.h naomi/../xxyDist.h \
在makefile下执行命令pwd;
cd ./naomi; \
pwd; \
pwd;
pwd;
Pwd就是显示当前目录
那么为什么要把几个命令写在"同一行"(是对于make来说,因为\的作用就是连接行),并用分号隔开每个命令?因为在Makefile这样做才能使上一个命令作用于下一个命令。
\为运行环境续命,第一个pwd把它的路径传给了第二个pwd,第二个pwd没有加\,所以最后一个pwd又反弹回了原来的路径。
一个目标多条如果有多条命令,就会忽略老的一条
而如果是这样的话
如果我修改了a.cpp b.h或者 a.h就会重新生成a.o
gcc –c a.pp生成a.o
把规则写在第二条下面也是一样的效果
所以相当于写成了
o: a.cpp b,h a.h
gcc –c a.cpp
有重复的效果也是一样的
其中a.cpp是重复的,但是没有关系
这为下面.d文件版分开写的规则奠定了基础
第四版:无子文件版参考:
https://segmentfault.com/a/1190000000349917
CC = g++
CFLAGS = -O3 -DNDEBUG
SOURCES = $(wildcard *.cpp)
OBJS := $(patsubst %.cpp, %.o,$(SOURCES))
petal:$(OBJS)
@echo "源文件:" $(SOURCES)
@echo "目标文件:" $(OBJS)
$(CC) -o $@ $(OBJS) $(CFLAGS)
%.d :%.cpp
@echo "create depend";
@set -e; \
gcc -MM $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
-include $(OBJS:.o=.d)
.PHONY:clean
clean:
@echo "开始清理项目..."
@echo "正在删除所有的.d文件"
rm -f $(OBJS:.o=.d)
@echo "正在删除所有的.o文件"
rm -rf $(OBJS)
@echo "正在删除petal"
rm -f petal.exe
@echo "清理结束"
前面两行很简单就是定义编译变量和编译选项。
SOURCES = $(wildcard *.c) 这句话意思是定义一个变量SOURCES,它的值包含当前目录下所有.c文件。 在我的例子里我把这个值打印出来了就是dList.c memory.c test.c debug.c
$(wildcard PATTEN) 是Makefile内建的一个函数:
函数名称:获取匹配模式文件名函数—wildcard
函数功能:列出当前目录下所有符合模式"PATTERN"格式的文件名。
返回值:空格分割的、存在当前目录下的所有符合模式"PATTERN"的文件名。
函数说明:"PATTERN"使用shell可识别的通配符,包括"?"(单字符)、"*"(多字符)等
OBJS := $(patsubst %.c, %.o,$(SOURCES)) 这一行是定义了一个变量OBJS,它的值是将变量SOURCES里的内容以空格分开,将所有.c文件替换成.o. 在我的例子里打印出来就是dList.o memory.o test.o debug.o。
$(patsubst PATTEN, REPLACEMENT, TEXT)也是内建函数
函数名称:模式替换函数—patsubst。
函数功能:搜索"TEXT"中以空格分开的单词,将否符合模式"TATTERN"替换为"REPLACEMENT"
sinclude $(SOURCES:.c=.d) 这一行是非常关键的,它在当前Makefile里去include另外的Makefile. 这里"另外"的Makefile是将SOURCES变量里所有.c替换成.d。 在我的例子里就是dList.d memory.d test.d debug.d. 意思就是执行到这里
的时候先去依次执行dList.d memory.d test.d debug.d. 这里的.d文件就是包含了每个.c文件自动生成的对头文件的依赖关系。这个依赖关系将由下面的%d:%c来完成。
%d: %c
此规则的含义是:所有的.d文件依赖于同名的.c文件。
第一行;使用c编译器自自动生成依赖文件($<)的头文件的依赖关系,并输出成为一个临时文件,"$$$$"表示当前进程号。如果$(CC)为GNU的c编译工具,产生的依赖关系的规则中,依赖头文件包括了所有的使用的系统头文件和用户定义的头文件。如果需要生成的依赖描述文件不包含系统头文件,可使用"-MM"代替"-M"。
第二行;使用sed处理第二行已产生的那个临时文件并生成此规则的目标文件。经过这一行后test.d里内容如下:test.o: test.c aaron.h dList.h debug.h 其他.d里以此类推。
第三行;删除临时文件。
.d文件的内容如下:
a2de3.o a2de3.d: a2de3.cpp a2de3.h incrementalLearner.h learner.h \
instanceStream.h instance.h capabilities.h xxxyDist3.h xxyDist.h \
xyDist.h smoothing.h utils.h mtrand.h FILEtype.h crosstab.h \
correlationMeasures.h xxxyDist.h globals.h
在.d文件里,不仅阐明了如何生成.o文件,而且阐明了如何生成它自己,结合
%d: %c规则,在第一次时sinclude $(SOURCES:.c=.d) 在例子里其实.d文件开始并不存在,所以当Makefile在include这些.d文件时首先看.d存在不,不存在就要去寻找.d的依赖文件和规则。这里就找到了%d: %c从而创建出真正的.d文件。
而后来头文件或者cpp发生修改了,.makefile会将所读取的每个makefile(.d)作为一个目标,如重新生成a.d 寻找更新a.d的规则。如果需要重新产生a.d就重新产生,重新引入,所以会重新产生.d文件。这样的规则是符合逻辑的,因为不论是头文件还是cpp的修改,都有可能导致目标文件的依赖关系发生变化,必须重新生成依赖文件,解决了上一版的问题。
到这里基本的意义弄明白了,但是让我不解的是%d: %c这个依赖的规则怎么能被执行到的?按照我的理解Makefile在执行时首先检查终极目标main是否存在,如果不存在则建立(根据main的依赖规则),如果存在在需要查看
main的依赖文件是否存在并且是最新的,这我的例子里就是要看test.o dList.o memory.o debug.o是否存在且最新。这样追下去是否没有%d: %c什么事啊, .d文件也应该不存在或者说是空的。尽管我们include了.d文件,但是没有依赖规则去执行它啊。后来仔细阅读了
Makefile文件的重建才明白了。
Makefile如果由其它文件重建(这里我的Makefile include了所有.d文件,.d也可以看成是一个Makefile),Makefile在读入所有其他makefile文件(.d)之后,先把这些.d包括进来,然后将所读取的每个makefile(.d)作为一个目标,如重新生成a.d 寻找更新a.d的规则。如果需要重新产生a.d就重新产生,重新引入 同样
如果此目标不存在则根据依赖规则重新创建。其实这里的关键点就是对于
include了理解,它是把include的文件首先当成一个目标,然后要去寻找其依赖文件和规则的,而不是我事先想象的简单的把其他文件的内容包含过来。
到此,问题解决,基本达到预期。