Makefile 原理
make
命令会在当前目录下找名字叫Makefile
或makefile
的文件。- 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
- 如果这个目标文件不存在,或是它所依赖的后面的
.o
文件的文件修改时间要比这个文件新,那么,他就会执行后面所定义的命令来生成这个文件。 - 如果这个文件所依赖的
.o
文件也不存在,那么make
会在当前文件中找目标为.o
文件的依赖性,如果找到则再根据那一个规则生成.o
文件。 - 当然,你的
.c
文件和.h
文件是存在的,于是make
会生成.o
文件,然后再用.o
文件生成make
的终极任务,也就是可执行文件了。
清空目标文件的规则
.PHONY : clean
clean :
rm $(objects)
.PHONY
是伪目标的意思
make 的工作方式
- 读入所有的 Makefile
- 读入被 include 的其它 Makefile
- 初始化文件中的变量
- 推导隐晦规则,并分析所有规则
- 为所有的目标文件创建依赖关系链
- 根据依赖关系,决定哪些目标要重新生成
- 执行生成命令
变量
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上 $
符号,但最好用小括号 ()
或是大括号 {}
把变量给包括起来。如果你要使用真实的 $
字符,那么你需要用 $$
来表示。
Makefile 变量的赋值
立即变量:定义的时候就已经确定了该变量的值。( :=
、 +=
)
延时变量:使用该变量的时候,才展开该变量,并确定该变量的值。( =
、 ?=
、 define
)
符号 | 含义 | 变量类型 |
---|---|---|
= |
递归赋值,将整个 Makefile 文件展开之后,再决定变量的值 | 延时变量 |
:= |
直接为变量进行赋值 | 立即变量 |
+= |
在变量后面追加值 | 立即变量 |
?= |
若前面没有定义该变量,则此处赋值,如果前面已经定义了,则此处不再赋值 | 延时变量 |
自动化变量
$@
:表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,$@
就是匹配于目标中模式定义的集合。$%
:仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是foo.a(bar.o)
,那么,$%
就是bar.o
,$@
就是foo.a
。如果目标不是函数库文件(Unix 下是.a
,Windows 下是.lib
),那么,其值为空。$<
:依赖目标中的第一个目标名字。如果依赖目标是以模式(即%
)定义的,那么$<
将是符合模式的一系列的文件集。注意,其是一个一个取出来的。(就是依赖中的第一个)$?
:所有比目标新的依赖目标的集合。以空格分隔。$^
:所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。$+
:这个变量很像$^
,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
变量值替换
格式: $(var:a=b)
或 ${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
或者使用
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
Makefile 关键字
override
如果一个变量的值需要在编译选项中指定或由系统传入,那么 makefile 中可以使用 override
关键字来设置,使这个变量的赋值被忽略
define
使用 define
关键字可以定义多行变量
define two-lines
echo foo
echo $(bar)
endef
wildcard
作用是让通配符 (Makefile 中的通配符就是 *
, %
算 pattern,不是通配符)在变量或函数中展开,通常用于提取指定目录的某一类型文件。因为在 Makefile 的规则中,函数中的通配符是不会被展开的。
all:$(subst .c, .o, $(wildcard *.c))
%.o:%.c
gcc -o $@ $<
export
将变量导出,以便于所有的子 makefile 都可以使用
include
和 C 语言的 #include
一样,将后面的文件展开到当前位置
Makefile 函数
字符串替换函数 subst
$(subst <from>, <to>, <text>)
属性 | 解释 |
---|---|
名称 | 字符串替换函数 subst |
功能 | 把字串 <text> 中的字符串 <from> 替换成 <to> |
返回 | 函数返回被替换过后的字符串 |
示例:
$(subst ee, EE, feet on the street)
结果值是 fEEt on the strEEt
模式字符串替换函数 patsubst
$(patsubst <pattern>, <replacement>, <text>)
属性 | 解释 |
---|---|
名称 | 模式字符串替换函数 patsubst |
功能 | 查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 <pattern> ,如果匹配的话,则以 <replacement> 替换。 这里 <pattern> 可以包括通配符 % ,表示任意长度的字串。如果 <replacement> 中也包含 % ,那么 <replacement> 中的这个 % 将是 <pattern> 中的那个 % 所代表的字串。(可以用 \ 来转义,以 \% 来 表示真实含义的 \% 字符) |
返回 | 函数返回被替换过后的字符串 |
示例:
$(patsubst *.c,*.o,foo.c bar.c)
结果值是 foo.o bar.o
查找字符串函数 findstring
$(findstring <find>, <in>)
属性 | 解释 |
---|---|
名称 | 查找字符串函数 findstring |
功能 | 在字串 <in> 中查找字串 <find> |
返回 | 如果找到,那么返回,否则返回空字符串 |
示例:
$(findstring a,a b c)
$(findstring a,b c)
第一个函数返回 a
字符串
第二个返回空字符串
过滤函数 filter
$(filter <patterns>, <text>)
属性 | 解释 |
---|---|
名称 | 过滤函数 filter |
功能 | 以模式 <patterns> 过滤字符串 <text> 中的单词,保留符合模式 <patterns> 的单词。可以有多个模式 |
返回 | 返回符合模式 <patterns> 的字串 |
示例:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
$(filter %.c %.s, $(sources))
返回的值是
foo.c bar.c baz.s
去空格函数 strip
$(strip <string>)
属性 | 解释 |
---|---|
名称 | 去空格函数 strip |
功能 | 去掉字符串 <string> 开头和结尾的空字符 |
返回 | 返回被去掉空格的字符串值 |
示例:
$(strip a b c )
结果值是 a b c
排序函数 sort
$(sort <list>)
属性 | 解释 |
---|---|
名称 | 排序函数 sort |
功能 | 给字符串 <list> 中的单词排序(升序) |
返回 | 返回排序后的字符串 |
示例:
$(sort foo bar lose)
结果值是 bar foo lose
备注:sort 函数会去掉其中重复的单词
取单词函数 word
$(word <n>, <text>)
属性 | 解释 |
---|---|
名称 | 取单词函数 word |
功能 | 取字符串 <text> 中第 <n> 个单词(从 1 开始) |
返回 | 返回:返回字符串 <text> 中第 <n> 个单词。如果 n 比其中的单词数要大,返回空字符串。 |
示例:
$(word 2, foo bar baz)
返回值是 bar
去单词串函数 wordlist
$(wordlist <s>, <e>, <text>)
属性 | 解释 |
---|---|
名称 | 去单词串函数 wordlist |
功能 | 从字符串 <text> 中取从 <s> 开始到 <e> 的单词串。 <s> 和 <e> 是一个数字 |
返回 | 返回字符串 <text> 中从 <s> 到 <e> 的单词串。如果 <s> 比 <text> 中的单词数大,那么返回空字符串。如果 <e> 大于 <text> 的单词数,那么从 <s> 开始,到结束的单词串 |
示例:
$(wordlist 2, 3, foo bar baz)
返回值是 bar baz
单词个数统计函数 words
$(words <text>)
属性 | 解释 |
---|---|
名称 | 单词个数统计函数 words |
功能 | 统计 <text> 中字符串中的单词个数 |
返回 | 返回 <text> 中的单词数 |
示例:
$(words,foo bar baz)
返回值是 3
备注:如果我们要取中最后的一个单词,我们可以这样: $(word $(words <text>),<text>)
首单词函数 firstword
$(firstword <text>)
属性 | 解释 |
---|---|
名称 | 首单词函数 firstword |
功能 | 取字符串 <text> 中的第一个单词 |
返回 | 返回字符串 <text> 的第一个单词 |
示例:
$(firstword foo bar)
返回值是 foo
备注:这个函数可以用 word
函数来实现: $(word 1,<text>)
。
取目录函数 dir
$(dir <names>)
属性 | 解释 |
---|---|
名称 | 取目录函数 dir |
功能 | 从文件名序列 <names> 中取出目录部分。目录部分是指最后一个反斜杠 / 之前的部分。如果没有反斜杠,那么返回 ./ |
返回 | 返回文件名序列 <names> 的目录部分 |
示例:
$(dir src/foo.c hacks)
返回值是 src/ ./
取文件函数 notdir
$(notdir <names>)
属性 | 解释 |
---|---|
名称 | 取文件函数 notdir |
功能 | 从文件名序列 <names> 中取出非目录部分,非目录部分是指最后一个反斜杠 / 之后的部分 |
返回 | 返回文件名序列 <names> 的非目录部分 |
示例:
$(notdir src/foo.c hacks)
返回值是 foo.c hacks
取后缀函数 suffix
$(suffix <names>)
属性 | 解释 |
---|---|
名称 | 取后缀函数 suffix |
功能 | 从文件名序列 <names> 中获取后缀序列 |
返回 | 返回文件名序列 <names> 的后缀序列,如果文件没有后缀则返回空字串 |
示例:
$(suffix src/foo.c src-1.0/bar.c hacks)
返回值是 .c .c
取前缀函数 basename
$(basename <names>)
属性 | 解释 |
---|---|
名称 | 取前缀函数 basename |
功能 | 从文件名序列 <names> 中取出各个文件名的前缀部分 |
返回 | 返回文件名序列 <names> 的前缀序列,如果文件没有前缀,则返回空字串。 |
示例:
$(basename src/foo.c src-1.0/bar.c hacks)
返回值是 src/foo src-1.0/bar hacks
加后缀函数 addsuffix
$(addsuffix <suffix>, <names>)
属性 | 解释 |
---|---|
名称 | 加后缀函数 addsuffix |
功能 | 把后缀 <suffix> 加到 <names> 中的每个单词后面 |
返回 | 返回加过后缀的文件名序列 |
示例:
$(addsuffix .c, foo bar)
返回值是 foo.c bar.c
加前缀函数 addprefix
$(addprefix <prefix>, <names>)
属性 | 解释 |
---|---|
名称 | 加前缀函数 addprefix |
功能 | 把前缀 <prefix> 加到 <names> 中的每个单词前面 |
返回 | 返回加过前缀的文件名序列 |
示例:
$(addprefix src/,foo bar)
返回值是 src/foo src/bar
连接函数 join
$(join <list1>, <list2>)
属性 | 解释 |
---|---|
名称 | 连接函数 join |
功能 | 把 <list2> 中的单词对应地加到的 <list1> 单词后面。如果 <list1> 的单词个数要比 <list2> 的多,那么, <list1> 中的多出来的单词将保持原样。如果的 <list2> 单词个数要比 <list1> 多,那么 <list2> 多出来的单词将被复制到 <list1> 中 |
返回 | 返回连接过后的字符串 |
示例:
$(join aaa bbb,111 222 333)
返回值是 aaa111 bbb222 333
遍历函数 foreach
$(foreach <var>, <list>, <text>)
把参数 <list>
中的单词逐一取出放到参数 <var>
所指定的变量中,然后再执行 <text>
所包含的表达式。每一次 <text>
会返回一个字符串,循环过程中 <text>
的所返回的每个字符串会以空格分隔,最后当整个循环结束时, <text>
所返回的每个字符串所组成的整个字符串以空格分隔,将会是 foreach 函数的返回值。所以, <var>
最好是一个变量名, <list>
可以是一个表达式,而 <text>
中一般会使用这个参数来依次枚举中的单词。
示例:
names := a b c d
files := $(foreach n, $(names), $(n).o)
上面的例子中, $(name)
中的单词会被挨个取出,并存到变量 n
中, $(n).o
每次根据 $(n)
计算出一个值,这些值以空格分隔,最后作为 foreach
函数的返回,所以, $(files)
的值是 a.o b.o c.o d.o
。
foreach 中的
<var>
参数是一个临时的局部变量,foreach 函数执行完后,参数<var>
的变量将不在作用,其作用域只在 foreach 函数当中。