makefile⽂件中的依赖关系理解
⾸先,假设当前⼯程⽬录为prj/,该⽬录下有6个⽂件,分别是:main.c、abc.c、xyz.c、abc.h、xyz.h和Makefile。其中main.c包含头⽂件abc.h和xyz.h,abc.c包含头⽂件abc.h,xyz.c包含头⽂件xyz.h,⽽abc.h⼜包含了xyz.h。它们的依赖关系如图。
第⼀次使⽤Makefile应该写成这个样⼦(假设⽣成⽬标main):
1. main:main.o abc.o xyz.o
2. gcc main.o abc.o xyz.o -o main
3. main.o:main.c abc.h xyz.h
4. gcc -c main.c –o main.o -g
5. abc.o:abc.c abc.h xyz.h
6. gcc -c abc.c –o abc.o -g
marktwain
7. xyz.o:xyz.c xyz.h
8. gcc -c xyz.c -o xyz.o -g
9. clean:
10. rm main main.o abc.o xyz.o -f
虽然这样Makefile完全符合Makefile的书写规则,但是当代码⽂件再增加⼏倍后,再管理这些命令将会是⼀个噩梦因此Makefile提供了默认规则和⾃动推导帮我们完成⼀些常⽤功能。然后,我们将Makefile修改如下:
1. EXE=main
2. CC=gcc
3. OBJ=main.o abc.o xyz.o
英语四级听力真题下载4. CFLAGS=-g
5. $(EXE):$(OBJ)
6. $(CC) $^ -o $@
mistake可数吗7. clean:
8. rm $(EXE) $(OBJ) -f
学习意大利语
变量EXE,CC,OBJ分别代指⽬标程序名,编译器名,⽬标⽂件名。CFLAGS是C编译选项,它会附
加在每条编译命令(gcc -c)之后。$(EXE)是对变量的引⽤,$^代指所有的依赖项——即$(OBJ),$@代指⽬标项——即$(EXE)。
directions是什么意思该命令等价于:$(CC) $(OBJ) -o $(EXE)。
这个Makefile只有⽬标⽂件链接的命令,源⽂件的编译命令都被忽略了!这正是Makefile的⾃动推导功能——它可以将⽬标⽂件⾃动依赖于同名的源⽂件,即:
1. main.o:main.c
2. gcc -c main.c -o main.o
3. abc.o:abc.c
4. gcc -c abc.c -o abc.o
5. xyz.o:xyz.c
6. gcc -c xyz.c -o xyz.o
按照上述⽅式,只要⼯程下增加了源⽂件后,只需要在OBJ初始化处增加⼀个*.o即可。但是这种⽅式
是有问题的,Makefile的⾃动推导功能只会推导出⽬标⽂件对源⽂件的依赖关系,⽽不会在依赖关系中添加头⽂件!这导致的直接问题就是:当第⼀次执⾏make后,再次修改依赖的abc.h、xyz.h头⽂件的内容,⾃动推导功能只会去检测.c⽂件的修改时间戳,发现没有变化则不会再次编译⽣成main.o、abc.o、xyz.o⽂件,进⼀步导致不会进⾏重新make链接⽣成⽬标⽂件(因为检测到main.o、abc.o、xyz.o⽂件没有变化)!除⾮修改头⽂件后运⾏⼀次make clean,再运⾏make.
为了能让make⾃动包含头⽂件的依赖关系,gcc为我们提供了⼀个编译选项(gcc -M,对于g++是-MM),能输出⽬标⽂件的依赖关系!⽐如执⾏:
gcc -M main.c
会终端显⽰:
main.o:main.c abc.h xyz.h
注意:如果是不在当前路径下的头⽂件会显⽰出全路径!
如果将每个源⽂件的依赖关系包含到Makefile⾥,就可以使得⽬标⽂件⾃动依赖于头⽂件了!再次修改原先的Makefile:
1. EXE=main
2. CC=gcc
3. SRC=$(wildcard *.c)
4. OBJ=$(SRC:.c=.o)
5. CFLAGS=-g
6. all:depend $(EXE)
7. depend:
8. @(CC)−MM(SRC) > .depend
9. -include .depend
10. $(EXE):$(OBJ)
11. $(CC) $(OBJ) -o $(EXE)
12. clean:
13. @rm $(EXE) $(OBJ) .depend -f
创建了⼀个伪⽬标:all,它依赖于⽬标depend和实际的⽬标EXE。⽽depend正是将所有源⽂件对应的⽬标⽂件的依赖关系输⼊到.depend ⽂件,并被包含在Makefile内!这⾥有⼏个细节需要说明:
1..depend⽂件是隐藏⽂件,避免和⼯程的⽂件混淆。
2.include命令之前增加符号'-',避免第⼀次make时由于.depend⽂件不存在报告错误信息。
3.SRC初始化为wildcard *.c表⽰当前⽬录下的所有.c源⽂件,这就省去了我们⼿动输⼊新增的源⽂件。
4.OBJ初始化为SRC:.c=.o,表⽰将SRC中所有.c结尾的⽂件名替换为.o结尾的,这样就⾃动⽣成了源⽂件的⽬标⽂件序列。
5.clean的rm命令钱@符号表⽰执⾏该命令时不回显命令。
这样,每次执⾏make时都会重新计算⽬标⽂件的依赖关系,并输出到.depend⽂件,然后包含到Makefile后进⾏编译⼯作,这样⽬标⽂件的依赖关系就不会出错了!⽽我们得到了⼀个能⾃动包含源⽂件和识别头⽂件依赖关系的Makefile,将该⽂件应⽤于任何单⽬录的
waltdisneyC/C++⼯程(C++需要修改部分细节,不作赘述)都能正常⼯作。
betfair但是,这种⽅式也有⼀定的不⾜,当头⽂件的依赖关系不发⽣变化时,每次make也会重新⽣成.depend⽂件。如果这样使得⼯程的编译变得不尽⼈意,那么我们可以尝试将依赖⽂件拆分,使得每个源⽂件独⽴拥有⼀个依赖⽂件,这样每次make时变化的只是⼀⼩部分⽂件的依赖关系。
1. EXE=main
2. CC=gcc
3. SRC=$(wildcard *.c)
pqi4. OBJ=$(SRC:.c=.o)
5. DEP=(patsubst(SRC))
6. CFLAGS=-g
7. $(EXE):$(OBJ)
8. $(CC) $^ -o $@
9. $(DEP):.%.d:%.c
10. @t -e;
11. rm -f $@;
12. $(CC) −M $< > @.$$$;
13. d 's,$∗\.o[ :]*,\1.o $@ : ,g' < $@.
14. > $@;
15. rm -f @.$$$
16. -include $(DEP)
17. clean:
18. @rm $(EXE) $(OBJ) $(DEP) -f
注意,上⾯10-15⾏其实是⼀条命令,make只会创建⼀个Shell进程执⾏这条命令,这条命令分为5个
⼦命令,⽤;号隔开。执⾏步骤为:1)t-e命令设置当前Shell进程为这样的状态:如果它执⾏的任何⼀条命令的退出状态⾮零则⽴刻终⽌,不再执⾏后续命令。@表⽰makefile执⾏这条命令时不显⽰出来
2)把原来的.d⽂件删掉。
3)$<;依赖的⽬标集(即*.c), -M表⽰⽣成⽂件依赖关系, $@表⽰⽣成的⽬标⽂件(即*.d),$$表⽰本⾝的ProcessID。注意,在Makefile中$有特殊含义,如果要表⽰它的字⾯意思则需要写两个$,所以Makefile中的四个$传给Shell变成两个$,两个$在Shell中表⽰当前进程的id,⼀般⽤它给临时⽂件起名,以保证⽂件名唯⼀。麦考瑞大学
4)这个d命令⽐较复杂,就不细讲了,主要作⽤是查找替换,并加⼊.d的依赖关系。
5)最后把临时⽂件删掉。
该Makefile增加了⼀个变量DEP,初始化为patsubst %.c,.%.d,$(SRC),表⽰将SRC中的以*.c结尾的源⽂件名替换为.*.d的形式,⽐如main.c对应着⽂件.main.d,这就是main.c的依赖关系⽂件,且是隐藏的。
为了⽣成每个源⽂件的依赖⽂件,建⽴了⽬标依赖关系$(DEP):.%.d:%.c,该关系表⽰,对于⽬标DE
P,通过$@可以访问⼀个依赖⽂件,通过$>则访问对应的同名源⽂件。命令部分使⽤连接,表⽰当前命令作为⼀个整体在⼀个进程内执⾏。该组命令的含义是:将gcc -M⽣成的信息输出到⼀个临时⽂件,然后在:之前加上当前的⽂件名输出到依赖⽂件。⽐如对于main.c⽣成的临时⽂件信息为:
main.o:main.c abc.h xyz.h
处理后依赖⽂件信息是:
girlfriendmain.o .main.d:main.c abc.h xyz.h
这样的依赖关系表⽰main.o和它的依赖关系⽂件的依赖项是⼀致的,只要相关的源⽂件或头⽂件发⽣了改变,才会重新⽣成⽬标⽂件和依赖关系⽂件,也就达到了依赖关系⽂件单独更新的⽬的了。
虽然如此,但是这样的Makefile也不是完美的。现假设⼯程⽬录内新增⼀个源⽂件lmn.c,按照Makefile的指令make后会产⽣.lmn.d依赖关系⽂件。⽽如果我们再删除lmn.c源⽂件后,重新make后.lmn.d依然存在!尤其是当重复增删很多源⽂件后,⼯程⽬录下可能会存在很多⽆⽤的依赖⽂件,当然这些问题可以通过make clean解决。
通过前边的讨论,我们得到⼀个能在单⽬录⼯程下⼯作的通⽤Makefile,⾄于是实现为单独⼀个依赖⽂件的形式,还是每个源⽂件产⽣⼀个独⽴的依赖⽂件,要根据程序作者⾃⼰的喜恶来选择。虽然每
种⽅法都有⼀些细微的瑕疵,但是不影响这个通⽤的Makefile的实⽤性,试想⼀下在⼯程⽬录下拷贝⼀份当前的Makefile,稍加修改便可以正确的编译开发,⼀定会令⼈⼼情⼤好。