make是一个工具,可以自动化编译一个大型的项目[一个大型的项目往往包含,几十几百甚至上千个源文件,手动编译肯定是不现实的],一旦写好makefile文件,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。其中make也有很多发行版本[就像Linux也有ubuntu,mint等众多发行版本],GNU Make是目前使用最为广泛的发行版本。

一般来说,无论是C还是C++,首先要把源文件编译成中间代码文件,在Windows下也就是.obj文件,UNIX下是.o文件,即 Object File,这个动作叫做编译[compile]。然后再把大量的Object File合成执行文件,这个动作叫作链接[link]。

编译时,编译器要求语法正确,函数与变量的声明正确。对于后者,通常是你需要告诉编译器头文件的所在位置[头文件中应该只是声明,而定义应该放在.c/.cpp文件中],只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件[.o文件]。

链接时,主要是链接函数和全局变量。所以,我们可以使用这些中间目标文件来链接我们的应用程序。链接器并不管函数在哪个源文件中,只管函数的中间目标文件,在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫库文件[Library File],也就是.lib文件。在UNIX下,是Archive File,也就是.a文件。

总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File[.o文件]。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码[Linker Error]

Makefile Intro

makefile中的主要语法其实就是:

target ... : prerequisites ...
[Tab] command

规则中target可以是空格分开的多个文件名,也可以是一个标签[比如clean]。prerequisites就是要生成那个target所需要的文件或是目标。command也make需要执行的命令[任意的Shell命令]。

这本质上是一个文件的依赖关系。也就是说,target依赖于prerequisites中的文件,target的生成规则定义在command中。prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。

比如我有两个x.cy.c文件和他们对应的x.hy.h文件,然后对应的makefile可以这么写

sample:x.o y.o
    gcc x.o y.o -o sample

x.o:x.c x.h
    gcc -c x.c

y.o:y.c y.h
    gcc -c y.c

clean:
    rm sample x.o y.o

gcc syntax一般是:gcc [options] [source files] [object files] [-o output file]

然后在对应目录下直接输入命令make就可以生成执行文件sample。在这个makefile中,target包含:执行文件sample和中间目标文件[*.o],依赖文件[prerequisites]就是冒号后面的那些文件。每一个.o文件都有一组依赖文件,而这些 .o文件又是target文件sample的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。

在定义好依赖关系后,后续的那一行定义了如何生成对应的target[在这个例子中就是sample和.o文件]的命令,这一行一定要以一个Tab键作为开头,否则无法识别。make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令,否则就不会执行对应命令因为target已经生成。

这里要说明一点的是,clean不是一个文件,它是一个动作名字,其冒号后什么也没有。那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个动作的名字[也就是命令make clean]。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

Makefile two-phase

Make有两个工作阶段。第一阶段:读取所有的makefile文件[包括”MAKIFILES”变量指定的、指示符”include”指定的、以及命令行选项”-f(–file)”指定的makefile文件],内建所有的变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表。在第二阶段:根据第一阶段已经建立的依赖关系结构链表决定哪些目标需要更新,并使用对应的规则来重建这些目标。在 make 执行的第一阶段中如果变量和函数被展开,那么称此展开是立即的[immediate],此时所有的变量和函数被展开在需要构建的结构链表的对应规则中[此规则在建立链表是需要使用]。其他的展开称之为延后的[deferred]。这些变量和函数不会被立即展开,而是直到后续某些规则须要使用时或者在make处理的第二阶段它们才会被展开。

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

在默认的方式下,我们只输入make命令。

  1. make会在当前目录下顺序寻找GNUmakefile, makefile and Makefile文件[建议使用Makefile这个名字]。
  2. 如果找到,它会找文件中的第一个target文件,在上面的例子中,他会找到sample这个文件,并把这个文件作为最终target文件。
  3. 如果sample文件不存在,或是sample所依赖的.o文件的文件修改时间要比sample这个文件新,那么,他就会执行后面所定义的命令来生成sample这个文件。
  4. 如果sample所依赖的.o文件也不存在,那么make会在当前makefile中找target为.o文件的依赖关系,如果找到则再根据那一个规则生成.o文件。
  5. 默认.c.h文件是存在的,于是make会生成对应的.o文件,然后再用.o文件生成make的终极任务,也就是sample文件的。

Writing Rule

Makefile中,规则描述了在何种情况下使用什么命令来重建一个特定的文件,此文件被称为规则目标[target,通常规则中的target只有一个]。规则中除target之外的罗列的其它文件称为target的依赖[prerequisites],而规则的命令是用来更新或者创建此规则的目标。

规则的顺序在makefile文件中没有意义,除了makefile的终极目标所在的规则[The default goal is the target of the first rule in the first makefile. If the first rule has multiple targets, only the first target is taken as the default. not targets whose names start with “.”]。default goal就是当没有使用make命令行指定具体目标时,make默认的更新的哪一个目标[即只输入make的时候,更新的target]。default goal是执行 make 的唯一目的,其所在的规则作为第一个被执行的规则。而其它的规则是在完成重建“终极目标”的过程中被连带出来的。所以这些目标所在规则在Makefile中的顺序无关紧要。

一个规则的常规依赖[通常是多个依赖文件]表明了两件事。首先,它决定了重建此规则目标所要执行规则[确切的说是执行命令]的顺序。对于这样的规则,A:B C,那么在重建目标A之前,首先需要完成对它的依赖文件B和C的重建。重建B和C的过程就是执行Makefile中以文件B和C为目标的规则。其次,它确定了一个依存关系。规则中如果依赖文件比target新,则认为规则的target已经过期而需要重建target。通常,如果规则中依赖文件中的任何一个被更新,则规则的目标相应地也应该被更新。order-only prerequisites 举例:

LIBS = libtest.a
foo : foo.c | $(LIBS)
    $(CC) $(CFLAGS) $< -o $@ $(LIBS) 
**” “符号后面的依赖文件$(LIBS),只有在目标文件不存在的情况下,才会参与规则的执行。当目标文件存在时此依赖不会参与规则的执行过程。**

Makefile中通配符的用法和含义和Linux[unix]的shell完全相同。


文中部分内存容来自GNU Make Book和https://blog.csdn.net/liang13664759/article/details/1771246 特此声明。