现在的位置: 首页 > 综合 > 正文

GNU Make手册阅读笔记(3)– 第四章

2013年12月08日 ⁄ 综合 ⁄ 共 4644字 ⁄ 字号 评论关闭

第四章 Makefile的规则

1.makefile的终极目标



    除了

makefile

的“终极目标”所在的规则以外,其它规则的顺序在

makefile

文件中没有意义。“终极目标”就是当没有使用

make


命令行指定具体目标时,

make

默认的更新的哪一个目标。它是

makefile

文件中第一个规则的目标。如果在

makefile

中第一个规则有多个目标的话,那么多个目标中的第一个将会被作为

make

的“终极目标”。有两种情况的例外:

1.


目标名以点号“

.

”开始的并且其后不存在斜线“

/

”(“

./

”被认为是当前目录;“

../

”被认为是上一级目录);

2.


模式规则的目标。当这两种目标所在的规则是

Makefile

的第一个规则时,它们并不会被作为“终极目标”。

2."规则"的书写形式


通常规则的语法格式如下:



TARGETS :
PREREQUISITES


COMMAND


...



或者:



TARGETS :
PREREQUISITES ; COMMAND


COMMAND


...


1.       


规则的命令部分有两种书写方式:

       a.


命令可以和目标:依赖描述放在同一行。命令在依赖文件列表后并使用分号(;)和依赖文件列表分开。

       b.


命令在目标:依赖的描述的下一行,作为独立的命令行。当作为独立的命令行时此行必须以

[Tab]

字符开始。在

Makefile

中,在第一个规则之后出现的所有以

[Tab]

字符开始的行都会被当作命令来处理。


2.       

Makefile

中符号“

$

”有特殊的含义(表示变量或者函数的引用),在规则中需要使用符号“

$

”的地方,需要书写两个连续的(“

$$

”)。

     

3.       


前边已提到过,对于

Makefile

中一个较长的行,我们可以使用反斜线“

/

”将其书写到几个独立的物理行上。

    规则的中心思想是:目标文件的内容是由依赖文件文件决定,依赖文件的任何一处改动,将导致目前已经存在的目标文件的内容过期。

3.依赖的分类

    一类是在这些依赖文件被更新后,需要更新规则的目标;另一类是更新这些依赖的,可不需要更新规则的目标。我们把第二类称为:“

order-only

”依赖。书写规则时,“

order-only

”依赖使用管道符号“

|

”开始,作为目标的一个依赖文件。规则依赖列表中管道符号“

|

”左边的是常规依赖,管道符号右边的就是“

order-only

”依赖。这样的规则书写格式如下:



TARGETS :
NORMAL-PREREQUISITES | ORDER-ONLY-PREREQUISITES

order-only

”依赖的使用举例:

    LIBS =
libtest.a

foo : foo.c
| $(LIBS)

       $(CC)
$(CFLAGS) $< -o $@ $(LIBS)

make

在执行这个规则时,如果目标文件“

foo

”已经存在。当“

foo.c

”被修改以后,目标“

foo

”将会被重建,但是当“

libtest.a

”被修改以后。将不执行规则的命令来重建目标“

foo

”。


就是说,规则中依赖文件

$(LIBS)

只有在目标文件不存在的情况下,才会参与规则的执行。当目标文件存在时此依赖不会参与规则的执行过程。

4.通配符

Makefile

中统配符可以出现在以下两种场合:


1.       


可以用在规则的目标、依赖中,

make

在读取

Makefile

时会自动对其进行匹配处理(通配符展开);


2.       


可出现在规则的命令中,通配符的通配处理是在

shell

在执行此命令时完成的。


除这两种情况之外的其它上下文中,不能直接使用通配符。而是需要通过函数“

wildcard

”来实现。


如果规则的一个文件名包含统配字符(“

*

”、“

.

”等字符),在使用这样的文件时需要对文件名中的统配字符使用反斜线(

/

)进行转义处理。例如“

foo/*bar

”,在

Makefile

中它表示了文件“

foo*bar

”。


print: *.c


lpr -p $?


touch print



两点说明:

1.


上述的规则中目标“

print

”时一个空目标文件。(当前目录下存在一个文件“

print

”,但我们不关心它的实际内容,此文件的作用只是记录最后一次执行此规则的时间。

2.


自动环变量“

$?

”在这里表示依赖文件列表中被改变过的所有文件。


objects = *.o


foo :
$(objects)


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



这里变量“

objects

”的值是一个字符串“

*.o

”。在重建“

foo

”的规则中对变量“

objects

”进行展开,目标“

foo

”的依赖就是“

*.o

”,即所有的

.o

文件的列表。如果在工作目录下已经存在必需的

.o

文件,那么这些

.o

文件将成为目标的依赖文件,目标“

foo

”将根据规则被重建。

但是如果将工作目录下所有的

.o

文件删除,重新执行

make

将会得到一个类似于“没有创建

*.o

文件的规则”



的错误提示。


一般我们可以使用“

$(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.目录搜寻

1)

一般搜索(变量

VPATH

        GNU make

可以识别一个特殊变量“

VPATH

”。通过变量“

VPATH

”可以指定依赖文件的搜索路径,当规则的依赖文件在当前目录不存在时,

make

会在此变量所指定的目录下去寻找这些依赖文件。

        VPATH = src:../headers

2)


选择性搜索(关键字

vpath


1

vpath
PATTERN DIRECTORIES


为所有符合模式“

PATTERN

”的文件指定搜索目录“

DIRECTORIES

”。多个目录使用空格或者冒号(:)分开。类似上一小节的“

VPATH

”变量。

2

vpath
PATTERN


清除之前为符合模式“

PATTERN

”的文件设置的搜索路径。

3

vpath

           

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


vpath %.c
foo

vpath %
blish

vpath %.c
bar

           

表示对所有的

.c

文件,

make

依次查找目录:“

foo

”、“

blish

”、“

bar

”。

3)

目录搜索的机制


1.       


首先,如果规则的目标文件在

Makefile

文件所在的目录(工作目录)下不存在,那么就执行目录搜寻。


2.       


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


3.       


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


4.       


完成第三步的依赖处理后,

make

程序就可以决定规则的目标是否需要重建,两种情况时后续处理如下:


a)      


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


b)      


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

实际上,

GNU
make

也可以实现这种功能。如果需要

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

”目录下被更新(目标完整路径名不会被废弃)。

4)

命令行和搜索目录


make

在执行时,通过目录搜索得到的目标的依赖文件可能会在其它目录(此时依赖文件为文件的完整路径名),但是已经存在的规则命令却不能发生变化。因此,书写命令时我们必须保证当依赖文件在其它目录下被发现时规则的命令能够正确执行。


解决这个问题的方式是在规则的命令行中使用“自动化变量”

所以对于一个规则我们可以进行如下的描述:


foo.o : foo.c


cc -c $(CFLAGS)
$^ -o $@


    自动化变量“

$<

”代表规则中通过目录搜索得到的依赖文件列表的第一个依赖文件。

    自动化变量“

$^

”代表所有通过目录搜索得到的依赖文件的完整路径名(目录


+


一般文件名)列表。

    “

$@

”代表规则的目标。

5)

库文件和搜索目录

        1.
make

在执行规则时会在当前目录下搜索一个名字为“

libNAME.so

”的文件;

        2.


如果当前工作目录下不存在这样一个文件,则

make

会继

抱歉!评论已关闭.