变量与宏
变量可以由任意除了:、#、=的字符组成,在访问变量的时候,如果变量名超过一个字母,要用小括号括起来(花括号{}也可以,但不建议用)。
习惯上用大写字母表示用户在命令行上或环境中自定义的常数,用小写字母表示makefile中出现的变量,单词之间都用下划线隔开。
变量的值由赋值符号右边删除前导空格的所有字符组成。和shell一样,没有定义的变量会被解析为空,举一个例子:
1  | MAKE_DEPEND := $(CC) -M  | 
假如$(CC)没有被定义,MAKE_DEPENDd的值最终为<space>-M,这里的空格并不会被删除。
赋值类型
| 类型 | 解释 | 
|---|---|
:= | 
简单扩展,扩展后的字面值被存入变量 | 
= | 
经递归扩展,储存的实际是待扩展的字符串,等到用的时候再执行扩展 | 
?= | 
条件赋值,只在变量值不存在的时候才执行赋值 | 
+= | 
附加运算,将文本附加在变量里。var = $(var) sss的写法是错误的,会无限递归,然而使用:=就可以 | 
宏
使用define指令可以定义宏,宏其实是一种特殊的变量,使用格式为:
1  | define MACRO_NAME  | 
我们可以像使用变量一样使用宏。
工作目标的专属变量
可以在定义规则之前定义专属变量,格式如下:
1  | target...: variable [:+?]= value  | 
执行该条规则后,variable的值会自动恢复。
变量来自何处
- 文件:直接定义在makefile中,或者使用include引入
 - 命令行:如:
make CFLAGS=-g,命令行中的变量会覆盖环境变量和文件变量,当然也可以使用override指令在文件中强行赋值(不建议,可能会逼得用户砸电脑。。) - 环境:环境变量会被自动定义成make变量(优先级很低),同时可以使用export来导出变量到环境中(unexport作用相反)。过多依赖环境变量会降低makefile的可移植性。
 
条件指令
格式如下:
1  | if-condition  | 
其中if-condition可以是:
1  | ifdef variable-name  | 
strip函数可以去除变量首尾空格,建议在比较时使用,如:
1  | ifeq "$(strip $(OPTIONS))" "-d"  | 
include指令
include指令可以引入其他文件。有意思的是:make可以把makefile本身作为一个可能的工作目标。因此,下面的makefile是可以正常工作的:
1  | include foo.mk  | 
bar.mk的代码为:
1  | $(warning Reading FILENAME)  | 
当make读进整个makefile后,会试着寻找可用来重建当前所执行的makefile的规则,如果找到了,会处理此规则,然后再检查makefile是否更新,如果更新了,会清除内部状态,并重新读进此makefile。
include默认会在当前目录、--include-dir或-I以及make自己被编译的目录(如/usr/local/include)下寻找文件,如果没找到,会报错并结束运行,如果想让其忽略无法加载的文件,可以在include前面加一个-。
标准make变量
MAKE_VERSION
make的版本编号
CURDIR
正在执行make进程的当前工作目录,也就是当前shell的pwd值。在make命令中使用-C可以变更目录。
MAKEFILE_LIST
make读进的makefile文件名列表,当前被执行的是该列表的最后一项。
MAKECMDGOALS
返回对于当前所运行的make而言,make命令行上指定的工作目标。不包括命令行选项或对变量的赋值。
.VARIABLES
当前make从各个makefile文件读进的变量名称构成的列表,不含工作目标的专属变量
函数
自定义函数
使用前面介绍的宏来定义函数!可以使用$n来访问第n个参数。在调用函数时,使用以下格式:
1  | $(call macro-name[, param1 ...])  | 
内置函数
使用函数的语法为:
1  | $(function-name arg1[, argn])  | 
其中第一个参数的前导空格会被删除,而从第二个开始前导空格会被保留。
字符串函数
$(filter pattern …,text)
将text视为一系列被空格隔开的单纯,与pattern比较厚,返回相符者。
$(filter-out pattern…,text)
与filter相反,选出不符的每个单词
$(findstring string…,text)
返回在text里面搜索到的string;没找到返回空值(无%)
$(subst search-string,replace-string,text)
不具备通配符能力的替换函数
$(patsubst search-pattern,replace-pattern,text)
具备通配符能力的替换函数
$(words text)
返回text中单词数量
$(words, n,text)
返回text中的第n个单词
$(firstword text)
返回text中的第一个单词
$(wordlist start,end,text)
返回text中范围从start(含)到end(含)的单词
两个重要函数
$(sort list)
排序list参数并移除重复的项目
$(shell command)
将command参数扩展后传递给subshell执行
文件名函数
$(wildcard pattern…)
返回符合pattern的文件名,如
1  | source := $(wildcard *.c *.h)  | 
还可以用于测试文件是否存在,如
1  | dot-example-exists := $(wildcard ~/.emacs)  | 
如果返回空字符串,则说明用户主目录不包含.emacs这个文件。
$(dir list…)
返回list中每个单词的目录部分
$(notdir name…)
返回文件路径的文件名部分
$(suffix name…)
返回参数中每个单词的后缀
$(basename name…)
与suffix相反,返回不含后缀的部分
$(addsuffix suffix,name…)
将后缀添加到每个name后面,同理addprefix可以添加前缀
$(join prefix-list,suffix-list)
重建dir和notdir分解的表
流程控制
$(if condition,then-part,else-part)
make对condition求值时,会对其进行扩展,如果扩展后包含任意字符(即使是空格),求值结果也是真。
$(error text)
输出fatal error message。之后make会以返回值2结束运行
$(foreach variable,list,body)
反复扩展文本,例如:
1  | letters := $(foreach letter,a b c d,$(letter))  | 
其它函数
$(strip text)
移除前后空格
$(origin variable)
返回变量来源,可能的返回值有:
- undefined: 未定义
 - default: 来自make的内置数据库
 - environment: 来自环境(没有覆写)
 - file: 来自makefile
 - command line: 来自命令行
 - override: 来自override指令
 - automatic: 自动变量
 
$(warning text)
类似error,但是不会导致make结束运行。
高级函数
eval函数
将文本直接放入make解析器。用起来十分复杂,有很多神奇用法,真正用到的时候再学吧。
传递参数
使用函数参数传递最符合模块化的标准,有时为了方便,我们也可以使用变量前缀来共享。
系列导航