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

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-17 17:25:38

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

Maekfile中表示一个单一的文件名时可使用通配符。可使用的通配符有:“*”、“?”和“[…]”。在Makefile中通配符的用法和含义和Linuxunix)的Bourne shell完全相同。例如,“*.c”代表了当前工作目录下所有的以“.c”结尾的文件等。但是在Makefile中这些统配符并不是可以用在任何地方,Makefile中统配符可以出现在以下两种场合:

1.        可以用在规则的目标、依赖中,此时make会自动将其展开;

2.        可出现在规则的命令中,其展开是在shell在执行此命令时完成。

除这两种情况之外的其它上下文中,不能直接使用通配符。二是需要通过函数“wildcard”(可参考 7.3 文件名处理函数 一节)来实现。

如果规则中的某一个文件的文件名包含作为统配符的字符(“*”、“.”字符),在使用文件时需要对文件名中的统配字符进行转义处理,使用反斜线(\)来进行通配符的转义。例如“foo\*bar”,在Makefile中它表示了文件“foo*bar”。Makefile中对一些特殊字符的转移和B-SHELL以及C语言中的基本上相同。

另外需要注意:在Linuxunix)中,以波浪线“~”开始的文件名有特殊含义。

单独使用它或者其后跟一个斜线(~/),代表了当前用户的宿主目录。(在shell下可以通过命令“echo ~(~\)”来查看)。例如“~/bin”代表“/home/username/bin/”(当前用户宿主目录下的bin目录)

波浪线之后跟一个单词(~word),其代表由这个“word”所指定的用户的宿主目录。例如“~john/bin”就是代表用户john的宿主目录下的bin目录。

在一些系统中(像MS-DOSMS-Windows),用户没有各自的宿主目录,此情况下可通过设置环境变量“HOME”来模拟。

本节开始已经提到过,通配符可被用在规则的命令中,它是在命令被执行时由shell进行处理的。例如Makefile的清空过程文件规则:

 

clean:

    rm -f *.o

 

通配符也可以用在规则的依赖文件名中。看看下面这个例子。执行“make print”,执行的结果是打印当前工作目录下所有的在上一次打印以后被修改过的“.c”文件。

 

print: *.c

    lpr -p $?

    touch print

 

两点说明:1. 上述的规则中目标“print”时一个空目标文件。(不存在一个这样的文件,此目标不代表一个文件,它只是记录了一个所要执行的动作或者命令。参考 4.8 空目标文件 一节)。2. 自动环变量“$?”用在这里表示依赖文件列表中被改变过的所有文件。

变量定义中使用的通配符不会被展开(因此在定义变量不能按照这这种方式,下一小节将会详细讨论)。如果Makefile有这样一句:“objects = *.o”。那么变量“objects”的值就是“*.o”,而不是使用空格分开的所有.o文件列表。如果需要变量“objects”代表所有的.o文件,则需要是用函数“wildcard”来实现(objects = $(wildcar *.o))。

上一小节已经提到过在变量定义时使用通配符可能会导致意外的结果。本小节将此详细地分析和讨论。在书写Makefile时,可能存在这种不正确使用通配符的方法。这种看似正确的方式产生的结果可能并非你所期望得到的。假如在你的makefile中,期望能够根据所有的.o文件生成可执行文件“foo”。实现如下:

 

objects = *.o

 

foo : $(objects)

cc -o foo $(CFLAGS) $(objects)

 

这里变量“objects”的值是一个字符串“*.o”。在重建“foo”的规则中对变量“objects”进行展开,目标“foo”的依赖就是“*.o”,即所有的.o文件的列表。如果工作目录下已经存在必需的.o文件,那么这些.o文件将成为目标的依赖文件,目标“foo”将根据规则被重建。

但是如果我们将工作目录下所有的.o文件删除,在执行规则时将会得到一个类似于“没有创建*.o文件的规则” 的错误提示。这当然不是我们所期望的结果(可能在出现这个错误时会令你感到万分迷惑!)。为了实现们的初衷,在对变量进行定义的时需要使用一些高级的技巧,包括使用“wildcard”函数和实现字符串的置换。关于如何实现字符串的置换,我们将在后续进行详细地讨论。

前边提到过,在规则中,通配符会被自动展开。但在变量的定义和使用函数是,通配符不会被自动的展开。这种情况下需要通配符有效,要用到函数“wildcard”,其用法:$(wildcard PATTERN...) ;在Makefile中,它被展开未已经存在的、空格分割的、匹配此模式的所有文件列表。如果不存在符合此模式的文件,那么函数会忽略模式并返回空。需要注意的是:这种情况下规则中通配符的展开和上一小节匹配通配符的区别。

一般我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。复杂一些用法;可以使用“$(patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o。这样我们就可以得到在当前目录可生成的.o文件列表。因此在一个目录下可以使用如下内容的Makefile来将工作目录下的所有的.c文件进行编译并最后连接成为一个可执行文件:

 

#sample Makefile

objects := $(patsubst %.c,%.o,$(wildcard *.c))

 

foo : $(objects)

cc -o foo $(objects)

 

这里我们使用了make的隐含规则来编译.c的源文件。对变量的赋值也用到了一个特殊的符号(:=)。关于变量定义可参考 5.2 两种变量定义 一节。函数“patsubst”可参考 7.2 文本处理函数 一节

 

在一个较大的工程中,一般会将源代码和二进制文件(.o文件和可执行文件)安排在不同的目录来进行区分管理。这种情况下,我们需要使用make提供的目录自动搜索依赖文件功能(在指定的若干个目录下搜索依赖文件)。书写makefile时,指定依赖文件的搜索目录。当工程的目录结构发生变化时,我们就可以不更改Makefile的规则,而只更改依赖文件的搜索目录。

本节我们将详细讨论在书写Makefile时如何使用这一特性。在自己的工程中灵活运用这一特性,将会起到事半功倍的效果。

make可识别一个特殊变量“VPATH”。通过变量“VPATH”可以指定依赖文件的搜索路径,在规则的依赖文件在当前目录不存在时,make会在此变量所指定的目录下去寻找这些依赖文件。一般我们都是用此变量来说明规则中的依赖文件的搜索路径。其实“VPATH”变量所指定的是Makefile中所有文件的搜索路径,包括依赖文件和目标文件。

变量“VPATH”的定义中,使用空格或者冒号(:)将多个目录分开。make搜索的目录顺序按照变量“VPATH”定义中顺序进行(当前目录永远是第一搜索目录)。例如对变量的定义如下:

 

VPATH = src:../headers

 

它指定了两个搜索目录,“src”和“../headers”。对于规则“foo:foo.c”如果“foo.c”在“src”目录下,此时此规则等价于“foo:src:/foo.c”。

通过“VPATH”变量指定的路径在Makefile中对所有文件有效。当需要为不类型的文件指定不同的搜索目录时,需要使用另外一种方式。下一小节我们将会讨论这种更高级的方式。

另一个设置文件搜索路径的方法是使用make的“vpath”关键字(全小写的)。它不是一个变量,是一个make的关键字,它所实现的功能和上一小节提到的“VPATH”变量很类似,但是它更为灵活。它可以为不同类型的文件(由文件名区分)指定不同的搜索目录。它的使用方法有三种:

1vpath PATTERN DIRECTORIES

为符合模式“PATTERN”的文件指定搜索目录“DIRECTORIES”。多个目录使用空格或者冒号(:)分开。类似上一小节的“VPATH

2vpath PATTERN

清除之前为符合模式“PATTERN”的文件设置的搜索路径。

3vpath

清除所有已被设置的文件搜索路径。

 

vapth使用方法中的“PATTERN”需要包含模式字符“%”。“%”意思是匹配一个或者多个字符,例如,“%.h”表示所有以“.h”结尾的文件。如果在“PATTERN”中没有包含模式字符“%”而是一个明确的文件名,就是指出了此文件所在的目录,我们很少使用这种方式来为单独的一个文件指定搜索路径。在“vpath”所指定的模式中我们可以使用反斜杠来对字符“%”的引用(和其他的特使字符的引用一样)。

PATTERN”表示了具有相同特征的一类文件,而“DIRECTORIES”则指定了搜索此类文件目录。当规则的依赖文件列表中出现的文件不能在当前目录下找到时,make程序将依次在“DIRECTORIES”所描述的目录下寻找此文件。例如:

 

vpath %.h ../headers

 

其含义是:Makefile中出现的.h文件;如果不能在当前目录下找到,则到目录“../headers”下寻找。注意:这里指定的路径仅限于在Makefile文件内容中出现的.h文件。 并不能指定源文件中包含的头文件所在的路径(在.c源文件中所包含的头文件需要使用GCC的命令行来说明)。

Makefile中如果连续的多个vpath语句中使用了相同的“PATTERN”,make就对这些vpath语句一个一个进行处理,搜索某种模式文件的目录将是所有的通过vpath指定的符合此模式的目录,其搜索目录的顺序由vpath语句在Makefile出现的先后次序来决定。多个具有相同“PATTERN”的vpath语句之间相互独立。下边是两种方式下,所有的.c文件的查找目录的顺序(不包含工作目录,对工作目录的搜索永远处于最优先地位)比较:

vpath %.c foo

vpath % blish

vpath %.c bar

 

表示对所有的.c文件,make依次查找目录:“foo”、blish”、“bar”。

而:

vpath %.c foobar

vpath % blish

 

对于所有的.c文件make将依次查找目录:“foo”、“bar”、“blish

规则中一个依赖文件可以通过目录搜寻找到(使用前边提到的一般搜索或者是选择性搜索任一种),但是有可能此文件的完整路径名(文件的相对路径或者绝对路径,如:/home/Stallman/foo.c)却并不是规则中列出的依赖(规则“foo : foo.c”,在执行搜索后可能得到的依赖文件为:“../src/foo.c”。目录“../src”是使用“VPATH”或“vpath”指定的);因此使用目录搜索所到的完整的文件路径名可能需要废弃。make在解析Makefile文件执行规则时对文件路径保存或废弃所依据的算法如下:

1.        首先,如果规则的目标文件在Makefile文件所在的目录(工作目录)下不存在,那么就执行目录搜寻。

2.        如果目录搜寻成功,在指定的目录下存在此规则的目标。那么搜索到的完整的路径名就被作为临时的目标文件被保存。

3.        对于规则中的所有依赖文件使用相同的方法处理。

4.        完成第三步的依赖处理后,make程序就可以决定规则的目标是否需要重建,两种情况时后续处理如下:

a)       规则的目标不需要重建:那么通过目录搜索得到的所有完整的依赖文件路径名有效,同样,规则的目标文件的完整的路径名同样有效。就是说,当规则的目标不需要被重建时,规则中的所有的文件完整的路径名有效。已经存在的目标文件所在的目录不会被改变。

b)       规则的目标需要重建:那么通过目录搜索所得到的目标文件的完整的路径名无效,规则中的目标文件将会被在工作目录下重建。就是说,当规则的目标需要重建时,规则的目标文件会在工作目录下被重建,而不是在目录搜寻时所得到的目录。这里,必须明确:此种情况只有目标文件的完整路径名失效,依赖文件的完整路径名是不会失效的。否则将无法重建目标。

该算法看起来比较法杂,但它确实使make实现了我们所需要的东西。此算法使用纯粹的语言描述可能显得晦涩。本小节后续将使用一个例子来说明。使大家能够对此算法有明确的理解。对于其他版本的make则使用了一种比较简单的算法:如果规则的目标文件的完整路径名存在(通过目录搜索可以定位到目标文件),无论该目标是否需要重建,都使用搜索到的目标文件完整路径名。

实际上,GNU make也可以实现这种功能。如果需要make在执行时,将目标文件在已存在的目录存下进行重建,我们可以使用“GPATH”变量来指定这些目标所在的目录。“GPATH”变量和“VPATH”变量具有相同的语法格式。make在执行时,如果通过目录搜寻得到一个过时的完整的目标文件路径名,而目标存在的目录又出现在“GPATH”变量的定义列表中,则该目标的完整路径将不废弃,目标将在该路径下被重建。

为了更清楚地描述此算法,我们使用一个例子来说明。存在一个目录“prom”,“prom”的子目录“src”下存在“sum.c”和“memcp.c”两个源文件。在“prom”目录下的Makefile部分内容如下:

 

LIBS = libtest.a

VPATH = src

 

libtest.a : sum.o memcp.o

           $(AR) $(ARFLAGS) $@ $^

 

首先,如果在两个目录(“prom”和“src”)都不存在目标“libtest.a”,执行make时将会在当前目录下创建目标文件“libtest.a”。另外;如果“src”目录下已经存在“libtest.a”,以下两种不同的执行结果:

1)       当它的两个依赖文件“sum.c”和“memcp.c”没有被更新的情况下我们执行make,首先make程序会搜索到目录“src”下的已经存在的目标“libtest.a”。由于目标“libtest.a”的依赖文件没有发生变化,所以不会重建目标。并且目标所在的目录不会发生变化。

2)       当我们修改了文件“sum.c”或者“memcp.c”以后执行make。“libtest.a”和“sum.o”或者“memcp.o”文件将会被在当前目录下创建(目标完整路径名被废弃),而不是在“src”目录下更新这些已经存在的文件。此时在两个目录下(“prom”和“src”)同时存在文件“libtest.a”。但只有“prom/libtest.a”是最新的库文件。

当在上边的Makefile文件中使用“GPATH”指定目录时,情况就不一样了。首先看看怎么使用“GPATH”,改变后的Makefile内容如下:

LIBS = libtest.a

GPATH = src

VPATH = src

LDFLAGS += -L ./. –ltest

…….

……

 

同样;当两个目录都不存在目标文件“libtest.a”时,目标将会在当前目录(“prom”目录)下创建。如果“src”目录下已经存在目标文件“libtest.a”。当其依赖文件任何一个被改变以后执行make,目标“libtest.a”将会被在“src”目录下被更新(目标完整路径名不会被废弃)。

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