bug fixed

This commit is contained in:
Dongdong Tian 2014-03-07 14:46:29 +08:00
parent aadc6931a3
commit e1fcd42fcd

View File

@ -195,24 +195,24 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
定义模式规则
------------
你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有“%”字符。“%的意思是表示一个或多个任意字符。在依赖目标中同样可以使用“%”,只是依赖目标中的“%的取值,取决于其目标。
你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有“%”字符。“%的意思是表示一个或多个任意字符。在依赖目标中同样可以使用“%”,只是依赖目标中的“%的取值,取决于其目标。
有一点需要注意的是,“%的展开发生在变量和函数的展开之后变量和函数的展开发生在make载入Makefile时而模式规则中的“%则发生在运行时。
有一点需要注意的是,“%的展开发生在变量和函数的展开之后变量和函数的展开发生在make载入Makefile时而模式规则中的“%则发生在运行时。
模式规则介绍
~~~~~~~~~~~~
模式规则中,至少在规则的目标定义中要包含“%“,否则,就是一般的规则。目标中的“%“定义表示对文件名的匹配,“%“表示长度任意的非空字符串。例如:“%.c“表示以“.c“结尾的文件名文件名的长度至少为3而“s.%.c“则表示以“s.“开头,“.c“结尾的文件名文件名的长度至少为5
模式规则中,至少在规则的目标定义中要包含“%”,否则,就是一般的规则。目标中的“%”定义表示对文件名的匹配,“%”表示长度任意的非空字符串。例如:“%.c”表示以“.c”结尾的文件名文件名的长度至少为3而“s.%.c”则表示以“s.”开头,“.c”结尾的文件名文件名的长度至少为5
如果“%“定义在目标中,那么,目标中的“%“的值决定了依赖目标中的“%“的值,也就是说,目标中的模式的“%“决定了依赖目标中“%“的样子。例如有一个模式规则如下:
如果“%”定义在目标中,那么,目标中的“%”的值决定了依赖目标中的“%”的值,也就是说,目标中的模式的“%”决定了依赖目标中“%”的样子。例如有一个模式规则如下:
.. code-block:: makefile
%.o : %.c ; <command ......>;
其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是“a.o b.o“,那么“%c“就是“a.c b.c“
其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是“a.o b.o”,那么“%c”就是“a.c b.c”
一旦依赖目标中的“%模式被确定那么make会被要求去匹配当前目录下所有的文件名一旦找到make就会规则下的命令所以在模式规则中目标可能会是多个的如果有模式匹配出多个目标make就会产生所有的模式目标此时make关心的是依赖的文件名和生成目标的命令这两件事。
一旦依赖目标中的“%模式被确定那么make会被要求去匹配当前目录下所有的文件名一旦找到make就会规则下的命令所以在模式规则中目标可能会是多个的如果有模式匹配出多个目标make就会产生所有的模式目标此时make关心的是依赖的文件名和生成目标的命令这两件事。
模式规则示例
~~~~~~~~~~~~
@ -224,7 +224,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
其中,“$@“表示所有的目标的挨个值,“$<“表示了所有依赖目标的挨个值。这些奇怪的变量我们叫“自动化变量“,后面会详细讲述。
其中,“$@”表示所有的目标的挨个值,“$<”表示了所有依赖目标的挨个值。这些奇怪的变量我们叫“自动化变量”,后面会详细讲述。
下面的这个例子中有两个目标是模式的:
@ -233,7 +233,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
%.tab.c %.tab.h: %.y
bison -d $<
这条规则告诉make把所有的[.y]文件都以“bison -d <n>.y“执行,然后生成“<n>. tab.c“和“<n>.tab.h“文件。其中“<n>“表示一个任意字符串。如果我们的执行程序“foo“依赖于文件“parse.tab.o“和“scan.o“并且文件“scan.o“依赖于文件“parse.tab.h“如果“parse.y“文件被更新了那么根据上述的规则“bison -d parse.y“就会被执行一次于是“parse.tab.o“和“scan.o“的依赖文件就齐了。假设“parse.tab.o“由“parse.tab.c“生成和“scan.o“由“scan.c“生成而“foo“由 “parse.tab.o“和“scan.o“链接生成而且foo和其[.o]文件的依赖关系也写好,那么,所有的目标都会得到满足)
这条规则告诉make把所有的[.y]文件都以“bison -d <n>.y”执行,然后生成“<n>.tab.c”和“<n>.tab.h”文件。其中“<n>”表示一个任意字符串。如果我们的执行程序“foo”依赖于文件“parse.tab.o”和“scan.o”并且文件“scan.o”依赖于文件“parse.tab.h”如果“parse.y”文件被更新了那么根据上述的规则“bison -d parse.y”就会被执行一次于是“parse.tab.o”和“scan.o”的依赖文件就齐了。假设“parse.tab.o”由“parse.tab.c”生成和“scan.o”由“scan.c”生成而“foo”由“parse.tab.o”和“scan.o”链接生成而且foo和其[.o]文件的依赖关系也写好,那么,所有的目标都会得到满足)
自动化变量
~~~~~~~~~~
@ -244,33 +244,33 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
下面是所有的自动化变量及其说明:
- $@: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,“$@就是匹配于目标中模式定义的集合。
- $%: 仅当目标是函数库文件中表示规则中的目标成员名。例如如果一个目标是“foo.a(bar.o)“,那么,“$%“就是 “bar.o““$@“就是“foo.a“。如果目标不是函数库文件Unix下是[.a]Windows下是[.lib]),那么,其值为空。
- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即“%“)定义的,那么“$<“将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
- $@: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,“$@就是匹配于目标中模式定义的集合。
- $%: 仅当目标是函数库文件中表示规则中的目标成员名。例如如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”“$@”就是“foo.a”。如果目标不是函数库文件Unix下是[.a]Windows下是[.lib]),那么,其值为空。
- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即“%”)定义的,那么“$<”将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
- $?: 所有比目标新的依赖目标的集合。以空格分隔。
- $^:所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
- $+:这个变量很像“$^,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
- $*: 这个变量表示目标模式中“%“及其之前的部分。如果目标是“dir/a.foo.b“并且目标的模式是“a.%.b“那么“$*“的值就是“dir/a.foo“。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*也就不能被推导出但是如果目标文件的后缀是make所识别的那么“$*“就是除了后缀的那一部分。例如如果目标是“foo.c“因为“.c“是make所能识别的后缀名所以“ $*“的值就是“foo“。这个特性是GNU make的很有可能不兼容于其它版本的make所以你应该尽量避免使用“$*除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的那么“$*就是空值。
- $+:这个变量很像“$^,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
- $*: 这个变量表示目标模式中“%”及其之前的部分。如果目标是“dir/a.foo.b”并且目标的模式是“a.%.b”那么“$*”的值就是“dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*也就不能被推导出但是如果目标文件的后缀是make所识别的那么“$*”就是除了后缀的那一部分。例如如果目标是“foo.c”因为“.c”是make所能识别的后缀名所以“$*”的值就是“foo”。这个特性是GNU make的很有可能不兼容于其它版本的make所以你应该尽量避免使用“$*除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的那么“$*就是空值。
当你希望只对更新过的依赖文件进行操作时,“$?“在显式规则中很有用例如假设有一个函数库文件叫“lib“其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是
当你希望只对更新过的依赖文件进行操作时,“$?”在显式规则中很有用例如假设有一个函数库文件叫“lib”其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是
.. code-block:: makefile
lib : foo.o bar.o lose.o win.o
ar r lib $?
在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*在扩展时只会有一个文件而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名只需要搭配上“D“或“F“字样。这是GNU make中老版本的特性在新版本中我们使用函数“dir“或“notdir“就可以做到了。“D“的含义就是Directory就是目录“F“的含义就是File就是文件。
在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*在扩展时只会有一个文件而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名只需要搭配上“D”或“F”字样。这是GNU make中老版本的特性在新版本中我们使用函数“dir”或“notdir”就可以做到了。“D”的含义就是Directory就是目录“F”的含义就是File就是文件。
下面是对于上面的七个变量分别加上“D“或是“F“的含义:
下面是对于上面的七个变量分别加上“D”或是“F”的含义:
``$(@D)``
表示“$@“的目录部分(不以斜杠作为结尾),如果“$@“值是“dir/foo.o“那么“$(@D)“就是“dir“而如果“$@“中没有包含斜杠的话,其值就是“.“(当前目录)。
表示“$@”的目录部分(不以斜杠作为结尾),如果“$@”值是“dir/foo.o”那么“$(@D)”就是“dir”而如果“$@”中没有包含斜杠的话,其值就是“.”(当前目录)。
``$(@F)``
表示“$@“的文件部分,如果“$@“值是“dir/foo.o“那么“$(@F)“就是“foo.o““$(@F)“相当于函数“$(notdir $@)“
表示“$@”的文件部分,如果“$@”值是“dir/foo.o”那么“$(@F)”就是“foo.o”“$(@F)”相当于函数“$(notdir $@)”
``$(*D)``, ``$(*F)``
和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,\ ``$(*D)``\ 返回“dir“,而\ ``$(*F)``\ 返回“foo“
和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,\ ``$(*D)``\ 返回“dir”,而\ ``$(*F)``\ 返回“foo”
``$(%D)``, ``$(%F)``
分别表示了函数包文件成员的目录部分和文件部分。这对于形同“archive(member)“形式的目标中的“member“中包含了不同的目录很有用。
@ -287,17 +287,16 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
``$(?D)``, ``$(?F)``
分别表示被更新的依赖文件的目录部分和文件部分。
最后想提醒一下的是,对于“$<,为了避免产生不必要的麻烦,我们最好给$后面的那个特定字符都加上圆括号,比如,“$(<)“就要比“$<“要好一些。
最后想提醒一下的是,对于“$<,为了避免产生不必要的麻烦,我们最好给$后面的那个特定字符都加上圆括号,比如,“$(<)”就要比“$<”要好一些。
还得要注意的是,这些变量只使用在规则的命令中,而且一般都是“显式规则“和“静态模式规则“(参见前面“书写规则“一章)。其在隐含规则中并没有意义。
还得要注意的是,这些变量只使用在规则的命令中,而且一般都是“显式规则”和“静态模式规则”(参见前面“书写规则”一章)。其在隐含规则中并没有意义。
模式的匹配
~~~~~~~~~~
一般来说,一个目标的模式有一个有前缀或是后缀的“%“,或是没有前后缀,直接就是一个“%“。因为“%“代表一个或多个字符,所以在定义好了的模式中,我们把“%“所匹配的内容叫做“茎“,例如“%.c“所匹配的文件“test.c“中“test“就是“茎“。因为在目标和依赖目标中同时有“% “时,依赖目标的“茎“会传给目标,当做目标中的“茎“。
当一个模式匹配包含有斜杠实际也不经常包含的文件时那么在进行模式匹配时目录部分会首先被移开然后进行匹配成功后再把目录加回去。在进行“茎“的传递时我们需要知道这个步骤。例如有一个模式“e%t“文件“src/eat“匹配于该模式于是“src/a“就是其“茎“如果这个模式定义在依赖目标中而被依赖于这个模式的目标中又有个模式“c%r“那么目标就是“src/car“。“茎“被传递
一般来说,一个目标的模式有一个有前缀或是后缀的“%”,或是没有前后缀,直接就是一个“%”。因为“%”代表一个或多个字符,所以在定义好了的模式中,我们把“%”所匹配的内容叫做“茎”,例如“%.c”所匹配的文件“test.c”中“test”就是“茎”。因为在目标和依赖目标中同时有“%”时,依赖目标的“茎”会传给目标,当做目标中的“茎”。
当一个模式匹配包含有斜杠实际也不经常包含的文件时那么在进行模式匹配时目录部分会首先被移开然后进行匹配成功后再把目录加回去。在进行“茎”的传递时我们需要知道这个步骤。例如有一个模式“e%t”文件“src/eat”匹配于该模式于是“src/a”就是其“茎”如果这个模式定义在依赖目标中而被依赖于这个模式的目标中又有个模式“c%r”那么目标就是“src/car”。“茎”被传递
重载内建隐含规则
~~~~~~~~~~~~~~~~
@ -317,14 +316,14 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
同样,你也可以重新定义一个全新的隐含规则,其在隐含规则中的位置取决于你在哪里写下这个规则。朝前的位置就靠前。
老式风格的“后缀规则
老式风格的“后缀规则
--------------------
后缀规则是一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容GNU make同样兼容于这些东西。后缀规则有两种方式“双后缀“和“单后缀“
后缀规则是一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容GNU make同样兼容于这些东西。后缀规则有两种方式“双后缀”和“单后缀”
双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如“.c.o“相当于“%o : %c“。单后缀规则只定义一个后缀也就是源文件的后缀。如“.c“相当于“% : %.c“
双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如“.c.o”相当于“%o : %c”。单后缀规则只定义一个后缀也就是源文件的后缀。如“.c”相当于“% : %.c”
后缀规则中所定义的后缀应该是make所认识的如果一个后缀是make所认识的那么这个规则就是单后缀规则而如果两个连在一起的后缀都被 make所认识那就是双后缀规则。例如“.c“和“.o“都是make所知道。因而如果你定义了一个规则是“.c.o“那么其就是双后缀规则意义就是“.c“是源文件的后缀“.o“是目标文件的后缀。如下示例:
后缀规则中所定义的后缀应该是make所认识的如果一个后缀是make所认识的那么这个规则就是单后缀规则而如果两个连在一起的后缀都被make所认识那就是双后缀规则。例如“.c”和“.o”都是make所知道。因而如果你定义了一个规则是“.c.o”那么其就是双后缀规则意义就是“.c”是源文件的后缀“.o”是目标文件的后缀。如下示例:
.. code-block:: makefile
@ -338,7 +337,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
.c.o: foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
这个例子,就是说,文件“.c.o“依赖于文件“foo.h“,而不是我们想要的这样:
这个例子,就是说,文件“.c.o”依赖于文件“foo.h”,而不是我们想要的这样:
.. code-block:: makefile
@ -347,7 +346,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
后缀规则中,如果没有命令,那是毫无意义的。因为他也不会移去内建的隐含规则。
而要让make知道一些特定的后缀我们可以使用伪目标“.SUFFIXES来定义或是删除,如:
而要让make知道一些特定的后缀我们可以使用伪目标“.SUFFIXES来定义或是删除,如:
.. code-block:: makefile
@ -362,22 +361,23 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
先清楚默认后缀,后定义自己的后缀列表。
make的参数“-r“或“-no-builtin-rules“也会使用得默认的后缀列表为空。而变量“SUFFIXE“被用来定义默认的后缀列表你可以用“.SUFFIXES“来改变后缀列表但请不要改变变量“SUFFIXE“的值。
make的参数“-r”或“-no-builtin-rules”也会使用得默认的后缀列表为空。而变量“SUFFIXE”被用来定义默认的后缀列表你可以用“.SUFFIXES”来改变后缀列表但请不要改变变量“SUFFIXE”的值。
隐含规则搜索算法
----------------
比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意在下面我们没有提到后缀规则原因是所有的后缀规则在Makefile被载入内存时会被转换成模式规则。如果目标是“archive(member)的函数库文件模式那么这个算法会被运行两次第一次是找目标T如果没有找到的话那么进入第二次第二次会把“member”当作T来搜索。
比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意在下面我们没有提到后缀规则原因是所有的后缀规则在Makefile被载入内存时会被转换成模式规则。如果目标是“archive(member)的函数库文件模式那么这个算法会被运行两次第一次是找目标T如果没有找到的话那么进入第二次第二次会把“member”当作T来搜索。
#. 把T的目录部分分离出来。叫D而剩余部分叫N。如果T是“src/foo.o那么D就是“src/“N就是“foo.o“
#. 把T的目录部分分离出来。叫D而剩余部分叫N。如果T是“src/foo.o那么D就是“src/”N就是“foo.o”
#. 创建所有匹配于T或是N的模式规则列表。
#. 如果在模式规则列表中有匹配所有文件的模式,如“%,那么从列表中移除其它的模式。
#. 如果在模式规则列表中有匹配所有文件的模式,如“%,那么从列表中移除其它的模式。
#. 移除列表中没有命令的规则。
#. 对于第一个在列表中的模式规则:
#. 推导其“茎“SS应该是T或是N匹配于模式中“%“非空的部分。
#. 计算依赖文件。把依赖文件中的“%“都替换成“茎“S。如果目标模式中没有包含斜框字符而把D加在第一个依赖文件的开头。
#. 测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则的依赖文件,那么这个文件就叫“理当存在“)
#. 推导其“茎”SS应该是T或是N匹配于模式中“%”非空的部分。
#. 计算依赖文件。把依赖文件中的“%”都替换成“茎”S。如果目标模式中没有包含斜框字符而把D加在第一个依赖文件的开头。
#. 测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则
的依赖文件,那么这个文件就叫“理当存在”)
#. 如果所有的依赖文件存在或是理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。
#. 如果经过第5步没有模式规则被找到那么就做更进一步的搜索。对于存在于列表中的第一个模式规则
@ -388,6 +388,7 @@ make的参数“-r“或“-no-builtin-rules“也会使用得默认的后缀列
#. 对于不存在的依赖文件,递归调用这个算法查找他是否可以被隐含规则找到。
#. 如果所有的依赖文件存在或是理当存在,或是就根本没有依赖文件。那么这条规则被采用,退出该算法。
#. 如果没有隐含规则可以使用,查看“.DEFAULT“规则如果有采用把“.DEFAULT“的命令给T使用。
#. 如果没有隐含规则可以使用,查看“.DEFAULT”规则如果有采用把“.DEFAULT”的命令给T使用。
一旦规则被找到,就会执行其相当的命令,而此时,我们的自动化变量的值才会生成。