跟我一起写makefile总结

最近为了搞懂makefile,精读了陈皓的《跟我一起写makefile》,算是基本搞明白了makefile的各种基本规则和变量了,下面总结如下,以备后面忘记的时候查阅。现在也有了很多makefile的自动生成管理工具,可能很多时候也不用自己写makefile了,但是理解了他的基本原理还是非常有帮助的,整理完成后,准备系统学习一下cmake等makefile管理系统,使自己具备大型项目的管理能力。
首先是要区分make和makefile。make是一个命令工具,make命令可以解析makefile,根据makefile的规则来编译程序。当执行make的时候,该命令会首先在本目录下寻找makefile,如果没有找到,也会根据相应的规则到其他几个目录去找,这个后面会有说明。
一:makefile的介绍
1.makefile的规则:

target:prerequisites
command

target是一个目标文件,可以是一个.o文件,也可以是可执行文件,还可以是标签(伪目标)
prerequisites就是要生成这个target所需要依赖的文件
command就是生成这个目标所要执行的命令(任意的shell命令)
这其实就是一个文件的依赖关系,target需要依赖prerequisite中的文件,其生成规则定义在command中。
2.make是如何工作的
如果我们要编译一个edit可执行文件,依赖文件为几个头文件和几个.c文件
a.make会在当前目录下寻找名字为Makefile或者makefile的文件
b.如果找到,它会找文件中的第一个目标文件,也就是edit文件,并把这个文件作为最终的目标文件
c.如果edit文件不存在,或者是edit所依赖的后面的.o文件的修改时间比edit这个文件新,那么,他会执行后面command定义的命令来生成这个edit文件
d.如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖,如果找到再根据它的规则生成.o文件
e.最终找到所有目标的依赖,依次生成所需要的目标,最终生成最后的目标文件。
3.makefile中使用变量
在makefile中可以定义变量,每个变量就是一个字符串。

定义:objects = main.o kdd.o
使用:$(objects)

4.让make自动推导
make看到一个.o文件,它会自动把对应名字的.c文件加到依赖中,比如如果要编译一个what.o文件,其依赖为w.h和what.c文件,则只需指定w.h文件为依赖即可,因为what.c文件会自动加到依赖中。
what.o : w.h
5.清空目标文件的规则
每个makefile中都应该写一个清空目标文件的规则,目标文件为.o和可执行文件,不仅有利于编译,也有利于保持文件的整洁。

.PHONY : clean
clean :
-rm edit $(objects)  ## "-"的作用是出错的时候继续执行

注意 clean不能放在makefile的开头,因为make会自动将开头的目标作为最终的目标,所以这种伪目标一般放在最后。伪目标在执行make的时候也不会自动执行,需要make clean来执行
二:makefile总述
1.makefile里面有什么
a.显式规则
显式规则明确规定如何生成一个或多个目标文件,这是由makefile明确写出的。
b.隐晦规则
由于我们的makefile具有自动推导功能,所以就有很多隐晦的规则,有make自动推导出来的,不需要明确写出来
c.变量的定义
在makefile中我们要定义一系列变量,变量一般都是字符串,有点像c语言中的宏,当makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
d.文件指示
包括三部分,一个是在makefile中引用另一个makefile,就像c语言的include,另一个是根据某些情况指定makefile的有效部分,就像c语言的预编译#if一样,还有就是定义一个多行的命令,后面会有详细介绍。
e.注释
makefile中用#来标明注释。在makefile中,所有的shell命令必须要tab开头。
2.makefile的文件名
大部分make命令都支持Makefile和makefile这两种文件名。
3.引用其他的makefile
在makefile中使用include关键字可以把别的makefile包含进来,被包含的文件会原模原样的放在当前文件的包含位置,所以包含include是有前后顺序的。

语法: include <filename>
include *.mk

如果当前目录下找不到makefile,make还会在下面几个目录下寻找:
a.如果make执行时,有“-I”或“–include-dir”参数,则make就会在这个参数所指定的目录下去寻找
b.如果目录prefix/include(一般是/usr/local/bin或/usr/include)存在的话,make也会去找。
4.环境变量MAKEFILES
这个环境变量也可以包含一些makefile,一般不建议使用。
5.make的工作方式
a.读入所有的makefile
b.读入被include的其他makefile
c.初始化文件中的变量
d.推导隐晦规则,并分析所有规则
e.为所有的目标文件创建依赖关系链
f.根据依赖关系,决定哪些目标要重新生成
g.执行生成命令
三:书写规则
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
在makefile中,规则的顺序是很重要的,因为makefile只有一个最终目标,其他的目标都是被这个最终目标带出来的,makefile中目标有很多,但是第一条规则中的目标将被确立为最终的目标。
1.规则举例
2.规则的语法

targets : prerequisites
command
或者
targets : prerequisites; command

targets是文件名,以空格分开,可以使用通配符。
prequisites就是目标所需要依赖的文件。
command是命令行,如果不在target一行,那么必须以tab开头,如果和target一行,可以用分号作为分隔。
3.在规则中使用通配符
如果我们想定义一系列比较类似的文件,我们可以使用通配符。make支持三种通配符* [...]
*.c表示所有后缀为c的文件。如果要在文件名中含有*,则需要使用\字符,用\*来代表*
例子:

clean :
	rm -f *.o

通配符同样可以用在变量中。如下:
objects = *.o
注意的是,这里的*不会展开,而object的值就是 *.o.makefile就像宏定义一样在使用的地方原样展开。如果想要在变量中将通配符展开,也就是让objects的值为所有.o文件的集合,那么需要用到wildcard关键字,如下:
objects := $(wildcard *.o)
4.文件搜寻
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,存放在不同的目录中,所以当make需要去寻找文件的依赖关系时,我们需要在文件前加上路径,但最好的办法是把一个路径告诉make,让它自己去寻找,这里就需要用到VPATH特殊变量了,如果没有指明这个变量,make只会在当前目录去寻找,如果定义了这个变量,本目录下面找不到时,就会去指定的目录寻找了。
VPATH = src:../headers #多个目录用:分隔
另一个设置文件搜索路径的方法是使用make的vpath关键字,这个比较灵活,可以针对某一类pattern的文件来指定搜索目录,有下面三种使用方法:

a.vpath <pattern> <directories> #为符合模式pattern的文件指定搜索目录
b.vpath <pattern> #清除符合模式pattern的文件的搜索目录
c.vpath  #清除所有已经被设置的文件搜索目录

上面的pattern需要包含关键字%,用来匹配零或者若干字符。注意%一般用于pattern的匹配,*是字符串的通配符,不要混了。
5.伪目标

.PHONY : clean
clean :
rm *.o

上面就是一个伪目标,伪目标不会自动运行,只有make clean时才会运行,用于实现一些辅助的功能,比如清除中间文件,打包,复制等。伪目标一般没有依赖文件,但也可以有依赖文件。

.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj:
rm *.o
cleandiff
rm *.diff

输入make cleanall会自动调用cleanobj和cleandiff
6.多目标,
makefile的规则中的目标可以不止一个,其支持多目标,而且多个目标可以同时依赖于多个文件。

bigoutput  littleoutput : output.c
command...

7.静态模式
静态模式可以更加容易的定义多目标的规则
语法:

targets:target-pattern : prereq-patterns
commands

targets定义了一系列的目标文件,可以有通配符,是目标的一个集合
target-pattern 指明了targets的模式,也就是目标集的模式
prereq-patterns是目标的依赖模式
例子:

objects = foo.o bar.o
all:$(objects)
$(objects):%.o:%.c
$(CC) -c $(CFLAGS) $< -O $@

里面的几个自动化变量后面会有介绍。
8.自动生成依赖
gcc -M main.c 就会自动生成main.c所需要的头文件的路径。
四:书写命令
每条规则中的命令和操作系统的shell的命令行是一致的。make会按照顺序一条一条的执行命令。每条命令的开头必须以tab键开头,除非命令是紧跟在依赖规则后面的分号后的。
1.显示命令
通常make执行时会把要执行的命令打印到屏幕,如果我们不想让它打印,可以在命令前加上@
make的参数-n或者--just-print,则make只显示命令,不会具体执行,可以方便我们debug。
-s--slient则是全面禁止命令的显示。
2.命令执行
如果你想让上一条命令的结果应用在下一条命令上,你应该使用分号来分割这两条命令,将两条命令写在一行上,用分号分隔。
例如:

例1:
exec:
cd /home/cc
pwd
例2
exec:
cd /home/cc;pwd

例子1中,cd没有起作用,例子2中的cd才会起作用。
3.命令出错
make会检测每个命令的返回码,如果返回成功,则继续执行下一条命令,如果返回非零,则表示该命令执行失败,make会终止当前规则,并可能终止所有规则。但有些时候,命令出错不表示有错误,像mkdir,如果该目录存在,会返回错误,但我们不想终止编译,所以这时可以在命令前加一个减号“-”,表示不管命令是否出错,都继续执行。
还有一个全局的办法,就是make -i 或者 --ignore–errors参数,make会忽视所有出错。
还有一个-k 或者 --keep-going ,表示如果某规则出错了,那么就终止该规则的执行,但继续执行其他规则。
4.嵌套执行make
在一些大的工程中,我们会把不同的模块放到不同的目录中,每个目录中都可以写一个makefile,这样对模块编译和分段编译非常有好处。比如我们有一个子目录为subdir,这个目中下有个makefile,来指明这个目录下文件的编译规则,则总控makefile可以如下:

subsystem:
cd subdir && $(MAKE)
MAKE是一个变量,可以定义为一定的编译规则。
等价于:
subsystem:
$(MAKE) -C subdir

我们把最上面的makefile叫做总控makefile,总控makefile的变量可以传递到下级的makefile中(如果显式的声明),但是不会覆盖下层的makefile中所定义的变量,除非指定了“-e”参数。
如果你要传递变量到下级makefile中,可以这样声明:
export <variable>
export variable=value

variable = value
export variable

如果你不想让某些变量传递到下级,可以这样声明:
unexport <variable>
需要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,都会传递到下层makefile中。
如果你不想这两个变量向下传递,可以如下写:

subsystem:
cd subdir && $(MAKE) MAKEFLAGS=

还有一个make参数在嵌套执行中比较有用,-w 和 --print-directory。make的过程中会打印出当前目录信息,可以看到make的执行过程。如下打印:
make : Entering directory “/home/hchen/gnu/make”.
当使用-C参数来指定make下层目录时,-w会自动打开,如果参数中有-s或则–slicnet 或者–no-print-directory时,-w总是失效。
5.定义命令包
如果makefile中出现一些相同的命令序列,则我们可以定义将这些相同的命令序列定义成一个变量,以define开头,以endef结束

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef

run-yacc就是命令包的名字,注意不要和makeifle中的其他变量重名。使用如下:

foo.c : foo.y
$(run-yacc)

五:使用变量
在makefile中定义的变量,就像时c/c++中的宏一样,代表了一个文本字符串,执行的时候会在调用的位子原样展开。与宏不同的是,变量的值可以改变。
1.变量的基础
变量在声明时需要给予初值,而在使用时,需要变量名前加上$符号,最好用()或者{}括起来。如果某个地方需要用$字符,需要用$$来表示。变量可以用在很多地方,如规则中的目标,依赖,命令以及新的变量中。
2.变量中的变量
在定义变量的值时,我们可以使用其他变量来构造变量的值。

bar = gcc
foo = $(bar)
ugh := $(bar)

还有一个比较有用的操作符是?=, 如foo ?= bar 含义是如果foo没有被定义过,则foo的值就是bar。如果被定义过,就什么也不做。
3.变量高级用法
第一种,变量值的替换,格式为$(var:a=b),意思是将变量var中所有以a字串结尾的a替换为b。
如:

foo :=a.o b.o c.o
bar :=$(foo:.o=.c)

结果bar就是a.c b.c c.c
第二种 把变量的值再当成变量

x = y
y = z
a := $($(x))

结果a就是z。
把变量的值当成变量同样可以用在操作符的左边,

dir =foo
$(dir)_sources := $(wildcard $(dir)/*.c)

4.追加变量值
我们可以使用+=操作符来给变量追加值。如:

objects = mainl.o foo.o
objects += another.o

:=的情况和=一样。
5.override指示符
如果有变量是通过make的命令行参数设置的,那么makefile中对这个变量的赋值会被忽略。如果你想在makefile中设置这类参数的值,可以使用override指示符。语法为:

override <variable> = <value>

6.多行变量
还有一种设置变量值的方法是使用define关键字。使用define关键字设置的变量的值可以有换行,前面讲的命令包就是利用这个关键字。
7.环境变量
make运行时的系统环境变量可以在make开始运行时被载入到makefile中,但是如果makefile中已经定义了这个变量,或者这个变量由make命令行带入,则系统环境变量的值将被覆盖,也就是说环境变量中的值的优先级最低。但如果make指定了-e参数,那么环境变量将覆盖makefile中定义的变量。
当make嵌套调用时,上层makeifle中定义的变量会以环境变量的方式传递到下层的makefile,当然,默认情况下只有通过make命令行设置的变量会被传递,而定义在文件中的变量,如果要向下层makefile传递,则需要使用export关键字来声明。
8.目标变量
前面所讲的变量都是全局变量,整个makefile文件都可以访问,另外,我们还可以定义局部变量,成为target-specific variable, 它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。语法为:

<target> : <variable-assignment>
<target> : override <variable-assignment>

<variable-assignment>可以是前面所讲的各种赋值表达式,如"=" , “:=” , “+=” , “?=”. 第二个语法是针对make命令行带入的变量,或者是系统环境变量。
如:
prog : CFLAG = -g 不管其他地方定义的CFLAG是啥,对于prog目标,CFLAG就是-g.
9.模式变量
在GNU的make中,还支持模式变量,就是针对某一种模式来定义一个变量,模式一般用%来表示
%.o : CFLAG = -O
六:使用条件判断
使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支,条件表达式可以是比较变量的值,或是比较变量和常量的值。
1.示例

ifeq ($(cc), gcc)
$(cc) -o foo $(libs_for_gcc)
else
$(cc) -o foo $(norml_libs)
endif

2.语法
条件表达式的语法:

<conditional-directive>
<text-if-true>
else
<text-if-false>
endif

conditional-directive : 有四个关键字:

ifeq (<arg1>, <arg2>)
ifneq (<arg1>, <arg2>)
ifdef <variable-name>
ifndef <variable-name>

特别注意的是,make是在读取makefile时就计算条件表达式的值了,并根据条件表达式的值来选择语句,就像预处理一样,所以最好不要把自动化变量(如$@)放入条件表达式中,因为自动化变量是在运行时才有的。
七:使用函数
makefile中可以使用函数来处理变量,下面进行介绍。函数调用后,函数的返回值可以当作变量来使用。
1.函数的调用语法
$(<function> <arguments>) 或者
${<function> <arguments>}
参数间以逗号分隔,函数名与参数间以空格分隔。
2.字符串处理函数
a.subst
$(subst <from>, <to>, <text>)
字符串替换函数,把字符串text中的from字符串替换为to,返回被替换过的字符串。
b.patsubst
$(patsubst <pattern>, <replacement>, <text>)
模式字符串替换函数,查找text中的单词是否符合pattern,如果匹配,则以replacement替换。pattern可以使用%通配符,表示任意长度的字串。

objects = foo.o bar.o bb.c
$(patsubst %.o, %.c, $(objects))  结果是foo.c bar.c bb.c

c.strip
$(strip <string>)
去掉string字串中开头和结尾的空格字符。注意只去掉开头和结尾的。
d.findstring
$(findstring <find>, <in>)
查找字符串函数,在字符串in中查找find字符串,如果找到,返回find,否则返回空字符串。

$(findstring a, a b c)   //返回a
$(findstring a, b c)   //返回“”空字符串

e.filter
$(filter <pattern>, <text>)
过滤函数,以pattern模式过滤text字符串中的单词,保留符合模式pattern的单词。返回符合模式pattern的字串。

source := foo.c bar.c baz.c ugh.h
foo : $(source)
cc $(filter %.c %.s, $(source)) -o foo

$(filter %.c %.s, $(source))的返回值是foo.c bar.c baz.c
f.filter-out
$(filter-out <pattern>, <text>)
反过滤函数,以pattern模式过滤text字符串中的单词,去除符合pattern的单词
g.sort
$(sort <list>)
排序函数,给list中的单词排序(升序),返回排序后的字符串,sort函数会去掉list中相同的单词
h. word
$(word <n>, <text>)
取单词函数,取字符串中的n个单词,从1开始数。如果n比text中的单词数大,返回空字符
i. wordlist
$(wordlist <s>, <e>, <text>)
取单词串函数,从字符串text中从s到e的单词串,如果s比text中的单词要大,则返回空字符串
$(wordlist 2, 3, foo bar baz),返回值为“bar baz”
j.words
$(words <text>) 统计字符串中的单词个数,返回字符串中的单词数。
k.firstword
$(firstword <text>) 取字符串text中的第一个单词
3.文件名操作函数
a.dir
$(dir <names>) 从名字序列中取出目录部分,各个名字用空格分隔
b.notdir
$(notdir <names>) 从名字列表中取出非目录部分,就是取出文件名
c.suffix
$(suffix <names>) 取后缀函数,从文件名序列中取出各个文件名的后缀,比如.c .h等。
d.basename
$(basename <names>) 取前缀名字,从文件名序列中取出各个文件名的前缀部分

$(basename src/foo.c src/bar.c hacks) 返回值为src/foo src/bar hacks

e.addsuffix
$(addsuffix <suffix>, <names>) 加后缀函数, 将后缀suffix加到names中的每个单词的后面,返回加过后缀的文件名序列
$(addsuffix .c, foo bar)返回值为foo.c bar.c
f.addprefix
$(addprefix <prefix>, <names>) 加前缀函数,把前缀prefix加到names中的每个单词后面。示例:
$(addprefix src/,foo bar),返回值为src/foo src/bar
g.join
$(join <list1>, <list2>) 链接函数,将list2中的单词对应的加到list1的单词后面。如果list1中的单词个数要比list2中的多,那么list1中多出来的单词保持原样,如果list2的单词个数比list1多,则list2中多出来的单词复制到list1中。
$(join aaa bbb, 111 22 333) 返回值为aaa111 bbb222 333
4.foreach函数
foreach函数是用来做循环用的,
$(foreach <var>,<list>,<text>) 函数的意思是把参数list中的单词逐一取出放到var所指定的变量中,然后再执行text所包含的表达式。每一次text会返回一个字符串,循环过程中,text所返回的每个字符串都会以空格分隔,最后当整个循环结束后,text所返回的每个字符串所组成的整个字符串将会是foreach函数的返回值。所以var最好是一个变量名,list可以是一个表达式,text中一般会使用var这个参数来依次枚举list中的单词。例子:
names := a b c d
files := $(foreach n, $(names), $(n).o) 上面的例子中,names中的单词会被挨个取出,并存在n中,$(n).o每次计算出一个值,这些值以空格分隔,所以最后files的值为 a.o b.o c.o d.o
5.if函数
if函数很像make的条件语句ifeq,if的语法为:
$(if <condition>,<then-part>)或者是$(if <condition>,<then-part>,<else-part>)
condition参数是if的表达式,如果其返回的为非空字符串,那么这个表达式相当于真。
6.call函数
call函数是唯一 一个可以用来创建新的参数化的函数,这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。
$(call <expression>, <parm1>,<parm2> ...) 当执行这个函数时,expression中的变量,如$(1), $(2), $(3)等,会被parm1, parm 2, parm3依次取代。而expression的返回值就是call函数的返回值。例如:

reverse = $(1) $(2)
foo = $(call reverse, a, b)  那么foo的值就是a b。

7.origin函数
origin函数并不操作变量的值,他只是告诉你这个变量是哪里来的, 语法为:
$(origin <variable>) 注意variable是变量的名字,不是引用,所以不要用$号。
返回值:
undefined : 没有被定义过
default : 默认的定义,如CC
file: 在makefile中被定义
command line : 被命令行定义
override : 被override指示符重新定义的
automatic : 自动化变量。
8.shell函数
shell函数就是执行shell的命令,它的参数就是操作系统shell的命令,shell函数把执行操作系统命令后的输出作为函数返回。这里要注意,makefile中不是任何地方都能直接运行shell命令的,我们从前面的介绍会发现,只有target后面跟的command才能直接运行shell函数,其他地方都需要利用shell函数来运行shell command。
例子:

contents := $(shell cat foo)
files := $(shell echo *.c)//注意这里不会真正打印到屏幕,而是将echo命令的返回值赋值给files变量

注意,shell函数会生成一个shell程序来执行命令,所以要注意其性能。
9.控制make的函数
a. error
$(error <text ...>)error函数会产生一个致命错误,当执行该函数时,会打印出text信息,然后终止执行
b. warning
$(warning <text ...>) 和error函数类似,但是它只是输出一段warning,不会让make终止。
10. realpath & abspath
在一些场合下可以看的到使用了 realpath 和 abspath 函数这里做一下简单的对比:
只有这个目录存在 realpath 就会返回目录的绝对路径,不会包含 ./ …/ 这些字符。当目录不存在的时候realpath 返回为空。
而abspath则不同,不管这个文件夹是否存在,都会显示这个目录的绝对路径
八:make的运行
1.make的退出码
make命令有三个退出码:
0 - 表示成功执行
1 - 如果make运行时出现任何错误,返回1
2 - 如果使用了make的-q选项,并且make使得一些目标不需要更新,那么返回2

2.指定makefile
make命令默认在本目录下寻找makefile和Makefile,也可以用-f 和 --file参数来指定某个makefile。
3.指定目标
一般来说,make的最终目标是makefile中的第一个目标,而其他目标一般是由这个目标连带出来的。

.PHONY: all
all : prog1 prog2 prog3 prog4

从上面的伪目标可以看出,all也可以作为最终目标,也就是伪目标也可以作为最终目标。
在一般的程序的发行版本中,一般包括下面几个伪目标:
all : 功能一般是编译所有的目标
clean: 删除所有被make创建的文件
install : 安装已编译好的程序,就是把目标执行文件拷贝到指定的目标中去
print : 功能是列出改变过的源文件
tar : 把源程序打包备份,也就是一个tar文件
dist : 创建一个压缩文件,一般是把tar文件压成Z文件,或者gz文件
TAGS : 更新所有的目标,已被完成的重编译使用
check和test : 一般用来测试makefile的流程
4.检查规则
有时候我们不想让我们的makefile中的规则执行起来,只是想检查一下我们的命令,或者是执行的序列,于是我们可以使用make的如下参数
-n --just-print --dry-run --recon : 不执行参数,只打印命令,用于调试makefile
-t --touch 只是把目标的时间更新,不重新编译
-q --question : 这个参数是找目标的意思,如果目标存在,什么也不输出,如果目标不存在,打印一条出错信息。
-W <file> --what-if=<file> --assume-new=<file> --new-file=<file> 这个参数需要指定一个文件,一般是源文件或者依赖文件,make会根据规则推导出运行依赖于这个文件的命令,一般来说可以和-n参数一同使用,来查看这个依赖文件所发生的规则
5.make的参数
make的参数可以参考make的help
九:隐含规则
makefile中有一些隐含规则,make会按照惯例心照不宣的运行,比如将.c文件编译成.o文件,还有一些系统变量,比如CFLAGS等。
1.使用隐含规则
2.隐含规则一览
隐含规则自动推导是默认打开的,如果我们想关闭,可以使用参数-r 或者–no-builtin-rules
a.<n>.o的依赖会自动推导为.c文件或者.cpp文件
3.隐含规则使用的变量
a.关于命令的变量
AR : 函数库打包程序,默认命令为ar
AS : 汇编语言编译程序,默认命令为as
CC : c语言编译程序,默认命令为cc
CXX : c++语言编译程序,默认命令为g++
b.关于命令参数的变量
上面的各个命令都带有一些参数
ARFLAGS: 函数库打包程序AR命令的参数,默认值是rv
ASFLAGS : 汇编语言编译器参数
CFLAGS : c语言编译器参数
CXXFLAGS : c++语言编译器参数
4.隐含规则链
5.定义模式规则
可以使用模式规则来定义一个隐含规则,在规则中,目标的定义需要有%,意思是表示一个或多个任意字符,依赖文件中同样可以使用%,只是依赖中的%的值取决于其目标。有一点需要注意的是,%的展开发生在变量和函数的展开之后,变量和函数的展开发生在make载入makefile时,而模式中%的展开则发生在运行时。
a. 模式规则介绍
%.o : %.c; <command ...>
b.模式规则示例

%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -O $@

c.自动化变量
所谓自动化变量,就是这种变量会把模式中所定义的一些列文件自动的挨个取出,直至所有的符合模式的文件取完。自动化变量只应出现在规则的命令中。下面介绍所有的自动化变量及其说明:
$@ 规则中的目标文件集
$% 仅当目标是函数库文件时,表示规则中的依赖目标成员名,例如如果一个目标时foo.a:bar.o,那么$%就是bar.o
$< 依赖文件中的第一个目标名字
$? 所有比目标新的依赖目标的集合,以空格分隔
$^ 所有依赖目标的集合,以空格分隔,如果在依赖目标中有多个重复的,那么这个变量会去除重复的,只保留一份。
$+ 所有依赖目标的集合,不去除重复的。
$* 这个变量表示目标模式中%及其之前的部分,如果是目标"dir/a.foo.b", 并且目标模式是a.%.b,则$*的值就是dir/a.foo. 如果目标的后缀是make所能识别的,则$*就是除了后缀那一部分。例如目标是foo.c, 因为make能识别.c文件,所以$*就是foo
对上面的自动变量进行扩展, 加上D和F,D表示目录部分,F表示文件部分, $(@D)就是目标文件集的目录部分,$(^F) 依赖文件集的文件部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值