GNU make中文手册-第二章: GNU make 介绍

来源:百度文库 编辑:亮点网 时间:2020/02/21 02:33:50
GNU make中文手册-第二章: GNU make 介绍作者: hew  发布日期: 2006-3-21    查看数: 151   出自: http://www.linuxsky.net 第二章: GNU make 介绍

--------------------------------------------------------------------------------

make在执行时,需要一个命名为Makefile的文件。这个文件告诉make以何种方式编译源代码和链接程序。典型地,可执行文件可由一些.o文件按 照一定的顺序生成或者更新。如果在你的工程中已经存在一个或者多个正确的Makefile。当对工程中的若干源文件修改以后,需要根据修改来更新可执行文 件或者库文件,正如前面提到的你只需要在shell下执行“make”。make会自动根据修改情况完成源文件的对应.o文件的更新、库文件的更新、最终 的可执行程序的更新。

make通过比较对应文件(规则的目标和依赖,)的最后修改时间,来决定哪些文件需要更新、那些文件不需要更新。对需要更新的文件make就执行数据库中 所记录的相应命令(在make读取Makefile以后会建立一个编译过程的描述数据库。此数据库中记录了所有各个文件之间的相互关系,以及它们的关系描 述)来重建它,对于不需要重建的文件make什么也不做。

而且可以通过make的命令行选项来指定需要重新编译的文件。


--------------------------------------------------------------------------------

Problems and Bugs

=================

If you have problems with GNU `make' or think you've found a bug, please report it to the developers; we cannot promise to do anything but we might well want to fix it.

Before reporting a bug, make sure you've actually found a real bug.Carefully reread the documentation and see if it really says you can do what you're trying to do. If it's not clear whether you should be able to do something or not, report that too; it's a bug in the documentation!

Before reporting a bug or trying to fix it yourself, try to isolate it to the smallest possible makefile that reproduces the problem. Then send us the makefile and the exact results `make' gave you, including any error or warning messages. Please don't paraphrase these messages: it's best to cut and paste them into your report. When generating this small makefile, be sure to not use any non-free or unusual tools in your commands: you can almost always emulate what such a tool would do with simple shell commands. Finally, be sure to explain what you expected to occur; this will help us decide whether the problem was really in the documentation.

Once you have a precise problem you can report it in one of two ways.Either send electronic mail to:



bug-make@gnu.org



or use our Web-based project management tool, at:



http://savannah.gnu.org/projects/make/



In addition to the information above, please be careful to include the version number of `make' you are using. You can get this information with the command `make --version'. Be sure also to include the type of machine and operating system you are using. One way to obtain this information is by looking at the final lines of output from the command

`make --help'.


--------------------------------------------------------------------------------

以上时GNU make的bug反馈方式。如果在你使用GNU make过程中。发现bug或者问题。可以通过以上的方式和渠道反馈。

好了。开始我们的神奇之旅吧!



2.1 Makefile简介
在执行make之前,需要一个命名为Makefile的特殊文件(本文的后续将使用Makefile作为这个特殊文件的文件名)来告诉make需要做什么(完成什么任务),该怎么做。通常,make工具主要被用来进行工程编译和程序链接。

本节将分析一个简单的Makefile,它对一个包含8个C的源代码和三个头文件的工程进行编译和链接。这个Makefile提供给了make必要的信 息,make程序根据Makefile中的规则描述执行相关的命令来完成指定的任务(如:编译、链接和清除编译过程文件等)。复杂的Makefile我们 将会在本文后续进行讨论。

当使用make工具进行编译时,工程中以下几种文件在执行make时将会被编译(重新编译):

1. 所有的源文件没有被编译过,则对各个C源文件进行编译并进行链接,生成最后的可执行程序;

2. 每一个在上次执行make之后修改过的C源代码文件在本次执行make时将会被重新编译;

3. 头文件在上一次执行make之后被修改。则所有包含此头文件的C源文件在本次执行make时将会被重新编译。

后两种情况是make只将修改过的C源文件重新编译生成.o文件,对于没有修改的文件不进行任何工作。重新编译过程中,任何一个源文件的修改将产生新的对应的.o文件,新的.o文件将和以前的已经存在、此次没有重新编译的.o文件重新连接生成最后的可执行程序。

首先让我们先来看一些Makefile相关的基本知识。



2.2 Makefile规则介绍
一个简单的Makefile描述规则组成:

TARGET... : PREREQUISITES...

COMMAND

...

...

target:规则的目标。通常是程序中间或者最后需要生成的文件名。可以是.o文件、也可以是最后的可执行程序的文件名。另外,目标也可以是一个make执行的动作的名称,如目标“clean”,成这样的目标是“伪目标”。

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

command:规则的命令行。是make程序所有执行的动作(任意的shell命令或者可在shell下执行的程序)。

一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[Tab]字符开始,[Tab]字符告诉make此行是一个命令行。make按照命令完成相应的动作。这也是书写Makefile中容易产生,而且比较隐蔽的错误。

命令就是在任何一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可以没有依赖而只有动作(指定的命令)。比如Makefile中的目标“clean”,此目标没有依赖,只有命令。它所指定的命令用来删除make过程产生的中间文件(清理工作)。

在Makefile中“规则”就是描述在什么情况下、如何重建规则的目标文件,通常规则中包括了目标的依赖关系(目标的依赖文件)和重建目标的命令。 make执行重建目标的命令,来创建或者重建规则的目标(此目标文件也可以是触发这个规则的上一个规则中的依赖文件)。规则包含了目标和依赖的关系以及更 新目标所要求的命令。

Makefile中可以包含除规则以外的部分。一个最简单的Makefile可能只包含规则描述。规则在有些Makefile中可能看起来非常复杂,但是无论规则的书写是多么的复杂,它都符合规则的基本格式。

2.3 简单的示例
本小节开始我们在第一小节中提到的例子。此例子由3个头文件和8个C文件组成。我们讲述写一个简单的Makefile,来描述如何创建最终的可执行文件“edit”,此可执行文件依赖于8个C源文件和3个头文件。Makefile文件的内容如下:

#sample Makefile

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时,推荐者中将较长行分解为使用反斜线连接得多个行的方式。当我们完成了这个Maekfile以 后;创建可执行程序“edit”,你所要做的就是在包含此Makefile的目录(当然也在代码所在的目录)下输入命令“make”。删除已经本目录下生 成的文件和所有的.o文件,只需要输入命令“make clean”就可以了。

在这个Makefile中,目标(target)包含:可执行文件“edit”和.o文件(main.o,kbd.o….),依赖 (prerequisites)就是冒号后面的那些 .c 文件和 .h文件。所有的.o文件既是依赖(相对于可执行程序edit)又是目标(相对于.c和.h文件)。命令包括 “cc –c maic.c”、“cc –c kbd.c”……

目标是一个文件时,当它的任何一个依赖文件被修改以后,这个目标文件将会被重新编译或者重新连接。当然,此目标的任何一个依赖文件如果有必要则首先会被重 新编译。在这个例子中,“edit”的依赖为8个.o文件;而“main.o”的依赖文件为“main.c”和“defs.h”。当“main.c”或者 “defs.h”被修改以后,再次执行“make”时“main.o”就会被更新(其它的.o文件不会被更新),同时“main.o” 的更新将会导致“edit”被更新。

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

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



2.4 make如何工作
默认的情况下,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中来保证其依赖关系的正确性、和执行命令的正确性。



2.5 指定变量
同样是上边的例子,我们来看一下终极目标“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文件)。这样做不但减少维护的工作量,而且可以避免由于遗漏而产生错误的可能。



2.6 自动推导规则
在使用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的隐含规则。



2.7 另类风格的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会更清晰、明了。



2.8 清除工作目录过程文件
在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”)。