Makefile难点记录

Makefile难点记录

Makefile是linux中自动化编译工具make的描述脚本,makefile文件通过目标、条件和动作三个概念完成对源文件和目标文件的依赖描述。Makefile不仅仅只能用于自动化编译,还可以用于处理所有与依赖和时间有关的操作。虽然现在有了cmake,ninja等等更加抽象化和跨平台的工具,但是这些大体上都是建立在make基础之上。掌握makefile还是有着一定的意义。

make的执行过程

理解make的执行过程对于我们理解它的符号扩展原则有着很重要的作用。make的总体执行过程如下:

  1. 读入当前目录Makefile文件。
  2. 读入被include的其它Makefile。
  3. 初始化文件中的变量。
  4. 推导隐晦规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。

其中1-5步为第一个阶段:读取内容阶段。6-7为第二个阶段,目标更新阶段。make执行的过程和主要原理是比较容易理解的。其中最为复杂和深奥的就是由于make对变量和模式的支持所牵扯到的扩展的问题。这其实是任何一个牵扯到模式和支持其嵌套的语言都会遇到问题。

make提供的便捷性特征

在欣赏make提供的便捷性特征之前,我们先看一段普通的未使用任何便捷特征的makefile文件。

Makefile难点记录

在这个简单的项目基础上,我们可以明显的发现,如果项目的源文件开始增加并且包含的头文件也增加,那么编写makefile文件的工作量将会大大增加。很显然make不得不引入和多特征来简化我们的工作。相关特性如下:

  1. 变量,我们可以将大量的目标文件定义在一个变量中,然后在后续很多地方进行引用。
  2. 隐式规则,很多时候我们都是将 *.c 编译为 *.o,make可以通过文件名自动进行这些编译,而不需要我们手动的写出。
  3. 在规则中可以使用通配符,即*(匹配0个或任意个字符)和?(匹配任意一个字符)。
  4. 多目标规则,即可以多个目标拥有一个相同的依赖。
  5. 静态模式规则,即由目标文件名称确定前置条件名称。

make的扩展解析

make的扩展问题主要处理的是变量的展开时间点。make对变量展开分为两类,即立即展开和推迟展开。立即展开发生在第一阶段,当make解析到变量时就做值的替换。推迟展开指在make解析到变量时,并不立刻做值的替换,而是等到后面遇到立即展开或者推迟到第二阶段。在此之外,make还附加了第二次展开的概念。第二次展开是相对于第一次展开而言,第二次展开一般发生在make执行的读取阶段和更新阶段之间。

对于变量的立即展开和推迟展开,make分别给出了如下规定:

immediate = deferred
immediate ?= deferred
immediate := immediate
immediate ::= immediate
immediate += deferred or immediate
immediate != immediate

对于第二展开,可以通过下面一小段代码说明:

Makefile难点记录

代码的执行结果如下: Makefile难点记录

上述示例中,在第一次展开时debug1对应$(var)结果为top,debug2对应$$(var)结果为$(var),接着在第二次展开的时候debug对应$(var)结果为bottom,不是top的原因是因为后面的再次赋值为bottom,而这也恰恰表明了第二次展开的存在。

make目标和规则中的模式使用

正如前面所说的,在通常情况下随着项目文件的增加,makefile文件会膨胀,增加开发人员的管理工作。通过在目标和规则中使用模式,make可以大大的减少开发人员的重复工作。make中支持的通配符也就是shell所支持的那些。make可以在规则中单独使用通配符,也可以目标和同时使用(即静态模式)。如果想要在目标中使用通配符的话,则需要使用make提供的wildcard函数来实现,不能在目标中单独使用通配符的原因很简单,目标在解析的时候应该是明确的或者可以通过上下文环境计算出来,否则后续的分析工作将难以进行。


对于makefile的难点,后续将会继续补充。

参考资料

GNU Make参考手册
跟我一起写Makefile

转载于:https://my.oschina.net/taodf/blog/2994963