1.1 手动编译
1.2 文件编译依赖树
<1>. 没有makefile的日子
1.1 程序是如何编译的?
如果存在这么一个工程目录结构:
如果想要编译上面的工程,在没有使用makefile的情况下,可能需要使用如下的编译命令:
通过上面的命令生成nomakefile工程编译完成的文件。那么可能比较懒的程序员就在想这个很类似于一个批处理的过程,那么能不能将整个过程编写成脚本的形式,减少命令的输入,makefile就是用来将手动编译自动化的一个工具。
1.2 文件编译依赖树
查看上面的手动编译流程的话,为了生成nomakefile文件,需要存在foo.o文件和main.o文件,如果想要生成foo.o文件,需要编译foo.c,为了生成main.o,那么需要编译main.c文件,如此即生成一个所谓的文件编译依赖树:
通过手动编译文件的话,显然是将文件全部重新编译,在makefile中如果说依赖的文件没有更新的话,那么目标文件是不会重新编译的。
<2>. 快速体验makefile
还是上面例子如果使用makefile来管理,事情会变得如此简单,首先编写makefile文件:
# makefile comment
.
查看上面的makefile,可以看出:
1. makefile中可以使用#添加行注释
2. makefile文件笼统的将是一个个规则的集合,每个规则是由目标开始,然后是该目标依赖的文件,下一行以tab键开始,然后是生成目标所需的命令。
目标:依赖文件,以空格隔开
生成目标文件的命令
3. makefile在执行默认会将第一个目标做为最终目标,在这个文件就是hellomakefile,make在执行时将自动生成“最终目标”所依赖的文件,也就是main.o和foo.o文件。
4. makefile中可以定义变量(或者称之为宏),在程序的其他部分中使用,这里也就是变量(宏) OBJECTS。
5. makefile中可以使用内建函数,例如上面中的@echo函数。
<3>. makefile变量
makefile中用户可以自定义变量,例如:OBJECTS=foo.o main.o,那么在下面的makefile中可以使用${OBJECTS}代替该变量定义的值(foo.o,main.o)。另外可以使用默认变量:
$@:表明的是目标文件名,例如在foo.o:foo.c foo.h中$@表示的就是foo.o
$<:表明的是目标所依赖的文件的第一个文件,例如在 foo.o:foo.c foo.h表明的是就是foo.c文件。
$?:表明的是所有比目标文件更新的文件的集合,其中使用空格个风格。对于 foo.o:foo.c foo.h,如果更新了foo.c文件,那么重新make时,将$?将显示foo.c。这里需要注意的是如果更新了foo.h文件,那么$?将显示foo.c和foo.h文件,主要原因是在foo.c文件中包含了foo.h文件。
<4>. 条件编译
makefile中可以使用类似于编程语言中的跳转命令,如果满足一定的条件,将执行一定的动作。常见的条件编译指令:
ifeq(arg1, arg2) .. endif:如果arg1和arg2相等的话,将执行该区域中的内容。
ifneq(arg1, arg2) .. endif:如果arg1和arg2不相等的话,将执行该区域内的内容。
ifdef ARG .. endif:判定ARG变量是否已经定义
ifndef ARG .. endif:和ifdef相反
上面的语句可以和else一起来使用:
ifneq ("same", "diff")
<5>. makefile函数
makefile中可以使用内建函数,makefile中的内建函数大多是和字符串相关和目录相关函数。makefile中函数使用$(function-name function-arg),例如$(strip($(STR))),makefile中内建函数大多是和字符串相关的函数,常用函数:strip,finstring等,这些函数和高级语言中的string类的方法是极为类似的,具体可以参考[这里]。
<6>. 参考资料