Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103639257
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-17 17:23:32

  出处:Unix爱好者家园unix-cd.com  

默认的情况下,make执行Makefile中的第一个规则,此规则的第一个目标称之为“最终目的”或者“终极目标”(就是一个Makefile最终需要更新或者创建的目标)。

上例的Makefile,目标“edit”在Makefile中是第一个目标,因此它就是make的“终极目标”。当修改了任何C源文件或者头文件后,执行make将会重建终极目标“edit”。

当在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同样会去寻找它的依赖文件的重建规则(是这样一个规则:这个依赖文件在规则中作为目标出现),就是.c.h文件的重建规则。在上例的Makefile中没有哪个规则的目标是.c或者.h文件,所以没有重建.c.h文件的规则。不过C言语的源程序文件可以使用工具Bison或者Yacc来生成(具体用法可参考相应的手册)。

完成了对.o文件的创建(第一次编译)或者更新之后,make程序将处理终极目标“edit”所在的规则,分为以下三种情况:

1.        目标文件“edit”不存在,则执行规则创建目标“edit”。

2.        目标文件“edit”存在,其依赖文件中有一个或者多个文件比它“更新”,则根据规则重新链接生成“edit”。

3.        目标文件“edit”存在,它比它的任何一个依赖文件都“更新”,则什么也不做。

上例中,如果更改了源文件“insert.c”后执行make,“insert.o”将被更新,之后终极目标“edit”将会被重生成;如果我们修改了头文件“command.h”之后运行“make”,那么“kbd.o”、“command.o”和“files.o”将会被重新编译,之后同样终极目标“edit”也将被重新生成。

以上我们通过一个简单的例子,介绍了Makefile中目标和依赖的关系。对于Makefile中的目标。在执行“make”时首先执行终极目标所在的规则,接下来一层层地去寻找终极目标的依赖文件所在的规则并执行。当终极目标的规则被完全的展开以后,make将从最后一个被展开的规则处开始执行,之后处理倒数第二个规则,……依次回退。最后一步执行的就是终极目标所在的规则。整个过程就类似于C语言中的递归实现一样。在更新(或者创建)终极目标的过程中,如果出现错误make就立即报错并退出。整个过程make只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是否正确,make不做任何错误检查。

因此,需要正确的编译一个工程。需要在提供给make程序的Makefile中来保证其依赖关系的正确性、和执行命令的正确性。

 

同样是上边的例子,我们来看一下终极目标“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

 

在这个规则中.o文件列表出现了两次;第一次:作为目标“edit”的依赖文件列表出现,第二次:规则命令行中作为“cc”的参数列表。这样做所带来的问题是:如果我们需要为目标“edit”增加一个的依赖文件,我们就需要在两个地方添加(依赖文件列表和规则的命令中)。添加时可能在“edit”的依赖列表中加入了、但却忘记了给命令行中添加,或者相反。这样给后期的维护和修改带来了很多不方便,而且容易出现修改遗漏。

为了避免这个问题,在实际工作中大家都比较认同的方法是,使用一个变量“objects”、“OBJECTS”、“objs”、“OBJS”、“obj”或者“OBJ”来作为所有的.o文件的列表的替代。在使用到这些文件列表的地方,使用此变量来代替。在上例的Makefile中可是添加这样一行:

 

objects = main.o kbd.o command.o display.o \

     insert.o search.o files.o utils.o

 

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

 

objects = main.o kbd.o command.o display.o \

         insert.o search.o files.o utils.o

edit : $(objects)

    cc -o edit $(objects)

…….

…….

clean :

    rm edit $(objects)

 

需要增加或者去掉一个.o文件时。我们只需要改变“objects”的定义(加入或者去掉若干个.o文件)。这样做不但减少维护的工作量,而且可以避免由于遗漏而产生错误的可能。

 

在使用make编译.c源文件时,可以省略编译一个.c文件所使用的命令。这是因为make存在一个默认的规则,能够自动完成对.c文件的编译并生成对应的.o文件。它执行命令“cc -c”来编译.c源文件。对于上边的例子,此默认规则就使用命令“cc -c main.c -o main.o”来创建文件“main.o”。因此对一个目标文件是“N.o”,倚赖文件是“N.c”的规则。可以省略其规则的命令行,使用make的默认命令。此默认规则称为make的隐含规则。

我们书写Makefile时,对于一个.c文件如果使用make的隐含规则,那么它会被自动作为对应.o文件的一个依赖文件(对应是指:文件名除后缀外,其余都相同的两个文件)。因此我们也可以在规则中省略目标的倚赖.c文件。

我们上边的例子就可以以更加简单的方式书写,使用了变量“objects”。简化版本的Makefile如下:

 

# sample Makefile

objects = main.o kbd.o command.o display.o \

         insert.o search.o files.o utils.o

    

edit : $(objects)

     cc -o edit $(objects)

    

main.o : defs.h

kbd.o : defs.h command.h

command.o : defs.h command.h

display.o : defs.h buffer.h

insert.o : defs.h buffer.h

search.o : defs.h buffer.h

files.o : defs.h buffer.h command.h

utils.o : defs.h

    

.PHONY : clean

clean :

     rm edit $(objects)

 

这种格式的Makefile更接近于我们实际的应用。(关于目标“clean”的详细说明我们在后边。

make的隐含规则在实际工程的make中会经常使用,它使得编译过程变得方便。几乎在所有的Makefile中都用到了make的隐含规则,make的隐含规则是非常重要的一个概念。后续我们会在第九章会专门讨论make的隐含规则。

 

makefile

Makefile中,目标使用隐含规则生成,我们就可以也可以书写另外一种风格Makefile。在这个Makefile中,根据依赖而不是目标对规则进行分组。上例的Makefile就可以这样来实现:

 

#sample Makefile

objects = main.o kbd.o command.o display.o \

       insert.o search.o files.o utils.o

 

edit : $(objects)

cc -o edit $(objects)

 

$(objects) : defs.h

kbd.o command.o files.o : command.h

display.o insert.o search.o files.o : buffer.h

 

例子中头文件“defs.h”作为所有.o文件的依赖文件。其它两个头文件作为其对应规则的目标中所列举的所有.o文件的依赖文件。

但是这种风格的Makefile并不值得我们借鉴。问题在于:同时把多个目标文件的依赖放在同一个规则中进行描述(一个规则中含有多个目标文件),这样导致规则定义不明了,比较混乱。建议大家不要在Makefile中采用这种方式了书写。否则后期维护将会是一件非常痛苦的事情。

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

 

Makefile中的规则也可以完成除编译以外的任务。例如:前边提到的实现清除当前目录中在编译过程中生成的文件(edit和哪些.o文件)的规则:

 

clean :

rm edit $(objects)

 

在实际应用时,我们会把这个规则写成如下稍微复杂一些的样子。以防止出现始料未及的情况。

.PHONY : clean

clean :

-rm edit $(objects)

 

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

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

上例中因为目标“clean”没有出现在终极目标“edit”依赖关系中,所以我们执行“make”时,目标“clean”所在的规则将不会被处理。如果需要执行此规则,需要在make的命令行选项中明确指定这个目标(执行“make clean”)。

阅读(201) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~