Makefile自动生成依赖,自动化编译(2)

当修改一个非子目录下的.h的时候

因为修改头文件,可以把新的头文件包含在文件中,需要重新生成.o的依赖关系,但是因为.depend只依赖了cpp所以不会因为头文件的修改而修改.depend文件,实际上这样是错误的。

Makefile自动生成依赖,自动化编译

因为 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不存在

Makefile自动生成依赖,自动化编译

但是我修改了aode.cpp之后,并没有重新生成naominbaode.o,仅仅重新生成了aode.o

Makefile自动生成依赖,自动化编译

注:不可以去掉OBJ = naomi/naomiaode.o naomi/naominbaode.o 里的naomi

Makefile自动生成依赖,自动化编译

然后修改

改naominbaode.cpp会自动地重新生成.o 和.depend

但是依旧能正确地产生

g++ -c -o naomi/naominbaode.o naomi/naominbaode.cpp

Makefile自动生成依赖,自动化编译

但是如果改的是naominbaode.h就 不同了,

Makefile自动生成依赖,自动化编译

并没有重新生成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自动生成依赖,自动化编译

在makefile下执行命令

pwd;

cd ./naomi; \

pwd; \

pwd;

pwd;

Makefile自动生成依赖,自动化编译

Pwd就是显示当前目录

那么为什么要把几个命令写在"同一行"(是对于make来说,因为\的作用就是连接行),并用分号隔开每个命令?因为在Makefile这样做才能使上一个命令作用于下一个命令。

\为运行环境续命,第一个pwd把它的路径传给了第二个pwd,第二个pwd没有加\,所以最后一个pwd又反弹回了原来的路径。

一个目标多条

Makefile自动生成依赖,自动化编译

Makefile自动生成依赖,自动化编译

如果有多条命令,就会忽略老的一条

而如果是这样的话

Makefile自动生成依赖,自动化编译

如果我修改了a.cpp b.h或者 a.h就会重新生成a.o

gcc –c a.pp生成a.o

Makefile自动生成依赖,自动化编译

把规则写在第二条下面也是一样的效果

Makefile自动生成依赖,自动化编译

所以相当于写成了

o: a.cpp b,h a.h

gcc –c a.cpp

有重复的效果也是一样的

Makefile自动生成依赖,自动化编译

其中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的修改,都有可能导致目标文件的依赖关系发生变化,必须重新生成依赖文件,解决了上一版的问题。

Makefile自动生成依赖,自动化编译



    到这里基本的意义弄明白了,但是让我不解的是%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的文件首先当成一个目标,然后要去寻找其依赖文件和规则的,而不是我事先想象的简单的把其他文件的内容包含过来。
    到此,问题解决,基本达到预期。

linux

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/1bd469904afe66bc5805f6dc554a61ec.html