前面我们涉及到的makefile,都只是考虑到目标文件(.o)依赖于源文件(.c)。然而实际情况却并没有这么简单,我们的源文件一般都是会包含一些自己编写的头文件的,这样的话%.o : %.c这种模式规则的写法是不是就有问题了呢?当源文件不改变而头文件改变时,make解释器是无法根据文件的新旧关系来决定重新编译的,即改变头文件,make不会重新编译。
 
 
  编写func.h func.c main.c三个文件的内容分别如下:
 
 
makefile程序以及以上三个文件的依赖关系如下:
 
 执行make,可以得到正确的可执行文件,修改func.h中的宏定义,将HELLO宏改为"Hello Makefile",此时,再次执行make,提示文件是最新的,如下所示:
 
make解释器根据文件新旧关系判断出并不需要重新编译,但是,这并不是我们想要的结果,那么,有什么好的解决方案吗?想必大家首先想到的就是将func.h加入到依赖关系中去,这样头文件的改变,make就能聪明的感知到。
修改makefile如下:
 

上图中将func.h加入到了依赖关系中,同时将$^改为$<,因为gcc编译生成目标文件时-c选项只能对应一个文件,而且gcc在编译时是不考虑.h头文件的。

  执行make,输出如下,可见得到了正确的可执行文件:

 
  下面我们来考虑一下这种解决方案存在的问题:
  1、头文件作为依赖条件出现在每个目标对应的规则中(即使这个目标文件与该头文件没有任何关系)。
  2、当头文件改动,任何源文件都将被重新编译(编译低效)。
  3、当项目中头文件数量巨大时,makefile将很难维护。
 
有没有什么更好的解决方案呢,我们的一个疯狂想法是:
  1、通过命令自动生成对头文件的依赖。
  2、将生成的依赖自动包含进makefile中。
  3、当头文件改动后,自动确认需要重新编译的文件。
 
  为了完成这个疯狂的想法,我们先来进行一些预备知识的学习。
Linux的sed命令,其具体表述如下:
 
sed是一个流编辑器,可以对输入输出流进行编辑,我们只用到了字符串替换这一项功能,替换方式为   sed 's:src:des:g',其中,s和g是固定格式,src为待替换的字符串,des为替换后的字符串。
演示结果如下:
 
sed还支持正则表达式,如下所示:
演示结果如下:
 
  \(.*\)\.o[ :]*匹配以.o结尾的文件名,包括它的路径结构,后边的[ :]表示文件名后有任意空格或者冒号(:)也可以进行匹配。替换部分则表示在匹配到的文件名前加上前缀objs/。
 
Linux中的gcc关键编译选项,具体描述如下:
 
演示如下:
 
-M选项列出目标文件所有的依赖,包括系统提供的头文件。而-MM选项只列出一些简单依赖,包括我们自己编写的一些文件。使用gcc -MM -E func.c还可以提高效率。
 
下面我们将gcc的这个特殊选项和sed命令结合在一起,看一下输出结果:
 
下面介绍一个小技巧:拆分目标的依赖
 
makefile和执行结果如下所示:
 
   可见,目标的完整依赖可以拆分为部分依赖,而不会改变最终的依赖关系(即拆分后test依旧是依赖a b c这三个伪目标),也不会影响最终行为。依赖中如果存在伪目标,那么伪目标对应的规则一定会被执行,如果依赖中存在伪目标但是该伪目标并没有对应的规则,则默认认为该伪目标已经存在,即依赖条件(伪目标a b c)默认满足。
   将目标的完整依赖拆分为多个部分依赖,则可以将同一个目标的依赖写在多个地方,甚至是写在不同的文件中。
  有了以上的预备知识,我们离上述提到的三个疯狂目标更近了一步,更深入的知识将在下一篇讲解。
 
 
参考如下:
   狄泰软件教程及课件
   gun make手册
   专业嵌入式软件开发
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted on 2018-02-06 11:17  周伯通789  阅读(637)  评论(0编辑  收藏  举报