上篇文章是从网上转载过来的,内容基本上与《跟我一起写Makefile》一样,接下来的这篇文章主要对上篇中的特别点进行分析
1:Makefile使用变量
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
用objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o 定义一个变量,与C语言中宏定义类似,于是如果有新的 .o 文件加入,我们只需简单地修改一下 objects 变量就可以了。
2:Makefile自动推导
只要 make 看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果 make找到一个 whatever.o,那么 whatever.c,就会是 whatever.o 的依赖文件。并且 cc -c whatever.c 也会被推导出
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
上述的*.o目标文件,其依赖的*.c文件就可以省略,靠Makefile自己推导,我们只需要把要依赖的.h文件写出来就行
3:清空目标规则
为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪目标”。
.PHONY : clean
只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。于是整个过程可以这样写:
clean:
rm edit $(objects)
更为稳健的做法是:
.PHONY : clean
clean :
-rm edit $(objects)
前面说过,.PHONY 意思表示 clean 是一个“伪目标”,。而在 rm 命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然,clean 的规则不要放在文件的开头,不然,这就会变成 make 的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean 从来都是放在文件的最后”。
4:注释
Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用“#”字符,这个就像 C/C++中的“//”一样。如果你要在你的 Makefile 中使用“#”字符,可以用反斜框进行转义,如:“\#”。
5:引用其它的 Makefile
在 Makefile 使用 include 关键字可以把别的 Makefile 包含进来,这很像 C 语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include 的语法是: include <filename>
filename 可以是当前操作系统 Shell 的文件模式(可以保含路径和通配符) 在 include前面可以有一些空字符,但是绝不能是[Tab]键开始。include 和<filename>可以用一个或多个空格隔开。举个例子,你有这样几个 Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了 e.mk 和 f.mk,那么,下面的语句:
include foo.make *.mk $(bar)
等价于:
include foo.make a.mk b.mk c.mk e.mk f.mk
make 命令开始时,会把找寻 include 所指出的其它 Makefile,并把其内容安置在当前的位。就好像 C/C++的#include 指令一样。如果文件都没有指定绝对路径或是相对路径的话,make 会在当前目录下首先寻找,如果当前目录下没有找到,那么,make 还会在下面的几个目录下找:
1、如果 make 执行时,有“-I”或“--include-dir”参数,那么 make 就会在这个参数 所指定的目录下去寻找。
2、如果目录<prefix>/include(一般是:/usr/local/bin 或/usr/include)存在的话,make 也会去找。如果有文件没有找到的话,make 会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成 makefile 的读取,make 会再重试这些没有找到,或是不能读取的文件,如果还是不行,make 才会出现一条致命信息。如果你想让 make不理那些无法读取的件,而继续执行,你可以在 include 前加一个减号“-”。
如: -include <filename>
其表示,无论 include 过程中出现什么错误,都不要报错继续执行。和其它版本 make
兼 容的相关命令是 sinclude,其作用和这一个是一样的。
6:在规则中使用通配符
如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make 支持三个通配符:“*”,“?”和“[...]”通配符代替了你一系列的文件,如“*.c”表示所以后缀为 c 的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“\”,如“\*”来表示真实的“*”字符,而不是任意长度的字符串
7:文件搜寻
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉 make,让 make 在自动去找。
Makefile 文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:../headers
上面的的定义指定两个目录,“src”和“../headers”,make 会按照这个顺序进行搜
索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)
另一个设置文件搜索路径的方法是使用 make 的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个 make 的关键字,这和上面提到的那个 VPATH 变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:
1、vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>。
2、vpath <pattern>
清除符合模式<pattern>的文件的搜索目录。
3、vpath
清除所有已被设置好了的文件搜索目录。
vapth 使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录。例如: vpath %.h ../headers 该语句表示,要求 make 在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)
8:自动化变量
所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。
下面是所有的自动化变量及其说明:
$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于 目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"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 的
当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用,例如,假设有一个函数库文件叫"lib",其由其它几个 object 文件更新。那么把 object 文件打包的比较有效率的 Makefile 规则是:
lib : foo.o bar.o lose.o win.o
ar r lib $?
9:静态模式
静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活
我们还是先来看一下语法:
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
....
targets 定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern 是指明了 targets 的模式,也就是的目标集模式。
prereq-parrterns 是目标的依赖模式,它对 target-parrtern 形成的模式再进行一次依赖目标的定义。
看一个例子:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
上面的例子中,指明了我们的目标从$object 中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object 集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。于是,上面的规则展开后等价于下面的规则:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o