type
status
date
slug
summary
tags
category
icon
password
URL
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。
好了,还是让我们来看一看如何书写规则。
一、规则举例
看到这个例子,各位应该不是很陌生了,前面也已说过,
foo.o
是我们的目标, foo.c
和 defs.h
是目标所依赖的源文件,而只有一个命令 cc -c -g foo.c
(以Tab键开头)。这个规则告诉我们两件事:- 文件的依赖关系,
foo.o
依赖于foo.c
和defs.h
的文件,如果foo.c
和defs.h
的文件日期要比foo.o
文件日期要新,或是foo.o
不存在,那么依赖关系发生。
- 生成或更新
foo.o
文件,就是那个cc命令。它说明了如何生成foo.o
这个文件。(当然,foo.c文件include了defs.h文件)

从整个实验就可以看出,文件依赖的规则。我只修改了hello.c的内容,在编译出helloworld的时候,只有hello.o被重新编译了,而world.o没有更改。
二、规则的语法
或者这样:
targets是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。
command是命令行,如果其不与“target:prerequisites”在一行,那么,必须以
Tab
键开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上)prerequisites也就是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,那么,目标就被认为是“过时的”,被认为是需要重生成的。这个在前面已经讲过了。
如果命令太长,你可以使用反斜杠(
\
)作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和如何生成目标文件。一般来说,make会以UNIX的标准Shell,也就是
/bin/sh
来执行命令。三、通配符
如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三个通配符:
*
, ?
和 ~
。这是和Unix的B-Shell是相同的。波浪号(
~
)字符在文件名中也有比较特殊的用途。如果是 ~/test
,这就表示当前用户的 $HOME
目录下的test目录。而 ~hchen/test
则表示用户hchen的宿主目录下的test 目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是 MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量“HOME”而定。通配符代替了你一系列的文件,如
*.c
表示所有后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如: *
,那么可以用转义字符 \
,如 \*
来表示真实的 *
字符,而不是任意长度的字符串。下面是一个变量使用通配符的例子
$(wildcard *.c) : 列出一个确定文件夹下的所有
.c
文件$(patsubst %.c, %.o, $(wildcard *.c)) : 将列出来的
.c
文件获得对应的.o
文件这种用法由关键字“wildcard”,“patsubst”指出,关于Makefile的关键字,我们将在后面讨论。
四、文件搜寻
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。
Makefile文件中的特殊变量
VPATH
就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了。实例:

输出结果:

在Makefile中并没有指定hello.c的路径,但是在编译时,因为设置了VPATH,编译器会在src目录下寻找hello.c
另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:
vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>。
vpath <pattern>
清除符合模式<pattern>的文件的搜索目录。
vpath
清除所有已被设置好了的文件搜索目录。
vpath使用方法中的<pattern>需要包含
%
字符。 %
的意思是匹配零或若干字符,(需引用 %
,使用 \
)例如, %.h
表示所有以 .h
结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了< pattern>的文件集的搜索的目录。例如:
五、伪目标
“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显式地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。
只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。
伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性:

六、多目标
Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。当然,多个目标的生成规则的执行命令不是同一个,这可能会给我们带来麻烦,不过好在我们可以使用一个自动化变量
$@
(关于自动化变量,将在后面讲述),这个变量表示着目前规则中所有的目标的集合,这样说可能很抽象,还是看一个例子吧。上述规则等价于:
其中,
-$(subst output,,$@)
中的 $
表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是替换字符串的意思, $@
表示目标的集合,就像一个数组, $@
依次取出目标,并执于命令。
备注:函数subst在后续的函数篇讲。
七、静态模式
静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。
argets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-pattern是指明了targets的模式,也就是的目标集模式。
prereq-patterns是目标的依赖模式,它对target-pattern形成的模式再进行一次依赖目标的定义。
这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的<target-pattern>定义成
%.o
,意思是我们的<target>;集合中都是以 .o
结尾的,而如果我们的<prereq-patterns>定义成 %.c
,意思是对<target-pattern>所形成的目标集进行二次定义,其计算方法是,取<target-pattern>模式中的 %
(也就是去掉了 .o
这个结尾),并为其加上 .c
这个结尾,形成的新集合。所以,我们的“目标模式”或是“依赖模式”中都应该有
%
这个字符,如果你的文件名中有 %
那么你可以使用反斜杠 \
进行转义,来标明真实的 %
字符。看一个例子:

上面这个例子中,指明了目标从
$obj
中获取,%.o
表明要所有以.o
结尾的目标,也就是helloworld.o和hellolinux.o,而依赖模式%.c
则取模式%.o
的%
,也就是helloworld,hellolinux,并为其加上.c
的后缀,于是,依赖的目标就是helloworld.c和hellolinux.c。而命令中的$<
和$@
则是自动化变量,$<
表示第一个依赖文件,$@
表示目标集(helloworld.o hellolinux.o)。所以上面的规则等价于下面的规则所以这种静态模式规则就很有效率,假设存在很多
%.o
的情况下,一条指令就可以代替N个规则