频道栏目
首页 > 程序开发 > 软件开发 > 其他 > 正文
(01)Makefile简介
2019-12-16 09:07:38         来源:chicken_wing_is_good的博客  
收藏   我要投稿

注:根据徐海兵翻译整理的《GNU make中文手册》进行的再次归纳精简。

01_1.什么是make及makefile?以及make的作用。

Linux(unix)环境下使用 GNU 的 make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为 Makefile 文件的编写。此文件正是 make 正常工作的基础。所要完成的 Makefile 文件描述了整个工程的编译、连接等规则。

make 是一个命令工具,它解释 Makefile 中的指令(应该说是规则)。通常,make工具主要被用来进行工程编译和程序链接。

在 Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。Makefile 有自己的书写格式、关键字、函数。像 C 语言有自己的格式、关键字和函数一样。而且在 Makefile 中可以使用系统 shell 所提供的任何命令来完成想要的工作。


01_2.make工具。

当使用make工具进行编译时,工程中以下几种文件在执行make时将会被编译 (重新编译):
1. 所有的源文件没有被编译过,则对各个 C 源文件进行编译并进行链接,生成最后的可执行程序;
2. 每一个在上次执行make之后修改过的C源代码文件在本次执行 make时将会被重新编译;
3. 头文件在上一次执行 make 之后被修改。则所有包含此头文件的 C 源文件在本次执行make 时将会被重新编译。


01_3.makefile规则介绍

一个简单的 Makefile 描述规则组成:
TARGET... : PREREQUISITES... 
	COMMAND 
	... 
	... 


target:规则的目标。通常是最后需要生成的文件名或者为了实现这个目的而必需的中间过程文件名。可以是.o文件、也可以是最后的可执行程序的文件名等。

 

prerequisites:规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依赖于一个或者多个文件。

 

command:规则的命令行。是规则所要执行的动作(任意的 shell 命令或者是可在shell下执行的程序) 。它限定了 make执行这条规则时所需要的动作。

 

一个规则可以有多个命令行,每一条命令占一行。 注意: 每一个命令行必须以[Tab]字符开始,[Tab]字符告诉 make 此行是一个命令行。make按照命令完成相应的动作。这也是书写 Makefile 中容易产生,而且比较隐蔽的错误。
命令就是在任何一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可以没有依赖而只有动作(指定的命令)
make程序根据规则的依赖关系,决定是否执行规则所定义的命令的过程我们称之为执行规则。


01_4.简单的示例。

edit : main.o kbd.o command.o display.o \ 
       insert.o search.o files.o utils.o 
	cc -o edit main.o kbd.o command.o display.o \ 
        insert.o search.o files.o utils.o 
main.o : main.c defs.h 
	cc -c main.c  
kbd.o : kbd.c defs.h command.h 
	cc -c kbd.c 
command.o : command.c defs.h command.h 
	cc -c command.c 
display.o : display.c defs.h buffer.h 
	cc -c display.c 
insert.o : insert.c defs.h buffer.h 
	cc -c insert.c 
search.o : search.c defs.h buffer.h 
	cc -c search.c 
files.o : files.c defs.h buffer.h command.h 
	cc -c files.c 
utils.o : utils.c defs.h 
	cc -c utils.c 
clean : 
	rm edit main.o kbd.o command.o display.o \ 
        insert.o search.o files.o utils.o



书写时,可以将一个较长行使用反斜线(\)来分解为多行,这样可以使我们的Makefile书写清晰、容易阅读理解。但需要注意:反斜线之后不能有空格(这也是大家最容易犯的错误,错误比较隐蔽)

在这个Makefile中,我们的目标(target)就是可执行文件“edit”和那些.o文件(main.o,kbd.o….) ;

依赖(prerequisites)就是冒号后面的那些 .c 文件和 .h文件。所有的.o文件既是依赖(相对于可执行程序edit)又是目标(相对于.c和.h文件)。

命令包括 “cc –c maic.c”、“cc –c kbd.c”……

在描述依赖关系行之下通常就是规则的命令行(存在一些些规则没有命令行) ,命令行定义了规则的动作(如何根据依赖文件来更新目标文件) 。
命令行必需以[Tab]键开始,以和Makefile其他行区别。就是说所有的命令行必需以[Tab] 字符开始,但并不是所有的以[Tab]键出现行都是命令行。 但make程序会把出现在第一条规则之后的所有以[Tab]字符开始的行都作为命令行来处理。 (记住:make程序本身并不关心命令是如何工作的,对目标文件的更新需要你在规则描述中提供正确的命令。“make”程序所做的就是当目标程序需要更新时执行规则所定义的命令)。


目标“clean”不是一个文件,它仅仅代表执行一个动作的标识。正常情况下,不需要执行这个规则所定义的动作,因此目标“clean”没有出现在其它任何规则的依赖列表中。因此在执行make时,它所指定的动作不会被执行。除非在执行make时明确地指定它。而且目标“clean”没有任何依赖文件,它只有一个目的,就是通过这个目标名来执行它所定义的命令。
Makefile中把那些没有任何依赖只有执行动作的目标称为“伪目标” (phony targets)


01_5.make如何工作。

默认的情况下,make执行的是Makefile中的第一个规则,此规则的第一个目标称之为“最终目的”或者“终极目标”。
当在 shell 提示符下输入“make”命令以后。make读取当前目录下的 Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标” ,开始处理第一个规则(终极目标所在的规则) 。在我们的例子中,第一个规则就是目标“edit”所在的规则。规则描述了“edit”的依赖关系,并定义了链接.o文件生成目标“edit”的命令; make在执行这个规则所定义的命令之前,首先处理目标“edit”的所有的依赖文件(例子中的那些.o文件)的更新规则(以这些.o文件为目标的规则)。对这些.o文件为目标的规则处理有下列三种情况:
1. 目标.o文件不存在,使用其描述规则创建它;
2. 目标.o文件存在,目标.o文件所依赖的.c源文件、 .h文件中的任何一个比目标.o文件“更新”(在上一次 make之后被修改)。则根据规则重新编译生成它;
3. 目标.o文件存在,目标.o文件比它的任何一个依赖文件(的.c源文件、.h文件)“更新”(它的依赖文件在上一次 make之后没有被修改),则什么也不做。


这些.o 文件所在的规则之所以会被执行,是因为这些.o 文件出现在“终极目标”的依赖列表中。
在 Makefile 中一个规则的目标如果不是“终极目标”所依赖的(或者“终极目标”的依赖文件所依赖的) ,那么这个规则将不会被执行,除非明确指定执行这个规则(可以通过make的命令行指定重建目标,那么这个目标所在的规则就会被执行,例如 “make clean”)


完成了对.o文件的创建(第一次编译)或者更新之后,make程序将处理终极目标“edit”所在的规则,分为以下三种情况:
1. 目标文件“edit”不存在,则执行规则以创建目标“edit”。
2. 目标文件“edit”存在,其依赖文件中有一个或者多个文件比它“更新” ,则根据规则重新链接生成“edit”。
3. 目标文件“edit”存在,它比它的任何一个依赖文件都“更新”,则什么也不做。


make的工作过程总结:对于一个 Makefile 文件,“make”首先解析终极目标所在的规则(上节例子中的第一个规则),根据其依赖文件(例子中第一个规则的8个.o文件)依次(按照依赖文件列表从左到右的顺序)寻找创建这些依赖文件的规则。
首先为第一个依赖文件(main.o)寻找创建规则,如果第一个依赖文件依赖于其它文件(main.c、defs.h),则同样为这个依赖文件寻找创建规则(创建 main.c和 defs.h的规则,通常源文件和头文件已经存在,也不存在重建它们的规则)……,直到为所有的依赖文件找到合适的创建规则。之后 make 从最后一个规则(上例目标为 main.o 的规则)回退开始执行,最终完成终极目标的第一个依赖文件的创建和更新。
之后对第二个、第三个、第四个……终极目标的依赖文件执行同样的过程(上例的的顺序是“main.o”、“kbd.o”、“command.o”……)。


创建或者更新每一个规则依赖文件的过程都是这样的一个过程(类似于 c 语言中的递归过程) 。对于任意一个规则执行的过程都是按照依赖文件列表顺序,对于规则中的每一个依赖文件,使用同样方式(按照同样的过程)去重建它,在完成对所有依赖文件的重建之后,最后一步才是重建此规则的目标。


更新(或者创建)终极目标的过程中,如果任何一个规则执行出现错误 make就立即报错并退出。整个过程make只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是否正确,make不做任何错误检查。


01_6.指定变量。

终极目标“edit”所在的规则:

edit : main.o kbd.o command.o display.o \ 
      insert.o search.o files.o utils.o 
	cc -o edit main.o kbd.o command.o display.o \ 
       insert.o search.o files.o utils.o 


如果我们需要为目标“edit”增加一个的依赖文件,我们就需要在两个地方添加(依赖文件列表和规则的命令中),这就给后期的维护和修改带来了很多不方便,添加或修改时出现遗漏。
为了避免这个问题,在实际工作中大家都比较认同的方法是,使用一个变量“objects”、“OBJECTS”、“objs”、“OBJS”、“obj”或者“OBJ”来作为所有的.o 文件的列表的替代。在使用到这些文件列表的地方,使用此变量来代替。
objects = main.o kbd.o command.o display.o \ 
      insert.o search.o files.o utils.o 


“objects”作为一个变量,它代表所有的.o文件的列表。在定义了此变量后,我们就可以在需要使用这些.o文件列表的地方使用“$(objects)”来表示它。


01_7.自动推导规则。

在使用make编译.c源文件时,编译.c源文件规则的命令可以不用明确给出。这是因为make本身存在一个默认的规则,能够自动完成对.c文件的编译并生成对应的.o文件。它执行命令“cc -c”来编译.c源文件。
在Makefile中我们只需要给出需要重建的目标文件名(一个.o文件) ,make会自动为这个.o文件寻找合适的依赖文件(对应的.c文件。对应是指:文件名除后缀外,其余都相同的两个文件) ,而且使用正确的命令来重建这个目标文件。


01_8.另类风格的makefile。

书写规则建议的方式是:单目标,多依赖。就是说尽量要做到一个规则中只存在一个目标文件,可有多个依赖文件。尽量避免使用多目标,单依赖的方式。这样书写的好处是后期维护会非常方便,而且这样做会使Makefile 会更清晰、明了。


01_9.清除工作目录过程文件。

.PHONY : clean 
clean : 
	-rm edit $(objects)


这两个实现有两点不同:
1. 通过“.PHONY”特殊目标将“clean”目标声明为伪目标。避免当磁盘上存在一个名为“clean”文件时,目标“clean”所在规则的命令无法执行。
2. 在命令行之前使用“-”,意思是忽略命令“rm”的执行错误。


这样的一个目标在 Makefile 中,不能将其作为终极目标(Makefile 的第一个目标)。因为我们的初衷并不是当你在命令行上输入make以后执行删除动作。而是要创建或者更新程序。

 

点击复制链接 与好友分享!回本站首页
相关TAG标签 简介
上一篇:笔试基础归纳-8.22
下一篇:计算机图形学(三)_图元的属性_7_ 线属性函数
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 第一门户--致力于做实用的IT技术学习网站