分类: LINUX
2010-11-13 16:37:12
所谓makefile的规则,就是在何种情况下通过什么命令来创建目标文件。构成makefile的三部分:目标、依赖、命令。
一、目标:
1、 常规的目标文件
通过命令和依赖文件来更新目标文件,最普通的用法。
2、 伪目标
伪目标:它不代表一个真正的文件名,在执行make时可以指定这个目标来执行所在规则定义的命令。
使用伪目标的原因:
A、 在makefile中定义只执行命令的目标(此目标的目的是为了执行一些命令,而不是创建这个目标)和工作目录下的实际文件出现名字冲突。
B、 提高makefle的执行效率。
例如:
clean:
rm *.o temp
当当前目录下没有clean文件时,执行 make clean时,rm命令总会被执行。
当当前目录下存在clean文件时,执行 make clean时,规则中没有依赖文件,所以目标文件总是最新的而不会去执行rm命令。为了避免这个问题,将目标clean声明为伪目标(.PHONY clean)。当声明为伪目标后,不管clean文件存不存在,rm命令都会执行,并且make也不会试图去查找隐含规则来创建目标文件,从而提高了makefile的执行顺序。
当一个伪目标作为另一个伪目标的依赖时,规则中的命令总是会被执行。
当伪目标没有作为任何目标的依赖时,必须通过make来指定这个伪目标来执行命令。
C、 通过伪目标创建多个可执行程序。
例如:
#sample Makefile
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
执行 make 时,目标“all”被作为终极目标。为了完成对它的更新,make 会创建(不存在)或者重建(已存在)目标“all”的所有依赖文件(prog1、prog2 和 prog3)。当需要单独更新某一个程序时,我们可以通过 make 的命令行选项来明确指定需要重建的程序。(例如:“make prog
当一个伪目标作为另外一个伪目标依赖时,make 将其作为另外一个伪目标的子例程来处理(可以这样理解:其作为另外一个伪目标的必须执行的部分,就行 C 语言中的函数调用一样)。下边的例子就是这种用法:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思(执行目标“clearall 时会触发它们所定义的命令被执行”)。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。例子首先通过特殊目标“.PHONY”声明了多个伪目标,它们之间使用空各分割,之后才是各个伪目标的规则定义。
D、强制目标(没有命令和依赖的规则)
如果一个规则没有命令或者依赖,而且它的目标是一个不存在的文件名。在执行此规则时,目标总会被认为是最新的。如果这样的目标文件作为另外一个目标文件的依赖文件时,因为依赖总被认为更新过,因此作为依赖所在的规则定义的命令总会被执行。
例如:
clean: FORCE
rm $(objects)
FORCE:
这个例子中,目标“FORCE”符合上边的条件。它作为目标“clean”的依赖出现,在执行 make时,它总被认为被更新过。所以“clean”所在的规则在被执行时规则所定义的命令总会被执行。这样的一个目标通常我们将其命名为“FORCE”。
上边的例子中使用“FORCE”目标的效果和我们指定“clean”为伪目标效果相同。两种方式相比较,使用“.PHONY”方式更加直观高效。这种方式主要用在非 GNU 版本的 make 中。在使用GNU make,尽量避免使用这种方式。在GNU make中我们推荐使用伪目标方式。
E、 空目标文件
空目标是伪目标的一个变种;此目标所在规则执行的目的和伪目标相同——通过 make 命令行指定终极目标来执行规则所定义的命令。和伪目标不同的是:这个目标可以是一个存在的文件,一般文件的具体内容我们并不关心,通常此文件是一个空文件。
空目标文件只是用来记录上一次执行此规则定义命令的时间。一般在这样的规则中,命令部分都会使用“touch”在完成所有命令之后来更新目标文件的时间戳,记录此规则命令的最后执行时间。make 时通过命令行将此目标作为终极目标,当前目录下如果不存在这个文件,“touch”会在第一次执行时创建一个空的文件(命名为空目标文件名)。
F、 makefile的特殊目标
在 Makefile 中,有一些名字,当它们作为规则的目标出现时,具有特殊含义。它们是一些特殊的目标,GNU make 所支持的特殊的目标有:
.PHONY:
目标“.PHONY”的所有的依赖被作为伪目标。伪目标时这样一个目标:当使用make命令行指定此目标时,这个目标所在规则定义的命令、无论目标文件是否存在都会被无条件执行。
.SUFFIXES:
特殊目标“SUFFIXES”的所有依赖指出了一系列在后缀规则中需要检查的后缀名(就是当前make需要处理的后缀)。
.DEFAULT
Makefile 中,目标“.DEFAULT”所在规则定义的命令,被用在重建那些没有具体规则的目标(明确规则和隐含规则)。就是说一个文件作为某个规则的依赖,但却不是另外一个规则的目标时。Make 程序无法找到重建此文件的规则,此种情况时就执行“.DEFAULT”所指定的命令。
.PRECIOUS
目标“.PRECIOUS”的所有依赖文件在make过程中会被特殊处理:当命令在执行过程中被中断时,make不会删除它们。而且如果目标的依赖文件是中间过程文件,同样这些文件不会被删除。这一点目标“.PRECIOUS”和目标“.SECONDAY”实现的功能相同。另外,目标“.PRECIOUS”的依赖文件也可以是一个模式,例如“%.o”。这样可以保留有规则创建的中间过程文件。
.INTERMEDIATE
目标“.INTERMEDIATE”的依赖文件在make时被作为中间过程文件对待。没有任何依赖文件的目标“.INTERMEDIATE”没有意义。
.SECONDARY
目标“.SECONDARY”的依赖文件被作为中间过程文件对待。但这些文件不会被自动删除。没有任何依赖文件的目标“.SECONDARY”的含义是:将所有的文件作为中间过程文件(不会自动删除任何文件)。
.DELETE_ON_ERROR
如果在Makefile中存在特殊目标“.DELETE_ON_ERROR”,make在执行过程中,如果规则的命令执行错误,将删除已经被修改的目标文件。
.IGNORE
如果给目标“.IGNORE”指定依赖文件,则忽略创建这个文件所执行命令的错误。给此目标指定命令是没有意义的。当此目标没有依赖文件时,将忽略所有命令执行的错误。
.LOW_RESOLUTION_TIME
目标“.LOW_RESOLUTION_TIME”的依赖文件被 make 认为是低分辨率时间戳文件。给目标“.LOW_RESOLUTION_TIME”指定命令是没有意义的。
通常文件的时间辍都是高分辨率的,make 在处理依赖关系时、对规则目标-依赖文件的高分辨率的时间戳进行比较,判断目标是否过期。但是在系统中并没有提供一个修改文件高分辨率时间辍的机制(方式),因此类似“cp -p”这样的命令在根据源文件创建目的文件时,所产生的目的文件的高分辨率时间辍的细粒度部分被丢弃(来源于源文件)。可能会造成目的文件的时间戳和源文件的相等甚至不及源文件新。处理此类命令创建的文件时,需要将命令创建的文件作为目标“.LOW_RESOLUTION_TIME”的依赖,声明这个文件是一个低分辨率时间辍的文件。例如:
.LOW_RESOLUTION_TIME: dst
dst: src
cp -p src dst
首先规则的命令“cp –p src dst”,所创建的文件“dst”在时间戳上稍稍比“src”晚(因为命令不能更新文件“dst”的细粒度时间)。因此 make 在判断文件依赖关系时会出现误判,将文件作为目标“.LOW_RESOLUTION_TIME”的依赖后,只要规则中目标和依赖文件的时间戳中的初始时间相等,就认为目标已经过期。这个特殊的目标主要作用是,弥补系统在没有提供修改文件高分辨率时间戳机制的情况下,某些命令在 make 中的一些缺陷。对于静态库文件(文档文件)成员的更新也存在这个问题。make 在创建或者更新静态库时,会自动将静态库的所有成员作为目标“.LOW_RESOLUTION_TIME”的依赖。
.SILENT
出现在目标“.SILENT”的依赖列表中的文件,make 在创建这些文件时,不打印出重建此文件所执行的命令。同样,给目标“.SILENT”指定命令行是没有意义的。没有任何依赖文件的目标“.SILENT”告诉make在执行过程中不打印任何执行的命令。现行版本make支持目标“.SILENT”的这种功能和用法是为了和旧版本的兼容。在当前版本中如果需要禁命令执行过程的打印,可以使用make的命令行参数“-s”或者“--silent”。
.EXPORT_ALL_VARIABLES
此目标应该作为一个简单的没有依赖的目标,它的功能含义是将之后所有的变量传递给子make进程。
.NOTPARALLEL
Makefile 中,如果出现目标“.NOPARALLEL”,则所有命令按照串行方式执行,即使存在 make的命令行参数“-j”。但在递归调用的字 make 进程中,命令可以并行执行。此目标不应该有依赖文件,所有出现的依赖文件将被忽略。所有定义的隐含规则后缀作为目标出现时,都被视为一个特殊目标,两个后缀串联起来也是如此,例如“.c.o”。这样的目标被称为后缀规则的目标,这种定义方式是已经过时的定义隐含规则的方法(目前,这种方式还被用在很多地方)。原则上,如果将其分为两个部分、并将它们加到后缀列表中,任何目标都可采用这种方式来表示。实际中,后缀通常以“.”开始,因此,以上的这些特别目标同样是以“.”开始。
G、 多目标
一个规则中可以有多个目标,规则所定义的命令对所有的目标有效。多目标规则意味着所有的目标具有相同的依赖文件。多目标通常用在以下两种情况:
仅需要一个描述依赖关系的规则,而不需要在规则中定义命令。
对于多个具有类似重建命令的目标。重建这些目标的命令并不需要是绝对相同,因为我们可以在命令行中使用make的自动化变量来引用具体一个目标,并完成对它的重建。
H、 多规则目标
Makefile 中,一个文件可以作为多个规则的目标出现。这种情况时,此目标文件的所有依赖文件将会被合并成此目标一个依赖文件列表,其中任何一个依赖文件比目标更新(比较目标文件和依赖文件的时间戳)时,make 将会执行特定的命令来重建这个目标。对于一个多规则的目标,重建此目标的命令只能出现在一个规则中(可以是多条命令)。如果多个规则同时给出重建此目标的命令,make将使用最后一个规则所以的命令,同时提示错误信息。
一个仅仅描述依赖关系的描述规则可用来给出一个或做多个目标文件的依赖文件。例如,Makefile中通常存在一个变量,就像以前我们提到的“objects”,它定义为所有的需要编译生成的.o 文件的列表。当这些.o 文件在其源文件所包含的头文件“config.h”发生变化之后能够自动的被重建,我们可以使用多目标像下边那样来书写我们的 Makefile:
objects = foo.o bar.o
foo.o : defs.h
bar.o : defs.h test.h
$(objects) : config.h
这样做的好处是:我们可以在源文件中增加或者删除了包含的头文件以后不用修改已经存在的Makefile的规则,只需要增加或者删除某一个.o文件依赖的头文件。这种方式很简单也很方便。对于一个大的工程来说,这样做的好处是显而易见的。在一个大的工程中,对于一个单独目录下的.o文件的依赖规则建议使用此方式。规则中头文件的依赖描述也可以使用GCC自动产生。
二、依赖
1、 依赖的类型
GNU的makefile中有两种不同的依赖类型:
A:常规依赖:是书写makefile经常用的类型。只要依赖的文件比目标文件“新”,make就会更新目标文件。
B:“order-only”依赖:
格式:A:B | C
在目标文件A存在的情况下:当B文件比目标文件A“新”,就会更新目标文件A。C文件比目标文件A“新”,不会更新目标文件A。
在目标文件A不存在的情况下:跟常规依赖一样。
chinaunix网友2010-11-15 15:11:59
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com