第十三篇 自动生成依赖关系(下)
上一篇中我们初步生成了.dep依赖文件,但是,实际工程中,源文件成千上万,生成的依赖文件需要用文件夹管理起来,也就是在makefile中将.dep文件组织到指定目录。
我们可以通过以下思路来组织.dep文件,当include发现.dep文件不存在时:
1、通过规则和命令创建deps文件夹。
2、将所有.dep文件创建到deps文件夹。
3、.dep文件中记录目标文件的依赖关系。
初步想法如下,让deps文件夹作为all目标的依赖,执行make all时创建deps文件夹。代码如下:
执行make all,输出如下所示:
由于make解释器对include的处理方式,导致在输出结果中,先创建了依赖文件,然后才创建了deps文件夹,这是不符合我们的预期的,可见,让deps作为目标all的依赖不是一个好的方案。我们应该在用到deps文件夹的时候才让它生成。
修改makefile如下:
此时,deps文件夹作为.dep文件的依赖,因此,在需要生成.dep依赖文件时才生成deps文件夹,执行make all,输出结果如下:
从上图可见,这次是先创建了deps文件夹,然后又创建的.dep依赖文件。
但同时一个新问题也暴漏了出来,main.dep竟然被创建了两次,这究竟是怎么回事呢?
make解释器在解析makefile时,根据$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c这条模式规则生成具体的规则并记录下来,对于main来说就是deps/main.dep : deps main.c,make解释器记录下该规则后便去执行规则下的命令,生成依赖文件,接着再去推倒出func相关的规则并执行命令。当生成deps/func.dep时,deps文件夹的时间戳也被更新了,因此,make解释器根据记录下来的规则发现,应该重新生成main.dep,这就是为什么执行结果中出现了创建两次main.dep的现象。这是一个严重的问题,必须要解决掉,否则,当存在大量源文件要生成依赖文件时,容易造成死循环,导致崩溃。
引入条件语句,修改makefile,执行make all结果如下:
make解释器在第一次解析makefile时,发现deps文件夹不存在,这时解析出的规则为deps/main.dep : deps main.c,并记录下来。当生成deps/func.dep时同样也会更新deps文件夹的时间戳,此时会触发make再次解析刚刚记录的规则,而这时deps文件夹是存在的,因此会用新规则deps/main.dep : main.c替换刚刚的规则,这时不再依赖deps文件夹,而main.c又未曾改变,所以就不会再次创建main.dep了。
这就是使用ifeq来动态决定.dep的依赖:
上述makefile中使用了include $(DEPS),但是,当DEPS不存在时,make会寻找相应的目标和规则,在规则中可能会创建一个DEPS文件,但是创建好之后make是否还会进行一些操作呢?这就关系到make解释器对include关键字的处理方式了。
include的不为人知的知识点:
1、include前使用"-"号,不但会关闭警告,而且会关闭错误报告。
上图中程序中,即使当前目录不存在test.txt文件,执行make all,也会正常输出this is all,这是因为include前使用了"-"。
2、如果include触发规则创建了文件,那么make会将新创建出的文件重新包含进来。
看下面一段程序:
执行make后输出如下:
乍一看输出结果可能有点儿出乎意料,为什么不是this is all? 其实make一开始发现test.txt并不存在,于是开始寻找以test.txt作为目标的规则,找到之后便执行这个规则下的命令,这些命令创建了一个test.txt文件,于是make再次将这个文件的内容包含到调用include的地方,在本例中也就作为了第一个规则,make默认情况下,执行第一个规则,所以输出this is other。
3、如果include包含的文件存在,make还是会去检查是否存在以文件名作为目标的规则,若存在,且该规则的依赖在时间上较新,则会进一步去执行这个规则。
编写makefile如下:
执行make all,输出如下:
我们保证b.txt文件是最新的,从执行结果来看,虽然当前目录下存在test.txt,但是依然触发了test.txt : b.txt这条规则的执行。
关于include的总结: