在阅读大型工程C源码时不可避免的须要了解makefile文件,它定义了源文件的编译规则和连接规则,是阅读与编写源码都必须了解的知识,本文经过学习陈皓写的一份makefile中文教程,将其要点梳理以下,以备本身回顾之用。原始教程请参考陈皓博客或直接下载网友作好的PDF教程,直接百度便可。web
1、概念shell
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件须要先编译,哪些文件须要后编译,哪些文件须要从新编译,甚至于进行更复杂的功能操做。express
make 是一个命令工具,是一个解释 makefile 中指令的命令工具。数组
2、语法ide
2.1格式与组成函数
格式以下:工具
target ... : prerequisites ...
command
说明:这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。说白一点就是说, prerequisites 中若是有一个以上的文件比 target 文件要新的话,command 所定义的命令就会被执行。这就是 Makefile 的规则。命令必须以tab开头。学习
组成:测试
Makefile 里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。显式规则就是咱们定义出来的,隐晦规则即自动推导规则(make 看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中),支持变量定义相似于宏替换,注释提升可读性,文件指示包括:引入其余makefile文件,指定makefile的有效部分,定义一个多行命令。ui
2.2规则
2.2.1规则的语法
targets : prerequisites
command
...
或是这样:
targets : prerequisites ; command
command
2.2.2 文件搜索
(1)当 make 须要去找寻文件的依赖关系时,你能够在文件前加上路径,但最好的方法是把一个路径告诉 make,让make 自动去找。特殊变量“VPATH”就是完成这个功能的(如:VPATH = src:../headers,目录用冒号分隔),make在当前目录找不到就到变量VPATH路径下找。
(2)另外一个设置文件搜索路径的方法是使用 make 的“vpath”关键字 (注意,它是全小写的),格式为:vpath <pattern> <directories>,表示在directories下搜索符合pattern模式的文件。其中pattern须要使用%符号,用于表示匹配0或若干个字符。
2.2.3 多目标与静态模式
Makefile 的规则中的目标能够不止一个,其支持多目标。固然,多个目标的生成规则的执行命令是同一个,不过好在咱们的可使用一个自动化变量“$@”,它表示目前规则中全部目标的集合。示例参见第三节例1.
静态模式能够更加容易地定义多目标的规则,语法以下
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...
target-parrtern 指明了 targets 的模式,也就是的目标集模式。prereq-parrterns 是目标的依赖模式,它对 target-parrtern 造成的模式再进行一次依赖目标的定义。参见第三节例2。
2.2.4 自动生成依赖性
在添加删除头文件时,须要修改makefile文件内容,这是繁琐且易错的,因此大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。而为了将编译器自动生成的依赖关系文件与makefile关联起来,GNU 组织建议把编译器为每个源文件自动生成的依赖关系放到一个文件中,如为“name.c”生成一个“name.d”的 Makefile 文件,[.d]文件中存放着对应[.c]文件的依赖关系,以后将.d文件包含在咱们的主 Makefile 中,从而自动化地生成每一个文件的依赖性。
产生[.d]文件的模式规则见第三节例3。
2.2.5命令的关联
若是但愿第二条命令在第一条命令的基础上执行,那么这两条命令应该写在同一行,用分号分隔,而不能写在两行。
2.2.6 隐含规则
“隐含规则”是一种惯例,若是咱们不明确地写下规则,那么,make 就会在这些内建的规则中寻找所须要的规则和命令。“隐含规则”会使用一些咱们系统变量,咱们能够改变这些系统变量的值来定制隐含规则的运行时的参数;咱们还能够经过“模式规则”的方式写下本身的隐含规则。若是你确实不但愿任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令。
(1)隐含规则中所用到的变量
在隐含规则中的命令,基本上都是使用了一些预先设置的变量。一、你能够在你的 makefile 中改变这些变量的值,二、或是在 make 的命令行中传入这些值,三、或是在你的环境变量中设置这些值,固然,四、你也能够利用 make 的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的做用。
Make为不一样语言创建了不一样的编译连接隐含规则,而这些规则都有自身所用到的命令变量和命令参数变量,经过改变这些变量的值能够改变隐含规则的执行方式。
下面列出常见隐含规则:
语言 |
推导规则 |
生成的命令 |
编译C |
“<n>.o”目标的依赖目标会自动推导为“<n>.c” |
$(CC) –c $(CPPFLAGS) $(CFLAGS) |
编译 C++ |
“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C” |
$(CXX) –c (CPPFLAGS) $(CFLAGS) |
编译 Pascal |
“<n>.o”的目标的依赖目标会自动推导为“<n>.p” |
$(PC) –c $(PFLAGS) |
编译 Fortran /Ratfor |
“<n>.o”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”或“<n>.f” |
“.f” “$(FC) –c $(FFLAGS)” “.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)” “.f” “$(FC) –c $(FFLAGS) $(RFLAGS)” |
预处理 Fortran/Ratfor |
“<n>.f”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F” |
“.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)” “.r” “$(FC) –F $(FFLAGS) $(RFLAGS)” |
编译 Modula-2 |
“<n>.sym”的目标的依赖目标会自动推导为“<n>.def” “<n.o>” 的目标的依赖目标会自动推导为 “<n>.mod” |
$(M2C) $(M2FLAGS) $(DEFFLAGS)
$(M2C) $(M2FLAGS) $(MODFLAGS) |
汇编和汇编预处理 |
“<n>.o” 的目标的依赖目标会自动推导为“<n>.s” 默认使用 C 预编译器“cpp” |
$(AS) $(ASFLAGS)
$(AS) $(ASFLAGS) |
连接 Object 文件 |
“<n>”目标依赖于“<n>.o”,经过运行 C 的编译器来运行连接程序生成( 一 般 是 “ld” ) |
$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS) |
…… |
|
|
下面列出隐含规则中所用到的变量:包括命令变量和参数变量两种
命令变量 |
说明 |
命令参数变量 |
说明(没指明默认值为空) |
AR |
函数库打包程序 |
ARFLAGS |
AR 命令的参数。默认值是“rv”。 |
AS |
汇编语言编译程序 |
ASFLAGS |
汇编语言编译器参数。 |
CC |
C 语言编译程序 |
CFLAGS |
C 语言编译器参数。 |
CXX |
C++语言编译程序 |
CXXFLAGS |
C++语言编译器参数。 |
CO |
从 RCS 文件中扩展文件程序 |
COFLAGS |
RCS 命令参数。 |
CPP |
C 程序的预处理器 |
CPPFLAGS |
C /Pascal预处理器参数。 |
FC |
Fortran 和 Ratfor 的编译器和预处理程序 |
FFLAGS |
Fortran 语言编译器参数。 |
GET |
从 SCCS 文件中扩展文件的程序 |
GFLAGS |
SCCS “get”程序参数。 |
PC |
Pascal 语言编译程序 |
PFLAGS |
Pascal 语言编译器参数。 |
LEX |
Lex 方法分析器程序(针对于 C 或 Ratfor) |
LFLAGS |
Lex 文法分析器参数。 |
YACC |
Yacc 文法分析器(针对于 C 程序) |
YFLAGS |
Yacc 文法分析器参数。 |
YACCR |
Yacc 文法分析器(针对于 Ratfor 程序) |
|
|
TEX |
从 TeX 源文件建立 TeX DVI 文件的程序 |
|
|
RM |
删除文件命令 |
|
|
… |
|
LDFLAGS |
连接器参数。(如:“ld”) |
(2)模式规则
可使用模式规则来定义一个隐含规则。模式规则中,至少在规则的目标定义中要包含"%",不然,就是通常的规则。若是"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值。示例以下:
%.tab.c %.tab.h: %.y
bison -d $<
说明:这条规则告诉 make 把全部的[.y]文件都以"bison -d <n>.y"执行,而后生成"<n>.tab.c"和"<n>.tab.h"文件。(其中,"<n>"表示一个任意字符串)。
(3)后缀规则
后缀规则是一个比较老式的定义隐含规则的方法。为了和老版本的Makefile 兼容,GNU make 一样兼容于这些东西。后缀规则有两种方式:"双后缀"和"单后缀"。
双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如".c.o"至关于"%o : %c"。单后缀规则只定义一个后缀,也就是源文件的后缀。如".c"至关于"% : %.c"。
后缀规则不容许任何的依赖文件,若是有依赖文件的话,那就不是后缀规则,那些后缀通通被认为是文件名。
后缀规则中所定义的后缀应该是 make 所认识的,默 认 的 后 缀 列 表是:.out, .a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h,.info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。而要让 make知道一些特定的后缀,咱们可使用伪目标".SUFFIXES"来定义或是删除,如:.SUFFIXES: .hack .win把后缀.hack 和.win 加入后缀列表中的末尾;.SUFFIXES: # 删除默认的后缀;SUFFIXES: .c .o .h # 定义本身的后缀。
2.3变量
2.3.1 特殊符号及关键字
反斜杠(\):是换行符的意思,当一行过长不方便浏览时使用反斜杠换行;另外还有转义的做用,例外是$,若是想要使用$,须要用$$表示。
横杠(-):加在命令前,表示也许某些文件出现问题,但不要管,继续作后面的事。
井号(#):注释符,makefile只支持单行注释。可以使用注释符结合空变量定义一个空格变量:
nullstring :=
space := $(nullstring) # end of the line 其中,nullstring变量什么都没有,space为空格变量。
通配符*,?,[]:*号表示任意字符,?号任意一个字符,[]表示范围内的字符。
%:表示匹配0或若干字符,用于pattern模式中。
objects := $(wildcard *.o):在变量中使用通配符须要使用关键字wildcard指定,不然只会相似于宏替换,不会展开。
Include(filename):把其它makefile文件包含到当前位置,但命令前不能用tab开头。
VPATH:makefile中的特殊变量,用于指定make对依赖文件的搜索路径
vpath:make的关键字,全小写。相似于VPATH,但更灵活
override:在makefile中设置make命令中指定的变量的值,当make命令中指定了该变量的值时,makefile中该变量值被忽略。
2.3.2 变量
(1)自定义变量
在 makefile 中咱们可使用变量。makefile 的变量也就是一个字符串,相似于C 语言中的宏。不过变量名能够是任意大小写,当定义了变量后,可使用 “$(变量名)”的方式来使用这个变量了,其实就相似于宏替换。
变量的命名能够包含字符、数字,下划线(能够是数字开头) ,但不该该含有“:”、“#”、“=”或是空字符(空格、回车等)。变量是大小写敏感的, 推荐使用大小写搭配的变量名,如:MakeFlags。这样能够避免和系统的变量冲突。
在变量赋值时,变量的值能够是其余变量的表达式,而其余变量的定义能够在文件的任何位置,即不要求其余变量必定要预先定义。但在递归定义时将会出现无限循环,所以引入操做符“:=”来表示只使用预先定义的变量的值,从而避免变量递归定义时的无限循环。此外,变量的值能够是嵌套的变量表达式,其赋值方式就是按宏替换的方式依次展开求值。
变量值替换:1“${var:a=b}”,其意思是,把变量“var”中全部以“a”字串“结尾”的“a”替换成“b”字串;2另一种变量替换的技术是以“静态模式”(bar := $(foo:%.o=%.c))
变量做用域:makefile中定义的变量都是全局变量(除了自动化变量),也可定义做用于特定目标或模式的局部变量,格式为:
目标/模式:变量赋值表达式
(2)自动化变量
这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至全部的符合模式的文件都取完了。
$%:仅当目标是函数库文件时,表示规则中的目标成员名。
$@:表示目前规则中全部的目标的集合
$<:依赖目标中的第一个目标名字。
$?:全部比目标新的依赖目标的集合。以空格分隔。
$^:全部的依赖目标的集合。以空格分隔。
$+:这个变量很像"$^",也是全部依赖目标的集合。只是它不去除重复的依赖目标。
$*:这 个 变 量 表 示 目 标 模 式 中 "%"及 其 之 前 的 部 分 。
这七个自动化变量还能够取得文件的目录名或是在当前目录下的符合模式的文件名, 只须要搭配上"D"或"F"字样。
(3)命令序列变量
能够为命令序列指定一个变量,便于重用。定义方式为:
define 变量名
命令序列
endef
以后直接按变量引用方式($(变量名))引用便可。
(4)系统变量
MAKEFLAGS:保存make参数信息;
MAKELEVEL:保存了当前makefile在make嵌套执行的调用层数;
CFLAGS:环境变量,若系统环境中设置了该环境变量,则全部makefile文件均可以使用该变量设置的统一参数。若makefile文件中定义了该变量,将覆盖环境变量中的值。
MAKECMDGOALS:make的环境变量,这个变量中存放所指定的终极目标的列表,若是在命令行上,你没有指定目标,那么,这个变量是空值。
2.3.3 伪目标
“伪目标”并非一个文件,只是一个标签。为了不和文件重名的这种状况,咱们可使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,如
.PHONY: clean
clean:
rm *.o temp
伪目标能够做为默认目标也能够有依赖文件,借由这个特性,咱们能够一次性生成多个可执行文件。
2.4 表达式
2.4.1 赋值表达式
?=:用于变量定义,表示若是变量没有被定义,则定义并赋值,若是定义了语句做废。
+=:用于变量赋值时的追加,若是变量以前没有定义过,那么,“+=”会自动变成“=”,若是前面有变量定义,那么“+=”会继承于前次操做的赋值符(=或:=)。
2.4.2 条件表达式
格式为: <conditional-directive> <text-if-true> else <text-if-false> endif
其中<conditional-directive>表示条件关键字。这个关键字有四个:ifeq、ifneq、ifdef、ifndef。
2.5 函数
2.5.1 调用语法
${<function> <arguments>}
说明:<function>就是函数名,make 支持的函数很少。<arguments>是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。
2.5.2 经常使用函数
类别 |
函数名 |
功能 |
说明 |
字符串函数 |
$(subst <from>,<to>,<text> ) |
把字串<text>中的<from>字符串替换成<to>。 |
函数返回被替换事后的字符串。 |
$(patsubst <pattern>,<replacement>,<text> ) |
查找<text>中的单词是否符合模式<pattern>,匹配则以<replacement>替换。 |
若是<replacement>中也包含%,那么这个“%”将是<pattern>中的那个“%”所表明的字串。 |
|
$(strip <string> ) |
去掉<string>字串中开头和结尾的空字符。 |
函数返回被替换事后的字符串。 |
|
$(findstring <find>,<in> ) |
在字串<in>中查找<find>字串。 |
若是找到,返回<find>,不然返回空字符串。 |
|
$(filter <pattern...>,<text> ) |
以<pattern>模式过滤<text>字符串中的单词,保留符合<pattern>的单词。 |
能够有多个模式。 |
|
$(filter-out <pattern...>,<text> ) |
以<pattern>模式过滤<text>字符串中的单词,去除符合模式的单词 |
能够有多个模式。返回不符合模式<pattern>的字串。 |
|
$(sort <list> ) |
给字符串<list>中的单词排序(升序) 。 |
sort 函数会去掉<list>中相同的单词。 |
|
$(word <n>,<text> ) |
取字符串<text>中第<n>个单词。 (从一开始) |
若是<n>比<text>中的单词数要大,返回空字符串。 |
|
$(wordlist <s>,<e>,<text> ) |
从字符串<text>中取从<s>开始到<e>的单词串。 |
若是<s>比<text>中的单词数要大,返回空字符串。 |
|
$(words <text> ) |
统计<text>中字符串中的单词个数。 |
取最后一个单词: $(word $(words <text> ),<text> )。 |
|
$(firstword <text> ) |
取字符串<text>中的第一个单词。 |
|
|
文件操做函数 |
$(dir <names...> ) |
从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)以前的部分。 |
若是没有反斜杠,那么返回“./” |
$(notdir <names...> ) |
从文件名序列<names>中取出非目录部分。 |
非目录部分是指最后一个反斜杠(“/”)以后的部分。 |
|
$(suffix <names...> ) |
从文件名序列<names>中取出各个文件名的后缀。 |
若是文件没有后缀,则返回空字串。 |
|
$(basename <names...> ) |
从文件名序列<names>中取出各个文件名的前缀部分。 |
|
|
$(addsuffix <suffix>,<names...> ) |
把后缀<suffix>加到<names>中的每一个单词后面。 |
|
|
$(addprefix <prefix>,<names...> ) |
把前缀<prefix>加到<names>中的每一个单词后面。 |
|
|
$(join <list1>,<list2> ) |
把<list2>中的单词对应地加到<list1>的单词后面。 |
若是<list1>的单词个数多于<list2>,多出部分保持原样。反之复制到1中。 |
|
循环 |
$(foreach <var>,<list>,<text> ) |
把<list>中的单词逐一取出到<var>变量中,再执行<text>包含的表达式。 |
返回值为text每次执行返回的字符串组合(以空格相隔) |
条件 |
$(if <condition>,<then-part>,<else-part> ) |
|
|
call |
$(call <expression>,<parm1>,<parm2>,<parm3>...) |
<expression>中的变量($(1),$(2)…)会 被 <parm1> , <parm2>…依 次 取 代 。 |
返回值为<expression>的返回值 |
origin |
$(origin <variable> ) |
返回变量来源 |
返回值为:undefined,default,environment,file,command line,override,automatic |
Shell函数 |
$(shell 命令) |
它的参数是操做系统Shell 的命令 |
返回值为shell函数所执行的操做系统命令的输出 |
控制make的函数 |
$(error <text ...> ) |
产生一个致命的错误,<text ...>是错误信息。 |
|
$(warning <text ...> ) |
输出一段警告信息, make 继续执行。 |
|
2.6 make
2.6.1 指定makefile和目标
(1)指定makefile
make 命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。若找不到,将会依次到make命令-I参数指定目录和<prefix>/include(通常是:/usr/local/bin 或/usr/include)目录查找。若是要指定特定的 Makefile,可使用 make 的“-f”和“--file”参数,如:make -f Make.Linux。
(2)指定目标
通常来讲,make 的最终目标是 makefile 中的第一个目标,而其它目标通常是由这个目标连带出来的。而要指定其余目标做为最终目标,在 make 命令后直接跟目标的名字就能够完成(如前面提到的“makeclean”形式),即便是伪目标和隐含目标均可以做为最终目标。
GNU目标编写习惯说明:
伪目标 |
功能 |
all |
全部目标的目标,其功能通常是编译全部的目标。 |
clean |
删除全部被 make 建立的文件。 |
Install |
安装已编译好的程序,就是把目标执行文件拷贝到指定的目标中去。 |
|
列出改变过的源文件。 |
tar |
把源程序打包备份,也就是一个tar文件。 |
dist |
建立一个压缩文件,通常是把 tar 文件压成Z文件或是gz文件。 |
TAGS |
更新全部的目标,以备完整地重编译使用。 |
check、test |
测试 makefile 的流程。 |
2.6.2 make的参数
下面列举了全部 GNU make 3.80 版的参数定义:
参数 |
功能 |
-b,-m |
忽略和其它版本 make 的兼容性。 |
-B,--always-make |
认为全部的目标都须要更新(重编译) 。 |
-C <dir>, --directory=<dir> |
指定读取 makefile 的目录。若是有多个“-C”参数,后面的路径之前面的做为相对路径,并以最后目录做被指定目录。 |
—debug[=<options>] |
输出 make 的调试信息,它有几种不一样的级别可供选择。 |
-e, --environment-overrides |
指明环境变量的值覆盖 makefile 中定义的变量的值。 |
-f=<file> |
指定须要执行的 makefile。 |
-h |
|
-i |
在执行时忽略全部的错误 |
-I <dir> |
指定一个被包含 makefile 的搜索目标。 |
-k |
出错也不中止运行。 |
-l <load> |
指定 make 运行命令的负载。 |
-n |
仅输出执行过程当中的命令序列 |
-o <file> |
不从新生成的指定的<file> |
-p |
输出 makefile 中的全部数据,包括全部的规则和变量。 |
-q |
不运行命令,也不输出。仅仅是检查所指定的目标是否须要更新。 |
…… |
|
详细参数信息能够查看make帮助文档。
2.6.3 使用make更新函数库文件
函数库文件也就是对 Object 文件(程序编译的中间文件)打包生成的文件。能够以以下格式指定函数库文件及其组成:
${<function> <arguments>}
这不是一个命令,而是一个目标和依赖的定义。若是要指定多个 member,那就以空格分开,如:foolib(hack.o kludge.o)。
2.7其余
(1)显示命令:在makefile中的命令前加@符号,则该命令在make执行时将不被显示在屏幕上;而在make命令中使用参数-n或--just-print,则只在屏幕显示执行的命令序列,而不会执行命令,经常使用来调试。
(2)环境变量MAKEFILES:相似于include(filename),但环境变量MAKEFILES中的目标(即文件中的第一个标识符)不起做用,且不理会其中定义的文件的错误。建议不要使用该环境变量,容易出一些莫名的错误。
(3)make 是在读取 Makefile 时就计算条件表达式的值,并根据条件表达式的值来选择语句,因此,你最好不要把自动化变量(如“$@”等)放入条件表达式中,由于自动化变量是在运行时才有的。
(4)make的嵌套执行:大型工程为了利于模块编译和维护,一般会在每个模块目录中分别写上makefile文件,而在总控makefile文件中指定模块makefile的执行。而总控模块中的变量能够经过声明export variable传递到下级的makefile中,但不会覆盖下级makefile中的变量,直接使用export表示传递全部变量。其中变量SHELL和MAKEFLAGS老是会自动传递到下层makefile中,而MAKEFLAGS中保存了make参数信息,一旦定义了MAKEFLAGS变量,它将自动传递到下级makefile中(除了几个参数:C,f,h,o,W),若不想传递make参数,能够这样:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
(5)命令出错:make会检查每条命令执行后的返回值(),若是不正确则会终止该条规则的执行。1若是不但愿命令出错而终止规则运行,能够在命令前加一个“-”表示忽略该命令出错继续执行后续命令;2也能够指定make命令参数-i或—ignore-errors表示忽略全部命令的错误;3而若是一个规则的目标是.IGNORE,则表示该条规则的全部命令都忽略错误。4而若是指定make的参数-k或—keep-going,则表示命令出错时终止对应规则而继续执行其余规则。
3、例子
例1 多目标示例1
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
说明:其中,-$(subst output,,$@)中的“$”表示执行一个 Makefile 的函数,函数名为 subst,后面的为参数。这里的这个函数是截取字符串的意思,“$@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。
例2 多目标示例2
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”)。
例3 生成.d文件(源文件的依赖关系文件)的模式规则:
%.d: %.c @set -e; rm -f $@; \ $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$
说明:这个规则的意思是,全部的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除全部的目标,也就是[.d]文件,第二行的意思是,为每一个依赖文件“$<”, 也就是[.c]文件生成依赖文件, “$@”表示模式“%.d”文件,若是有一个 C 文件是 name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有多是“name.d.12345”,第三行使用sed 命令作了一个替换,关于 sed 命令的用法请参看相关的使用文档。第四行就是删除临时文件。