@xiaohaizi
2022-10-12T08:19:34.000000Z
字数 16243
阅读 231
make
一个简单的Makefile由若干条简单的规则组成,一个规则就像这样:
target … : prerequisites …
recipe
…
…
target:可以是实际文件或者虚假目标
prerequisites:target依赖的文件
recipe:是prerequisites比target新时,执行的命令,通常用于更新target。命令可以放在一行或多行中,多行命令每行以制表符开头。
当target不是虚拟目标时:
当target文件存在时
当prerequisites不是虚拟目标时
当prerequisites文件存在时
显式规则(explicit rule)
说明何时以及如何重新制作一个或多个文件。
隐式规则(implicit rule)
说明何时以及如何根据文件名重新制作一类文件。它描述了目标如何依赖于名称与目标相似的文件,并提供了创建或更新此类目标的方法。
变量定义(variable definition)
注释(comment)
# 以及#所在行之后的内容。
若需要使用#字符,则应写为“\#”
变量引用以及函数调用中不能使用注释(“#”将直接被当作字符处理)
recipe中的注释将被传入shell,是否解释它们由shell决定
使用反斜杠\来分割长行,反斜杠\和换行符将被替换为空格,周围的空格也会被压缩为一个空格
var := one$\
word
它相当于
var := one$ word
$后是一个空格,而没有名为空格的变量,所以"$ "会被解释称空字符串,上边的语句其实就相当于:
var := oneword
make查找Makefile时,会按照 GNUmakefile, makefile and Makefile的顺序进行查找。
推荐写为Makefile
若使用非标准名称,使用参数 ‘-f name’ or ‘--file=name’
include指令用于让make暂停读取当前的makefile,转而去读取一个或多个其他的makefile,读完了其他的再继续读取当前makefile中的剩余内容:
include filenames…
可以使用通配符。
比方说当前目录下有a.mk, b.mk, and c.mk三个mk文件,$(bar)被扩展为bish bash,那么:
include foo *.mk $(bar)
就被解释为:
include foo a.mk b.mk c.mk bish bash
使用include指令的情景:
情景1: 不同的程序分散在不同的目录中,被各自的Makefile处理,但是这些程序希望可以共享一组变量,或者模式规则。
情景2:从源文件自动生成先决条件,先决条件可以放在主 makefile 包含的文件中。
从绝对路径或者当前目录下找include的文件,
或者从‘-I’ or ‘--include-dir’ 中制定的目录中查找,
或者从prefix/include(通常是/usr/local/include) 、 /usr/gnu/include, /usr/local/include, /usr/include中查找。
如果都找不到 ,则生成警告,并且在Makefile中寻找有没有该Makefile对应的target,如果有的话,执行该target的recipe,否则报告fatal error。
若不想要警告,则使用:
-include filenames…
MAKEFILES环境变量代表一系列的文件,当执行make时,会先读取MAKEFILES代表的一系列文件。
默认目标不会从MAKEFILES代表的文件中选取。
对于include的文件来说,会:
有点儿绕
all:a c
echo all MAKE_RESTARTS: $(MAKE_RESTARTS)
a:
touch a
include haha
haha: tmp
# echo all: b > haha
echo haha: MAKE_RESTARTS: $(MAKE_RESTARTS)
include bili
bili: tmp
echo bili: MAKE_RESTARTS: $(MAKE_RESTARTS)
touch bili
b:
echo bbbb
c :
echo cccc
_tmp:
echo tmpppp
.PHONY: a tmp
输出:
Makefile:7: haha: No such file or directory
Makefile:14: bili: No such file or directory
echo bili: MAKE_RESTARTS:
bili: MAKE_RESTARTS:
touch bili
echo haha: MAKE_RESTARTS:
haha: MAKE_RESTARTS:
Makefile:7: haha: No such file or directory
echo bili: MAKE_RESTARTS: 1
bili: MAKE_RESTARTS: 1
touch bili
echo haha: MAKE_RESTARTS: 1
haha: MAKE_RESTARTS: 1
touch a
echo cccc
cccc
echo all MAKE_RESTARTS: 1
all MAKE_RESTARTS: 1
每次重新打开一个makefile,MAKE_RESTART变量就会自增1.
如果我们不想remake自己include的makefile,又想避免make利用隐式规则来remake它,可以给该makefile定义空recipe:
makefile: ;
假设Makefile中有一个foo目标,我们又写了一个GNUMakefile:
foo:
frobnicate > foo
%: force
@$(MAKE) -f Makefile $@
force: ;
执行make foo
时,将会先执行GNUMakefile中的foo目标,其他目标再去Makefile中找。
其中,force相当于一个虚假目标,不论make的目标是否存在,都会始终执行%: force
目标的规则。
分2个阶段:
第1阶段:读取所有makefile,包括include的makefile,内化所有变量及其值,还有隐式和显式规则,构建目标和先决条件的依赖图
第2阶段,根据上一阶段内化得到的数据结构来确定要更新哪些目标并执行recipe
在第1阶段扩展的变量称作immediate变量,在第2阶段扩展的称作deferred变量:
immediate = deferred
immediate ?= deferred
immediate := immediate
immediate ::= immediate
immediate += deferred or immediate
immediate != immediate
define immediate
deferred
endef
define immediate =
deferred
endef
define immediate ?=
deferred
endef
define immediate :=
immediate
endef
define immediate ::=
immediate
endef
define immediate +=
deferred or immediate
endef
define immediate !=
immediate
endef
conditional Directives是被immediate解析的。
A rule is always expanded the same way
make逐行解析Makefile:
规则顺序并不重要,除确认默认目标的情况下。
默认目标是第一条规则中的第一个目标,有两个例外:
.
开头的目标不是默认目标,除非目标包含斜杠\
。通常将默认目标起名为all。
foo.o : foo.c defs.h # module for twiddling the frobs
cc -c -g foo.c
这条规则说明2件事:
1. 如何判定目标foo.o是否过期:foo.o不存在或者foo.c、defs.h比foo.o更新一点。
两种方式:
方式一:
targets : prerequisites
recipe
…
方式二:
targets : prerequisites ; recipe
recipe
…
有2种:
normal prerequisites
order-only prerequisites
如果prerequisites发生了更新,只要target存在,就不更新target
语法:
targets : normal-prerequisites | order-only-prerequisites
文件名中使用通配符可以代表多个文件
‘*’, ‘?’ and ‘[…]’
~
在target和prerequisites中的通配符由make自身扩展;在recipe中的通配符由shell扩展;在其他场景中,显式使用wildcard函数。
通配符特殊意义可以通过在前边加上反斜杠\
关闭。
recipe中的通配符(由shell扩展):
clean:
rm -f *.o
prerequisites中的通配符(由make扩展):
print: *.c
lpr -p $?
touch print
定义变量时不会进行通配符扩展,比方说:
objects = *.o
当通配符不匹配任何文件时,它会保持原样,比方说:
foo: *.o
echo @^
若当前目录中不存在以.o
为后缀的文件名,则目标foo依赖与*.o
这个prerequisites,make时将报错:
make: *** No rule to make target `*.o', needed by `foo'. Stop.
通配符扩展自动在规则中发生,但是不会在定义变量或函数内部扩展,我们需要显式使用wildcard函数来达到目的:
$(wildcard pattern…)
该函数可以在makefile中任何地方使用,将被替换成匹配模式的文件名列表,各个文件名之间用空格分割开来。若没有匹配的文件,则被替换为空字符串。
VPATH变量指定make要搜索的路径,路径之间由冒号(:)或者空格分隔,路径顺序就是搜索顺序。在这些路径下搜索prerequisites以及target。
例:
VPATH = src:../headers
foo.o : foo.c
当执行make时,会从当前目录、src/
、../headers/
这几个目录中寻找有没有foo.c文件。
有3种形式:
vpath pattern directories
给符合pattern的文件名设置搜索路径directories。
vpath pattern
将给定pattern对应的搜索路径清空。
vpath
将之前使用vpath指令指定的搜索路径全部清空。
pattern是一个包含%
的字符串(若没有%,则必须和文件名完全匹配)。
例:
vpath %.h ../headers
之后,如果在当前目录下找不到.h的prerequisite,则去../headers目录下找。
如果某个文件名符合多个vpath指令中的pattern,则依次在这些目录中查找,例:
vpath %.c foo
vpath % blish
vpath %.c bar
则除了当前目录外,依次在foo、blish、bar这几个目录下找.c文件。
在处理完先决条件后,目标可能需要也可能不需要重建:
如果不需要重建目标,则在目录搜索期间找到的文件的路径将用于包含此目标的任何先决条件列表。简而言之,如果make不需要重建目标,则使用通过目录搜索找到的路径。
如果确实需要重建目标(已过时),则丢弃在目录搜索期间找到的路径名,并使用 makefile 中指定的文件名重建目标。简而言之,如果make必须重建,那么目标是在本地重建,而不是在通过目录搜索找到的目录中。
仅代表一个要执行的recipe,并非文件名。
虚假目标作为.PHONY目标的prerequisite,就不会被解释为文件。
.PHONY: clean
clean:
rm *.o temp
比方说:
clean: FORCE
rm $(objects)
FORCE:
所有依赖于FORCE的目标都会执行recipe,就像是声明了一个虚假目标一样,更建议使用 .PHONY。
.PHONY
.SUFFIXES
.DEFAULT
.PRECIOUS
.INTERMEDIATE
.SECONDARY
.SECONDARY
.DELETE_ON_ERROR
.IGNORE
.LOW_RESOLUTION_TIME
.SILENT
.EXPORT_ALL_VARIABLES
.NOTPARALLEL
.ONESHELL
.POSIX
具有独立目标的规则(使用标准分隔符:
)
通常用于拥有类似recipe的多个目标,比如:
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
等价于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
具有分组目标的规则(使用特殊分隔符&:
)
例
foo bar biz &: baz boz
echo $^ > foo
echo $^ > bar
echo $^ > biz
和所有规则中的prerequisites都合并到一个列表中是等价的。
若多个规则对同一个target都提供了recipe,make选用最后一个规则的recipe并打印一条错误消息。
若目标的显式规则都没有recipe,则寻求隐式规则的recipe。
根据目标名称来决定prerequisites
targets …: target-pattern: prereq-patterns …
recipe
…
例子:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
target-pattern是%.o
,prereq-patterns是%c
。
等价于:
foo.o:foo.c
xx
bar.o:bar.c
xx
当一个目标出现在多个规则中时,所有的规则必须是相同的类型:都是普通的,或者都是双冒号。如果它们是双冒号,则它们中的每一个都是独立于其他的。如果目标早于该规则的任何先决条件,则执行每个双冒号规则的配方。如果该规则没有先决条件,则始终执行其配方(即使目标已经存在)。这可能导致不执行、不执行任何或所有双冒号规则。
gcc -M foo.c
会自动生成foo.c依赖的头文件。
recipe是shell命令行,默认使用/bin/sh
。
以制表符开头的shell语句。第一条recipe也可以写在target/prerequisite行,不过需要用分号隔开。
注意:
反斜杠\
进行换行,连同反斜杠一起作为一条shell语句交给shell执行。(这一点与变量定义时不同)
例:
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
shell接收到的语句是:
for i in one two three; do \
echo $i; \
done
产生的输出是:
one
two
three
make在执行recipe前都会把该recipe打印出来。
当recipe以@开头时,echo会被抑制,@会在将recipe传递到shell前丢弃。
make的参数‘-n’ or ‘--just-print’仅打印recipe,而不执行它们。
make的参数 ‘-s’ or ‘--silent’ 拒绝打印recipe(就像给所有recipe前边都加了一个@)。
针对recipe中的每一行,都会新生成一个shell去处理。这意味着同一个recipe中不同语句的shell环境是不同的。
如果想让recipe中前边的行影响后边的行,放在一行中就好:
foo : bar/lose
cd $(<D) && gobble $(<F) > ../$@
将recipe的所有行都放在一个shell运行的好处:
- 避免生成额外进程提升性能
- 可以在recipe中包含换行符
只需要添加特殊的目标.ONESHELL
,就像这样:
.ONESHELL:
foo : bar/lose
cd $(@D)
gobble $(@F) > ../$@
shell取自环境变量SHELL,如果未设置该环境变量,则默认使用/bin/sh。
一般,make同一时间仅执行一个recipe,但如果添加了‘-j’ or ‘--jobs’选项,则可以让make同时执行多个recipe。
.NOTPARALLEL pseudo-target可以禁止make并行执行多个recipe。
同时运行多个recipe,每个recipe的输出在生成后立即显示,来自不同recipe的输出可能会交替显示。
‘--output-sync’ (‘-O’) 选项用于避免这种情况,该选项让make保存执行recipe时的输出,在执行完成后再显示到屏幕。
同步输出有4个级别(--output-sync=xxx或者-Oxxx):
在每个 shell 调用返回后,make查看它的退出状态。如果 shell 成功完成(退出状态为零),则配方中的下一行将在新的 shell 中执行;最后一行完成后,规则完成。
如果有错误(退出状态为非零),make放弃当前规则,也许放弃所有规则。
若要忽略recipe行的错误,在文本开头写一个-
,-
在传入shell之前会被丢弃。例如:
clean:
-rm -f *.o
当使用‘-i’ or ‘--ignore-errors’ 选项时,make会忽略执行recipe时产生的所有错误。
make收到kill信号后,可能会删除recipe应该更新的目标文件,以便于下次make的时候可以重新制作目标文件(已经开始制作目标文件,但没有制作完)。
递归使用make指的是在Makefile中调用make命令。
subsystem:
cd subdir && $(MAKE)
或者等价于:
subsystem:
$(MAKE) -C subdir
递归make命令时应始终使用MAKE变量,而不是显式使用命令make。
使用MAKE变量有一个副作用,相当于在recipe前边添加+
顶层make可以通过环境变量或者显式请求将变量传递给sub-make。
export和unexport用于导出或取消导出变量到sub-make。
单独使用export会导出所有变量。
‘-w’ or ‘--print-directory’选项可以在sub-make进入时和退出时打印消息,方便查看。
make: Entering directory `/u/gnu/make'.
make: Leaving directory `/u/gnu/make'.
不同目标可能用到相同的recipe时,可以考虑定义固定序列。固定序列本质上是一个变量,不能与其他变量命名冲突。如:
define run-yacc =
yacc $(firstword $^)
mv y.tab.c $@
endef
foo.c : foo.y
$(run-yacc)
在配方执行中,固定序列的每一行都被视为就像该行在规则中单独出现一样,前面有一个制表符。
target: ;
给目标定义empty recipe,可以防止目标寻找隐式规则。
变量
是一个名称(name),用于代表一个字符串,这个名称代表的字符串也被称作变量的值
。
变量的值可以用来代表targets, prerequisites, recipes, 以及Makefile中的其他部分。
当make读取Makefile时,变量和函数都会被展开(除了recipes),
变量名可以是任何字符串,除了‘:’, ‘#’, ‘=’, 以及空格。
变量名区分大小写,传统上使用大写字母。
引用变量方式,$(变量名)或${变量名}
变量引用可以使用在任何上下文中,targets, prerequisites, recipes, most directives, and new variable values.
例子:
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
字符串是严格展开的,所以对于下边的语句来说:
foo = c
prog.o : prog.$(foo)
$(foo)$(foo) -$(foo) prog.$(foo)
它可以被展开为:
prog.o : prog.c
cc -c prog.c
, {, (时,会将第一个字符看做变量名,比方说$foo
,会被当做${f}oo
对待。
递归扩展变量
使用“=”赋值,或者使用 define伪指令赋值的变量。
例子:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
最后会输出“Huh?”
在扩展{bar},然后继续扩展为$(uhg),然后继续扩展为Huh? 这就是递归扩展,知道不再包含变量引用为止。
递归循环变量有缺点:
展开变量引用时可能遇到循环依赖:
如果make在展开变量引用时检测到循环依赖,就会报错,比方说下边这句:
CFLAGS = $(CFLAGS) -O
速度慢一些
wildcardandshell函数给出不可预测的结果,wildcardandshell函数给出不可预测的结果,无法轻松控制它们何时被调用,甚至调用多少次。
简单扩展变量。
使用“:=”或“::=”赋值的变量。
简单扩展变量,在定义时就已经确定它的值,不会在运行时再次扩展。
例子:
x := foo
y := $(x) bar
x := later
相当于
y := foo bar
x := later
x的值虽然后续改变了,但y的值在定义y时已经确定,不会随着x值的修改而修改了。
简单扩展变量还可以在为其他变量赋值时引入空格,比方说:
nullstring :=
space := $(nullstring) # end of the line
space的值其实是一个空格
?=在变量尚未定义时,才去定义变量
$(var:a=b)
或者${var:a=b}
对于var中每个单词(以空白隔开的),将单词末尾的a替换为b,例:
foo := a.o b.o l.a c.o
bar := $(foo:.o=.c)
foo的值是4个单词:a.o
、b.o
、l.a
、c.o
,针对每个单词,将末尾的.o
替换为.c
,那么:
- a.o末尾的.o被替换为.c,就成了a.c
- b.o末尾的.o被替换为.c,就成了b.c
- l.a末尾没有.o,所以不被替换,保持原值l.a
- a.o末尾的.o被替换为.c,就成了c.c
最后的bar值就成了: a.c b.c l.a c.c
x = $(y)
y = z
z = Hello
a := $($(x))
a 值是 Hello
变量名后跟随“=” “:=” “::=” ,后边的内容就是变量的值
如:
objects = main.o foo.o bar.o utils.o
变量名周围的空格,以及=符号之后跟随的空格将被忽略。
“!=”(shell 赋值运算符)右侧放置shell表达式,
hash != printf '\043'
file_list != find . -name '*.c'
执行 shell 脚本并将变量设置为其输出
!= 定义的变量是递归展开变量,如果使用简单展开变量,请使用下边写法(shell 函数):
hash := $(shell printf '\043')
var := $(shell find .-name "*.c")
‘+=’向变量追加文本
objects += another.o
使用+=赋值的变量,若先前没有定义,则是一个递归扩展变量,否则沿用先前定义的变量类型。
make命令行参数传入的变量优先级更高,此时会忽略Makefile中定义的变量。
override variable = value
override variable := value
override variable += more
define指令用于定义多行变量。
define后跟随变量名,之后可以跟随一个可选的赋值运算符(=, :=, +=),之后的行是变量的值,直到遇到endef为止。
紧邻endef之前的换行不算变量值的一部分,如:
define newline
endef
变量newline中实际只包含1个换行。
若省略赋值运算符,默认是“=”。
与override可以配合使用:
override define two-lines =
foo
$(bar)
endef
通常将其设置为空字符串即可。
但是在一些flavor/origin函数中,需要使用undefine指令显式取消:
foo := foo
bar = bar
undefine foo
undefine bar
$(info $(origin foo))
$(info $(flavor bar))
这样就会输出“undefined”了。
取消命令行变量时,记得添加override
:
override undefine CFLAGS
make启动时将看到的环境变量都转为具有相同名称和值的变量,在Makefile中显示分配或命令行传递同名变量会覆盖环境变量的值(-e选项作用相反)。
当make执行recipe是,make中来自环境变量以及命令行的变量会被传递到递归调用,其他变量需使用export语句显式导出。
此类变量,只在构建特定目标过程中有效。语法为:
target … : variable-assignment
特定于目标的变量,对该目标的所有先决条件有效,以及对先决条件的所有先决条件也有效,例:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
CFLAGS不仅在prog的recipe中有效,也在prog.o foo.o bar.o的recipe中有效
此类变量,将对所有匹配制定模式的目标有效。语法为:
pattern … : variable-assignment
例:
%.o : CFLAGS = -O
CFLAGS变量将作用于所有以.o
结尾的目标。
若一个目标匹配多个模式,多个模式中有同名的特定于模式的变量,则选取名字最长的模式中定义的值,例:
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
lib/%.o: CFLAGS := -fPIC -g
%.o: CFLAGS := -g
all: foo.o lib/bar.o
lib/bar.o既匹配%.o
,也匹配lib/%.o
,这两个模式中都定义了名为CFLAGS
的变量,在构建lib/bar.o时,将选取模式名更长的那个模式中定义的CFLAGS
值,也就是-fPIC -g
。
若不希望先决条件继承变量,可在变量前加private,例:
EXTRA_CFLAGS =
prog: private EXTRA_CFLAGS = -L/usr/local/lib
prog: a.o b.o
a.o b.o不会从prog中继承EXTRA_CFLAGS变量。
MAKEFILE_LIST
make正在处理的文件名称列表,按照被解析的顺序排序。
.DEFAULT_GOAL
默认处理的目标
# Query the default goal.
ifeq ($(.DEFAULT_GOAL),)
$(warning no default goal is set)
endif
.PHONY: foo
foo: ; @echo $@
$(warning default goal is $(.DEFAULT_GOAL))
# Reset the default goal.
.DEFAULT_GOAL :=
.PHONY: bar
bar: ; @echo $@
$(warning default goal is $(.DEFAULT_GOAL))
# Set our own.
.DEFAULT_GOAL := foo
输出:
no default goal is set
default goal is foo
default goal is bar
foo
7.1 条件示例
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
不带else的条件语句:
conditional-directive
text-if-true
endif
带有else的条件语句:
conditional-directive
text-if-true
else
text-if-false
endif
带有else if的条件语句:
conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif
conditional-directive:
ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"
如果想测试变量是否是一个非空值,空白字符不会被认为是非空值,除非使用strip函数去除空格,就像这样:
ifeq ($(strip $(foo)),)
text-if-empty
endif
ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"
ifdef variable-name
若变量的值是非空值,则为true
ifndef variable-name
使用MAKEFLAGS变量和findstring配合起来
archive.a: …
ifneq (,$(findstring t,$(MAKEFLAGS)))
+touch archive.a
+ranlib -t archive.a
else
ranlib archive.a
endif
类似变量用法:
$(function arguments)
${function arguments}
参数与函数名之间用一个或多个空格或制表符分隔,多个参数之间使用逗号分割
$(subst from,to,text)
字符串替换函数,将text
的from
替换成to
,例:
$(subst ee,EE,feet on the street)
会生成fEEt on the strEEt
$(patsubst pattern,replacement,text)
模式替换函数,将text
中符合pattern
模式的子串替换为replacement
。
pattern中可能包含"%"作为通配符,用于替换一个单词中的任意数量的字符。若replacement中也包含一个%,那么该%就代表pattern中%匹配到的值。
若需匹配‘%’本身,则需要添加反斜杠"\"进行转义。
例:
$(patsubst %.c,%.o,x.c.c bar.c)
会产生结果x.c.o bar.o
。
$(strip string)
移除前导或后缀空格。
$(findstring find,in)
$(filter pattern…,text)
返回文本中匹配任何模式单词的所有空格分隔的单词,删除任何不 匹配的单词。
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
结果就是foo.c bar.c baz.s。
$(filter-out pattern…,text)
与filter反着来,返回不匹配的单词。
$(sort list)
对内容排序:
$(sort foo bar lose)
结果是:bar foo lose。
$(if condition,then-part[,else-part])
去除condition前后空格,然后展开,如果为任何非空字符串,则condition为true,否则为false。
如果condition为true,则返回then-part的值,否则返回else-part的值(若没有else-part,则返回空字符串)。
$(or condition1[,condition2[,condition3…]])
返回第一个空字符串,若全是空字符串,则返回空字符串。
$(and condition1[,condition2[,condition3…]])
若包含空字符串,则返回空字符串,负责返回最后一个字符串
语法:
$(call variable,param,param,…)
各个param被赋值给临时变量$(1)
、$(2)
...
没有参数的call函数调用没有意义。
reverse = $(2) $(1)
foo = $(call reverse,a,b)
foo的值是 b,a。
用于获取变量信息。语法是:
$(origin variable)
函数结果是一个字符串,可能是:
该函数与make之外的世界进行沟通。
执行shell命令,将输出中的换行都替换为空格,若有尾随换行,则将其删除。
contents := $(shell cat foo)
make的退出状态:
‘-f’ or ‘--file’
默认找GNUmakefile, makefile, and Makefile
默认找第一个
也可以通过命令行参数指定一个或多个目标。
如果可以找到如何制作某个文件的隐式规则,甚至该目标都不用包含在Makefile中。
避免自己制定目标的recipe,可以不编写目标的recipe,或者根本就不编写规则,make会挑选隐式规则去构建该目标。比方说:
foo : foo.o bar.o
cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
上例中用到了foo.o,但是没有为此编写规则,make会为其寻找一个隐式规则,不论此时foo.o是否存在。
隐式规则会为foo.o添加若干先决条件以及recipe,如果你想为foo.o目标添加更多的先决条件,比方说添加一些头文件什么的,也可以显式的为foo.o添加一个规则,里边添加上先决条件,但不写recipe。
对于同一个目标,可能有多个隐式规则都符合它。比方说对于.o目标来说,它可能从.c文件编译来,也可能由.p文件编译来,make在处理.o目标时,会先查找当前存在什么类型的源文件,当前存在.c,则使用C编译器,当前存在.p,则使用Pascal编译器。
C程序
n.o is made automatically from n.c with a recipe of the form
$(CC) $(CPPFLAGS) $(CFLAGS) -c
.
Linking a single object file
n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is
$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
$@
规则目标的文件名。
$%
目标成员名称。
$<
第一个先决条件的名称。
$?
比目标新的所有先决条件的名称,它们之间有空格。
$^
所有先决条件的名称,它们之间有空格。
$+
就像'$^',但不止一次列出的先决条件会按照它们在 makefile 中列出的顺序重复。
$*
隐式规则匹配的词干。The stem with which an implicit rule matches
$?
已更改的先决条件