变量与宏
变量可以由任意除了:
、#
、=
的字符组成,在访问变量的时候,如果变量名超过一个字母,要用小括号括起来(花括号{}
也可以,但不建议用)。
习惯上用大写字母表示用户在命令行上或环境中自定义的常数,用小写字母表示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解析器。用起来十分复杂,有很多神奇用法,真正用到的时候再学吧。
传递参数
使用函数参数传递最符合模块化的标准,有时为了方便,我们也可以使用变量前缀来共享。
系列导航