Linux基础学习笔记十一:Makefile依赖关系理解

一、前言

最近在看makefile,各文件之间的依赖关系整的我有点懵,看了好久还是迷迷糊糊,可能是我看的资料比较少。反正下面就通过一个例子逐渐引出自己的理解,完全是自己瞎琢磨,有错误欢迎指正!!!

一个简单的例子:4个.c文件,3个.h文件,其中mytest调用了test1,test2,test3;test1调用了test2,test3;test2调用了test3。
在这里插入图片描述
4个.c文件如下:
在这里插入图片描述
3个.h文件如下:
在这里插入图片描述

二、Makefile写法

1、最常规的写法:

mytest:  mytest.o test1.o test2.o test3.o
	gcc -o mytest mytest.o test1.o test2.o test3.o
mytest.o: mytest.c mytest.h test1.h test2.h test3.h
	gcc -c mytest.c
test1.o: test1.c test1.h test2.h test3.h
	gcc -c test1.c
test2.o: test2.c test2.h test3.h
	gcc -c test2.c
test3.o: test3.c test3.h
	gcc -c test3.c


.PHONY:clean
clean:
	-rm -rf *.o mytest

这种写法没有可说的,就是把每个目标所需的依赖都给写出来了,包括.h依赖文件。

2、无.h依赖文件的写法:

mytest:  mytest.o test1.o test2.o test3.o
	gcc -o mytest mytest.o test1.o test2.o test3.o
mytest.o: mytest.c 
	gcc -c mytest.c
test1.o: test1.c 
	gcc -c test1.c
test2.o: test2.c 
	gcc -c test2.c
test3.o: test3.c
	gcc -c test3.c

这种写法与第一种写法的区别就在于没有为每个目标添加.h依赖文件。

3、自动推导(简化)版本:

mytest:  mytest.o test1.o test2.o test3.o
	gcc -o mytest mytest.o test1.o test2.o test3.o

mytest.o:  mytest.c
	gcc -c mytest.c
test1.o: test1.c 

test2.o: 
	gcc -c test2.c
test3.o: 

由以上可知,可以省略.c依赖文件、指令,或者两者都省略。

由下面终极自动推导的例子可以看出,上面四句甚至可以直接不写!!!

mytest:  mytest.o test1.o test2.o test3.o
	gcc -o mytest mytest.o test1.o test2.o test3.o

三、问题分析

1、没有.h依赖文件为什么会编译成功?

我本来以为在第一种写法中,.h依赖文件刚好作为连接各个文件的桥梁,源文件中使用的各种变量或者函数才能被正确找到,这样整个工程才能编译成功。可是现在看来,没有.h依赖文件也可以编译成功。为什么呢?

我个人的理解是,在编译时,以mytest.c源文件为例,其中有如下头文件包含语句:

#include "test1.h" 
#include "test2.h" 
#include "test3.h" 

这些头文件包含语句,就已经可以建立起各个文件之间的调用关系了,不用在makefile中指明就可以编译成功了,注意这里说的仅仅是编译成功!!!

2、自动推导版本中,修改某个文件,再次make,哪些目标会被重新生成?

  • 先修改test3.c源文件进行测试:

在这里插入图片描述
由上图可以看到,第一次make时,一共执行了五条指令,这是因为一开始这些目标都不存在,所以需要生成这5个目标文件。

这时候,我修改了test3.c源文件,在该文件最后加了一句:

 printf("modified\n");

此时再次make(注意,在此之前,没有make clean),从下图可以看到这次只执行了两条指令,也很好理解,因为test3.c被修改了,所以test3.o要重新生成,则mytest也要重新生成。
在这里插入图片描述
从上面的运行结果可以看出,虽然只重新生成了test3.o,而test1.o和test2.o、mytest.o都没有重新生成,但是最后所有源文件的执行结果都多了添加的modified,也即test3.c的修改对它们都生效了。这是为什么呢?明明只重新生成了test3.o,然后重新生成应用程序mytest,其他文件没有被修改也没有被重新生成。

我个人理解是,比如test1.c调用了test3.c中的函数,其实并不是它把被调用函数的代码复制过来了(这种情况下,修改了test3.ctest1.c的功能不会受影响),而是可以认为它获取了被调用函数的指针,这样在执行到调用的函数时,调用的仍然是修改后的函数!!!

  • 再修改test3.h头文件进行测试:

在这里插入图片描述
可以看到,此时显示mytest为最新,没有重新生成任何目标文件。

之所以发生这种情况,是因为Makefile的自动推导功能只会推导出目标文件对源文件的依赖关系(所以,自动推导版本的makefile补全后,其实就是无.h依赖文件的写法),而不会在依赖关系中添加头文件!这导致的直接问题就是:当第一次执行make后,再次修改依赖的.h头文件的内容,自动推导功能只会去检测.c文件的修改时间戳,发现没有变化则不会再次编译生成.o文件,进一步导致不会进行重新make链接生成mytest目标文件(因为检测到4个.o文件都没有变化)!!!除非修改头文件后运行一次make clean,再运行make

四、小结

1、makefile中的依赖关系,不是为了使编译通过,更多的是为了实现当修改某个文件时,与其相关的文件也能被重新编译,避免出现修改无效的情况!!!

2、自动推导只会推导出目标文件对源文件的依赖关系,而不会在依赖关系中添加头文件!!!

因此,如果你在项目中只涉及到修改源文件,那么可以不用添加头文件依赖;但是如果你还需要修改.h头文件,那么在makefile中一定要将.h依赖文件根据实际情况添加上!!!

posted @ 2021-04-29 14:18  耐心的小黑  阅读(236)  评论(0编辑  收藏  举报