跟我一起写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) 依赖文件集的文件部分。

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
跟我一起 Makefile 作者:陈皓 整理:祝冬华 来源网络,希望能与大家分享这份学习资料,资源分数也设置了最低值,如有侵权,请联系我删除文件。 第一部分、概述 (6) 第二部分、关于程序的编译和链接 (6) 第三部分、Makefile 介绍 (7) 一、Makefile的规则 (7) 二、一个示例 (8) 三、make是如何工作的 (9) 四、makefile中使用变量 (10) 五、让make自动推导 (11) 六、另类风格的makefile (12) 七、清空目标文件的规则 (13) 第四部分、Makefile 总述 (13) 一、Makefile里有什么? (13) 1、显式规则。 (14) 2、隐晦规则。 (14) 3、变量的定义。 (14) 4、文件指示。 (14) 5、注释。 (14) 二、Makefile的文件名 (15) 三、引用其它的Makefile (15) 四、环境变量 MAKEFILES (16) 五、make的工作方式 (16) 第五部分、书规则 (17) 一、规则举例 (17) 二、规则的语法 (17) 三、在规则中使用通配符 (18) 四、文件搜寻 (19) 五、伪目标 (20) 六、多目标 (22) 七、静态模式 (22) 八、自动生成依赖性 (24) 第六部分书命令 (25) 一、显示命令 (26) 二、命令执行 (26) 三、命令出错 (27) 四、嵌套执行make (28) 五、定义命令包 (30) 第七部分使用变量 (30) 一、变量的基础 (31) 二、变量中的变量 (32) 三、变量高级用法 (34) 四、追加变量值 (37) 五、override 指示符 (37) 六、多行变量 (38) 八、目标变量 (39) 九、模式变量 (40) 第八部分使用条件判断 (40) 一、示例 (40) 二、语法 (42) 第九部分使用函数 (43) 一、函数的调用语法 (44) 二、字符串处理函数 (44) 1、subst (44) 2、patsubst (45) 3、strip (45) 4、findstring (46) 5、filter (46) 6、filter-out (46) 7、sort (47) 8、word (47) 9、wordlist (47) 10、words (47) 11、firstword (48) 12、字符串函数实例 (48) 三、文件名操作函数 (48) 1、dir (48) 2、notdir (48) 3、suffix (49) 4、basename (49) 5、addsuffix (49) 6、addprefix (49) 7、join (50) 四、foreach 函数 (50) 五、if 函数 (50) 六、call函数 (51) 七、origin函数 (51) “undefined” (52) “default” (52) “file” (52) “command line” (52) “override” (52) “automatic” (52) 八、shell函数 (53) 九、控制make的函数 (53) 1、error (53) 2、warning (54) 第十部分 make 的运行 (54) 二、指定Makefile (54) 三、指定目标 (55) “all” (56) “clean” (56) “install” (56) “print” (56) “tar” (56) “dist” (56) “TAGS” (56) “check”和“test” (56) 四、检查规则 (57) 五、make的参数 (57) 第十一部分隐含规则 (61) 一、使用隐含规则 (61) 二、隐含规则一览 (62) 1、编译C程序的隐含规则 (63) 2、编译C++程序的隐含规则 (63) 3、编译Pascal程序的隐含规则 (63) 4、编译Fortran/Ratfor程序的隐含规则 (63) 5、预处理Fortran/Ratfor程序的隐含规则 (63) 6、编译Modula-2程序的隐含规则 (63) 7、汇编和汇编预处理的隐含规则 (64) 8、链接Object文件的隐含规则 (64) 9、Yacc C程序时的隐含规则 (64) 10、Lex C程序时的隐含规则 (64) 11、Lex Ratfor程序时的隐含规则 (65) 12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则 (65) 三、隐含规则使用的变量 (65) 1、关于命令的变量。 (65) 2、关于命令参数的变量 (66) 四、隐含规则链 (67) 五、定义模式规则 (68) 1、模式规则介绍 (68) 2、模式

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值