makefile经典文档试译

类别:编程语言 点击:0 评论:0 推荐:
 一、总述

二、依赖性检查: make vs. 脚本

三、隐含规则的基本用法

四、处理的依赖性

五、空规则

六、特殊目标

七、不明目标

八、make的保留字

九、安静地运行命令

十、自动恢复SCCS文件

十一、传递参数:简单的make宏

一.总述

我们将祥细介绍make程序,这将包括:
 隐藏依赖性检查
 命令依赖性检查
 模式匹配规则
 自动修补源代码控制系统(SCCS)文件

这个版本的make可以与之前版本的makefile正常工作。依赖于加强版的makefile可能与其他版本的make应用不兼容。(make的加强请见相关资料)make组织了生成目标文件和可执行文件的过程。它可以让编译器一直化被消除对于在代码被变动后没被影响的不同模块的不必要的重编译。 make让编译的过程更加简化。你也可以用它来自动化地组织任何复杂或重复的非交互性工作。你也可以用make来更新和维护目标库文件,运行测试工具,或是向文件系统或磁带安装文件。

与自动修补源代码控制系统(SCCS)协同工作,可以用make来保证一个大型的软件项目可以根据一整个系统的源代码编译出想要得到的版本。 make从一个你创建的叫做makefile的文件里面读取信息,它里面包括了你在编译过程将要编译哪些文件,以及如何去编译它们的信息。当你写完并测试完makefile之后,就可以不用顾及编译处理过程的细节问题,make会把这一切料理好的。

二。依赖性检查: make vs. 脚本
尽管脚本可以在一些很细微的情况下保证在一致性,但是用脚本来编译一个软件项目通常都是不合适的。一方面,你不可能在当程序或模块的一部分被变的时候,等着一个简单的脚本来重新编译每一个程序或目标模块。另一方面,因为不得不为每一次迭代来编辑脚本就已经与一致性的目标相矛盾了。尽管用一个非常复杂的脚本来重编译那些需要被编译的模块是有可能的,但是make会把这件事做的更好。

make可以让你对编译的内容跟编译的方法写一个简单而且结构化的列表。它使用了一种依赖性检查机制来比较源文件或由源文件得到的中间文件的模块。make只在当一个或多个这样的被称做依赖性文件的前提文件在上一次编译之后被更改之后重新编译这些模块。

make比较已有的目标模块跟它的依赖文件的最后修改时间,来决定一个目标文件对于它的依赖文件是否已经过期。如果这个模块不存在,或是模块要比它的依赖文件生成的更早,那么make就认为这个模块文件是过期的了,这样就通过相应的命令来重新编译它。一个模块可以在当编译生成它的命令变化的时候被认为是过期的。

因为make是做了一个完整的依赖性的扫描,对一个源文件的修改就会一致地影响到任意数量的中间文件或是处理过程。这一点使你可以指定一个自顶而下的层次性的步骤。你可以把makefile想象成一个配方.make读出配方,决定哪些步骤需要被执行,并且只执行那些对于制造最终模块需要的步骤。每一个要被编译的文件,或是执行的步骤,都被叫做一个目标(target)。makefile对于某个目标的一条包括它的名字,它依赖于的文件列表和一系列编译生成这个目标所需要的命令。

这一系列的命令就叫做规则。make把依赖性看作是先决条件的目标,在处理当前目标之前(在需要的情况下)更新它们。一个目标的规则不一定总要生成一个文件,不过如果它生成了一个文件,它就被称为目标文件。目标生成的每个文件(比如说,目标所依赖的文件)就被叫做依赖文件.如果对于一个目标的规则用那个名字没有生成文件,那么make就执行规则并认为目标对之后的操作来说已经是最新的了(up-to-date)。make假定仅当它会对当前正在被处理的文件可能进行修改。如果一个源文件在被make操作的时候正在被另一个进程修改时,所生成的文件就有可能是处于一个不一致的状态中。

编写一个简单的makefile对于一个目标项来说makefile的基本格式是:
target ...:[dependency...]
[command]
....

在第一行,目标名字列表以一个冒号结束。紧跟着的是一系列的依赖文件列表(如果有的情况下)。如果几个目标被列举出来,这就说明每一个这样的目标会被用提供的规则单独的编译。后来的几行都是以TAB开始的,这样的起始被认为是一行包含目标的命令行。有一个很常见的错误就是用SPACE来代替TAB,造成makefile文件的写法错误。

以"#"开始的直到下一个新行开始的行被认为是注释行,它不会造成一个目标项的结束。目标项是以下一个非空的并以非TAB或#号开始的行标记结束的,或是以文件尾来标识。

一个很小的makefile可以由仅一个目标构成,比如:
test:
 ls test
 touch test

当你不带参数执行make命令的时候,它会自动找一个名叫"makefile"的文件来进行编译,如果它不存在的话,会找"Makefile"。如果这几个文件都不在SCCS控制下的话,make就会检查它的历史文件来确定makefile。如果这已经是过期了的,make会得到最新的版本的。

如果make找到一个makefile,它就开始对makefile中出现的第一个目标项进行依赖性检查。不然的话,你就必须要在命令行以参数的形式列举出要被编译的目标。make会在构造目标的过程中显示所有的它运行的命令。如:
$make
ls test
test not found
touch test
ls test
test

在这个例子中,因为test文件不存在,这样就被认为是过期了,make就执行目标项中的规则。如果你接着再运行一次make,那么它就会告诉你目标已经是最新的了,这样就跳过了规则的执行。如:
$make
'test' is up to date.

注意---如果命令行里包含任何外壳原字符,比如说分号(重新项符号<,>,>>,|),代替符(*,?,[],$,=) 或是引用符,退出符或注释(“,‘,、,#,等),make会调用Bourne外壳来处理一个命令行。如果外核不需要解析命令行,那么就让exec()直接运行.

在一条规则中不同的两行有一个很特别的性质就是,不同的命令行是被不同的进程或shell下执行的,这就是说比如有如下makefile:
test:
 cd /tmp
 pwd
执行的结果可能与你想象的不一样,它的结果会是:
$make test
cd /tmp
pwd
/usr/tutorial/waite/arcana/minor/pentangles
而不是你所想象的最后一行应该是/tmp

你可以使用分号来指明命令是在一个单独的shell中按顺序执行,如:
test:
 cd /tmp;pwd
或者你也可以通过在makefile里用''符来把命令输入流定位到下一行中。新的这一行则被make看为是一个空格。反斜线必须是一行的最后一个字符。而分号是在shell中所必须有的。如:
test:
 cd /tmp;
 pwd

三。隐含规则的基本用法:
当对于一个特定的目说没有规则存在的时候,make就会试图用一个隐含的规则来构建它。当make找到一个目标属于的一类文件的规则之后,它将用在这个隐含着的规则来进行操作。

在你所写的所有的makefile之外,make会继续读入一个默认的makefile,它位于  /usr/share/lib/make/make.rules,它包含一些目标项的隐藏规则,以及一些其他的信息。

注意 -- 隐藏规则是在早期的make里面手动编写的。一共有两类隐含的规则:后缀名式和模式匹配式。对于与另一个文件有相同的名字不过后缀名不同的文件来说,后缀式规则对它们指明了一系列的命令根据这个后缀名来编译这个文件。模式匹配规则则是根据一个与依赖性与wild-card模式相匹配的目标来选择编译规则。这样的隐含规则在默认情况下是后缀名规则。在一些情况下,使用后缀名规则可以消除编写一整个makefile文件的麻烦

比如说,通过一个单独的functions.c的C语言文件来构建名为functions.o的目标文件,你应该使用以下的命令:
$make functions.o
cc -c functions.c -o functions.o
这个规则对于从源文件nonesuch.c构建成nonesuch.o目标文件也同样的有效。为了从functions.c构建一个名为functions的可执行文件(它没有后缀名)你只需要用以下的命令:
$make functions
cc -o functions functions.c
这种从a.c构建a.o的规则被叫做.c.o后缀名规则(发音为"dot-see-dot-oh")。这条规则才对从a.c构建一个可执行的程序被叫做.c规则。

 

四。处理的依赖性:

在make命令开始之后,它通过深度依赖优先的法则遇到的处理顺序来处理目标。比如如下的makefile:
batch: a b
 touch batch
b:
 touch b
a:
 touch a
c:
 echo "you won't see me"
make就从目标batch开始。因为batch有一些依赖性还没有被检查,它们分别是a和b,make推延了生成batch直到它检查了a和b的依赖性规则。
batch
a b
因为a没有依赖性了,make就处理它。如果文件不存在,则make会执行在目标项里面的规则。
$make
touch a
.....
接下来,make回到父目标batch来,因为还有一个没有检查依赖性的目标b,make又开始找b的依赖性。
.....
touch b
.....
最后,因为所有的batch的依赖性都被检查过了并已被构建了出来(如果需要的话),make就生成了batch:
batch

因为它重构建了至少一个batch的依赖文件,make假设batch已经过期了,并重新构建batch,如果a或b当前没有被构建,但是在当前目录中已经存在且比batch要更新,make的时间版比较也会使batch被重新构建:
.....
touch batch
在依赖性扫描中没有遇到的目标项就不会被处理。尽管makefile里有一个关于c的目标项,所以它的规则就没有被执行。你可以选择一个代替的起始目标,比如用参数的形式把c传递给make.

在下一个例子中,batch目标没有生成文件,取而代之的,它被用作一个组织一组目标的标签。

batch: a b c
a: a1 a2
 touch a
b:
 touch b
c:
 touch c
a1:
 touch a1
a2:
 touch a2
在这种情况下,目标们按以下的顺序并处理被检查并被处理:
batch
a b c
a1 a2

本质上说,make接下来会:
1.检查batch的依赖性,注意这里有三个子目标,所以就推迟batch的生成
2.检查a,这个第一依赖目标,注意到它有两个自己的依赖目标,按例,make会继续:
 a.检查a1,如果需要的话,重新构造它。
 b.检查a2,如果需要的话,重新构造它。
3.决定是否要重新构建a
4.检查b并在需要的时候重建它.
5.检查c并在需要的时候重建它。
6.在遍历了它的依赖性树之后,make会检查并处理最顶部的目标batch。如果batch还有其他的规则,make会执行它。这里batch没有其他的规则了
这样make就不执行任何操作,但是注意batch已经被重建了,任何依赖于batch的目标也会被重建的。

五。空规则:
如果一个目标项没有规则,make会试图从一个隐藏规则中选一条来构建它。如果make不能找到一个合适的隐藏规则来处理它,而且在SCCS历史中没有找到相应的规则,make就会认为目标没有相应的文件,这样就把这种缺失的规则称为一个空规则。

注意 -- 你可以用一个空规则的依赖来强迫目标规则被执行。这种情况的一个更方便的叫法为强制(FORCE)。对以下的makefile:

haste: FORCE
 echo "haste makes waste"
FORCE:
make会操作之下的规则,甚至当这个文件已经是最新的了也不例外:
$touch haste
$make haste
echo "haste makes waste"
haste makes waste

make有很多个内建的特殊目标用来达到很多特殊的功能。比如:.PRECIOUS目标会在make被中断之后把make导引到保留库文件中。

 

六。特殊目标:
 1.以"."开始
 2.没有依赖性
 3.可以在makefile里的任意一个位置出现。

七。不明目标:
如果一个文件的名字没有在命令行中也不在依赖性列表中,且它:
 1.它不是当前工作目录中的一个文件。
 2.它没有目标以及依赖项
 3.它不属于任何隐藏规则定义的文件类
 4.没有任何SCCS历史文件
 5.没有为./DEFAULT特殊目标定义规则
这样make就会停止运行并发出一个错误信息。

如:
$make believe
make: Fatal error: Don't know how to make target 'believe'.
注意 - 尽管如果当-k参数起作用,make会继续对其他不以这个有错误发生的目标为依赖性的目标进行操作.

重复目标:
目标可以在一个makefile中不止一次出现,如:
foo: def_1
foo: dep_2
foo:
 touch foo
跟:
foo: dep_1 dep_2
 touch foo
完全相同。

然而,很多人认为如果一个目标只出现过一次会比较好,这样阅读会比较轻松一些。


八。make的保留字:
.BUILT_LAST_MAKE_RUN .DEFAULT .DERIVED_SRC
.DONE .IGNORE .INIT
.KEEP_STATE .MAKE_VERSION .NO_PARALLEL
.PRECIOUS .RECURSIVE .SCCS_GET
.SILENT .SUFFIXES .WAIT
FORCE HOST_ARCH HOST_MACH
KEEP_STATE MAKE MAKEFLAGS
MFLAGS TARGET_ARCH TARGET_MACH
VERSION_1.0 VIRTUAL_ROOT VPATH

九。安静地运行命令:
你可以对规则通过一行的行首添加一个"@"号限制止一个命令行的显示数据。比如说,如下列的目标:
quiet:
 @echo you only see me once
就会出现如下的效果:
$make quiet
you only see me once
如果你要在一个特定的make运行中限制命令的显示,你可以使用-s参数。如果你要限制所有的命令选择显示,你需要在你的makefile里面加入.SLIENT。如:
.SLIENT
quiet:
 echo you only see me once

特定函数目标以"."为开始符。目标名以一个"."开始的目标永远不会被用作为一个起始的目标,除非指明被要求为命令行的一个参数。make仅仅会输出一个错误信息并当命令返回一个非0的退出指令时停止。比如说,如果你有一个目标:
rmxyz:
 rm xyz
而且当前目录下面没有一个叫xyz的文件。make会在rm返回它的错误状态值之后挂起。
$ls xyz
xyz not found
$make rmxyz
rm xyz
rm:xyz: No such file or directory
***Error code 1
make: Fatal error: Command failed for target 'rmxyz'
注意 - 如果-和#是前两个字符,它们都会起到效果的。为了不等待命令的退出代码而继续进行处理,你可以用一个"-"来作为第一个非TAB字符:
rmxyz:
 -rm xyz
rm: xyz: No such file or directory
*** Error code 1 (ignored)


注意 - 除非你在测试一个makefile,在全局情形下,通常忽略非0的错误代码都是不好的主意。


尽管建议这样做通常是不好的,你可以通过-i参数来忽略所有的错误代码。你也可以通过在makefile中包含.IGNORE这个特殊目标来忽略所有的退出代码,尽管这样的处理方式应该被避免掉。

如果你正在处理一列目标,而且你想让make继续下一个目标的操作而不是在遇到一个非0的返回代码停下来的话,你可以使用-k参数。

十。自动恢复SCCS文件:
当源文件的名字在依赖性列表中,make会像其他目标一样对待它们的。因为源文件是假定存在于这个目录里面的,这样就没有必要再在makefile里面添加一个目录项。当一个目标没有依赖性,但它存在于当前的目录里时,make假设这个文件已经是最新的了。但是,如果源文件是被SCCS所控制中,那么make就会做一些附加的检查来确保这个源文件是最新的了。

如果文件不存在,或者历史文件更新一些,那么make会自动地用下面的命令来恢复最新版本的文件:
sccs get -s filename -Gfilename
注意- 在其他版本的make里面,自动的SCCS恢复仅是几个特定的隐藏规则的一个特性。而且,与之前版本的make不同,make仅仅在SCCS目录中查找历史文件;在当前目录下的历史文件被忽略掉了。然而,如果源文件是对任何人都是可写的,make不会恢复一到更新的版本上.
$ls SCCS/*
SCCS/s.functions.c
$rm -f functions.c
$make functions
sccs get -s functions.c -Gfunctions.c
cc -o functions functions.c

make检查恢复版本的历史文件的时间戳与历史文件的时间戳。它不检查看一下是否在当前目录下的版本是最新的版本。这样,如果哪个人作过一次按时间取操作(sccs get -c),make就不会发现这个事实,而且你会无意地构建一个更老版本的目标文件。为了确保你编译生成的目标是最新版本的,你应该在make之前,用 sccs取得SCCS或是一个sccs清除命令。

禁止SCCS恢复
恢复SCCS文件的命令在默认makefile中被规则.SCCS_GET指定出来。为了禁止自动恢复功能,只要简单地在makefile中相应的目标项中加入一个空规则就可以了。

#禁止SCCS恢复
.SCCS_GET

十一。传递参数:简单的make宏
make宏替换在当你想要从makefile中传递参数到命令行使你的工作变得很轻松。假设你要对你的程序编译一个优化版本,利用cc -O 参数。你可以通过在makefile中加入make宏来达到这类性质,比如下面的例子:
functions: functions.c
 cc $(CFLAGS) -o functions functions.c

宏引入以某个值的占位符的角色出现在makefile里面,或是在makefile自己里面,或是当成make命令的一个参数。如果你能定义一个CFLAGS宏,make会把你事先定义的宏值代换到指定的位置里去。

$rm functions
$make functions "CFLAGS= -O"
cc -O -o functions functions.c
注意 - 在.c和.c.o隐含规则中都有CFLAGS的引入存在。
注意 - 命令行定义必须是一个单参数的,所以这样的引用就如下例:
如果一个宏没有被定义,make会把这样的引用扩展为一个空的字符串,你也可以通过在makefile里面定义一个宏。一个典型的应用就是把CFLAGS设为-O,这样就可以默认生成优化了的目标代码.
CFLAGS=-O
functions:functions.c
 cc $(CFLAGS) -o functions functions.c

一个在命令行参数中指出的宏定义会覆盖在makefile里面定义的宏。条件性定义的宏是这条规则的例外。

比如说,编译一个正在用dbx或dbxtool调试的函数模块的时候,你可以在命令行中定义CFLAGS的值为-g:
$rm functions
$make CFLAGS=-g
cc -g -o functions functions.c
当用gprof来编译一个外变量时,把-O和-pg同时送给CFLAGS。

一个宏引用在宏的名字不再是一个单个的字符的时候必须包含圆括号。如果宏名字只是一个字符,那么圆括号可以被忽略。你可以用花括号来代替圆括号。比如,'$X','$(X),和'$'就是完全相同的。

本文地址:http://com.8s8s.com/it/it23305.htm