. 变量
. 函数
. 需要注意的地方
. 通用的, 好的行为
. 不通用的, 不好的行为
自动变量
$@: 目标文件完整路径名(通过目录搜索得到)
$<: 依赖文件列表中的第一个文件的完整路径名(通过目录搜索得到)
$^: 依赖文件完整路径名(通过目录搜索得到)
$?: 以来文件列表中所有改过的文件
$*: 静态模式被展开为 "茎"
$$$$: 当前进程号
全局变量
VPATH = 目录1:目录2:... <=> VPATH = ../Common
指定所有目标文件和规则依赖文件的搜索路径。 如果文件不在当前目录, 则从左到右依次搜索。多个目录以空格或 ‘:’ 分隔。
.LIBPATTERNS = PATTERN <=> .LIBPATTERNS = lib%.so lib%.a
指定库文件的文件名扩展方法。 多个方法之间以空格分隔。搜索时首先尝试第一个方法, 遍历所有路径, 失败后向右尝试第二个方法。
RM // RM=rm -f
'-rm -f' 中的 -f 选项是为了消除缺少要删除的文件时给出的提示并防止其退出. 命令前的 '-' 是为了避免 rm 命令出错导致退出 make.
SHELL <=> SHELL = /bin/sh
make 对所有命令的解析使用的时此变量的所指向的程序。
环境变量中无此同名变量。
MAKE <=> MAKE = /bin/make
make 的执行程序。
默认情况下, -t, -n, -q 会忽略依赖命令的执行, 但是当此目标的依赖命令中含有 MAKE 变量时, 此依赖命令将会被执行。同时, 执行 make 的命令选项会被 MAKEFLAGS 传递给下一级 make。
MAKEFLAGS
make 执行的命令选项。 此变量可以传递给下层的子 make。
当不需要 MAKEFLAG 给子 make 传参时, 应该在命令中将变量置空,
subsystem: cd substr && $(MAKE) MAKEFLAGS= # 取消参数继承
CURDIR
make 的工作目录。 当使用 -C 选项后, 将重置此变量。
MAKELEVEL
当前 make 的调用深度。 最上一级是 0。 下一级是 1。
函数
$(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
返回值: 替换后的字符.
src:=a.c b.c c.c $(patsubst %.c,%.o,$(src)) # a.o b.o c.o
$(VAR:PATTERN=REPLACEMENT)
名称: 模式字符串替换
功能: 将 VAR 中符合 PATTERN 的字符串替换为 REPLACEMENT
返回值: 替换后的字符串
说明: 此函数等价于简化版的 patsubst
src:=a.c b.c c.c $(src:.c=.o) # a.o b.o c.o
$(strip STRINT)
名称: 去除空格
功能: 1. 去除开头和结尾的空格
2. 将不同单词间连续的多个的空字符合并为一个空字符
返回值: 开头结尾不含空格, 使用单一空格分隔单词的字符串
src:= a.c b.c c.c d.c x:=$(strip $(src)) # a.c b.c c.c d.c
$(findstring KEY,STR)
名称: 查找字串
功能: 在 STR 中寻找 KEY 字串
返回值: 如何找到 KEY 则返回 KEY, 否则返回空字串
src:= a.c b.c c.c d.c x:=$(findstring a.c,$(src)) # a.c
$(filter PATTERN...,TEXT)
名称: 过滤函数.
功能: 将 TEXT 中符合模式 PATTERN 中的任何一个模式的字符串保留. 不同 PATTERN 之间用空格分隔. 结果字符串用空格分隔单词.
返回值: 空格分隔的, 符合 PATTERN 模式的字符串.
src:= a.cpp b.c c.o d.c x:=$(filter %.c %.cpp,$(src)) # a.cpp b.c d.c
$(filter-out PATTERN...,TEXT)
名称: 反过滤函数
功能: 将不符合 PATTERN 模式中的任何一个的字符串保留. 不同 PATTERN 之间用空格分隔. 结果字符串用空格分隔单词.
返回值: 空格分隔的, 符合 PATTERN 模式的字符串.
src:= a.cpp b.c c.o d.c x:=$(filter-out %.c %.cpp,$(src)) # c.o
$(sort LIST)
名称: 排序并去除重复
功能: 1. 将 TEXT 中的字符串按升序排序
2. 并去除重复的单词
返回值: 升序的, 单词唯一的字符串
src:= a.cpp c.o d.c b.c d.c x:=$(sort $(src)) # a.cpp b.c c.o d.c
$(word N,TEXT)
名称: 取单词函数
功能: 取 TEXT 中的第 N 个单词, N 从 1 开始. 如果 N 为 0, 则 make 报错.
返回值: 字符串中的第 N 个单词.
src:= a.cpp c.o d.c b.c d.c x:=$(word 2,$(src)) # c.o
$(wordlist S,E,TEXT)
名称: 取单词列表
功能: 从 TEXT 中取第 S 个到第 E 个单词, 包括第 S 和第 E 个. 若 E 大于 S, 则返回空字串.
返回值: 空格区分单词的字符串.
说明: windows xp 下 make 3.75 未成功.
$(words TEXT)
名称: 统计单词
功能: 统计 TEXT 以空白符分割的单词的个数.
返回值: 单词的个数
src:= a.cpp b.c c.c x:=$(words $(src)) # 3
$(firstword TEXT)
名称: 取首单词
功能: 取字符串中的第一个单词. 此函数等价于 $(word 1,TEXT)
返回值: 字符串的第一个单词
src:= a.cpp b.c c.c x:=$(firstword $(src)) # a.cpp
$(dir NAMES...)
名称: 取目录函数
功能: 获得文件的目录, 以 "/" 结尾.
返回值: 该文件的目录
x:=$(dir makefile) # ./
$(notdir NAMES...)
名称: 取文件名
功能: 获得文件的文件名.
返回值:文件的文件名(基础名 + 扩展名)
x:=$(notdir c:/windows/explorer.exe) # explorer.exe
$(basename NAMES...)
$(suffix NAMES...)
名称: 取文件基础名/扩展名
功能: 获得文件的基础名/扩展名. 当文件不含基础名/扩展名时, 则返回空值(相当于不返回其值).
返回值: 文件的基础名/扩展名.
x:=$(suffix c:/windows/explorer.exe fd) # ./ @echo $(x) # .exe @echo $(words $(x)) # 1x:=$(basename c:/windows/explorer.exe fd) # ./ @echo $(x) # c:/windows/explorer fd
$(addprefix PREFIX, NAMES...)
$(addsuffix SUFFIX, NAMES...)
名称: 增加前缀/后缀.
功能: 为 NAMES 增加前缀/后缀
返回值: 增加前缀/后缀后的字符串
x:=$(addprefix ./,explorer.exe hacks) @echo $(x) # ./explorer.exe ./hacks
$(join LIST1,LIST2)
功能: 链接函数
功能: 链接相应位置的字符串
返回值: 链接后的字符串x:=$(join a b c,.o.cpp.h) # a.o b.cpp c.h
$(wildcard PATTERN)
名称: 通配函数
功能: 匹配同一模式的文件
返回值: 文件名x:=$(wildcard *.c ???.o) # 所有的 .c 文件, 和任意三个字符为基础名的 .o 文件
$(foreach VAR,LIST,TEXT)
名称: 循环
功能: 执行一组操作
返回值: 空格分隔的多次执行表达式后的结果.dirs:=a b c d $(foreach dir,$(dirs),mkdir $(dir)) # 创建 a b c d 文件夹
$(if CONDITION,TRUE,FALSE)
名称: 条件执行
功能: 当 CONDITION 为真时, 执行 TRUE 的命令, 否则执行 FALSE 的命令. 当 CONDITION 为假, 且不存在 FALSE 时, 则返回空.
返回值: 执行的结果
$(call VARIABLE,PARAM...)
名称: 调用函数
功能: 调用一个定义的函数. PARAM 被赋值到 $(1) $(2)... 等临时变量去. VARIABLE 是一个变量名, 因此不需要加 "$". VARIABLE 在定义时必须使用递归展开.
返回值: VARIABLE 执行的结果.reverse=$(2) $(1) ls:=$(call reverse,a,b) # b a
$(value VARIABLE)
待补充
$(eval NAME)
名称: 二次求值函数
功能: 将 NAME 变量的内容作为 makefile 的内容. 即, 变量中的内容将被作为 makefile 中的内容被解析.
返回值: 空var:=server: server.o $(eval $(var)) # 相当于在 makefile 中直接写上 server: server.o do: g++ -Wall -g -o server server.o # 这里切记不可使用自动化变量, 因为 $@ 的内容是 "do", $^ 的内容是 "".
$(origin VARIABLE)
名称: 取变量原型
功能: 获得变量的定义方式
返回值: 字符串形式的变量定义方式说明. 具体返回值请查阅相关文档.var:=1234 type:=$(origin var) # file
$(shell CMD)
名称: 执行 shell 命令函数
功能: 在新的 shell 中执行 CMD 命令
返回值: CMD 在 shell 中执行的结果cmd:=ls files:=$(shell $(cmd)) # 当前目录下的所有文件和文件夹
$(waring TEXT)
名称: 警告
功能: 当执行到此函数时, 将会产生一个警告, 但并不退出 make
返回值: 空var1:=$(warning 1234) # 当 make 执行, 且任何命令执行前会产生一条警告, 警告的内容是 1234 var2=$(warning abcd) # 当此变量被引用时, 会产生一条警告信息
$(error TEXT)
名称: 致命错误
功能: 产生致命错误报警, 并退出 make 的执行.
返回值: 空
vpath PATTERN DIRECTORIES <=> vpath %.h ../headers:../Common
vpath PATTERN // 清除之前为 PATTERN 所设置的搜索路径
vpath // 清除所有文件搜索路径
指定所有符合 PATTERN 的相同 一类文件的搜索路径为 DIRECTORIES。 搜索顺序从左到右, 不同路径以空格或 ‘:’ 分隔。 多个 vpath 命令之间相互独立。 PATTERN 中使用 ‘%’ 指定文件名(不含扩展名)。
特殊目标
.PHONY <=> .PHONY: clean
其依赖都被认为是 伪目标.
.SUFFIXES
其依赖是当前 make 需要处理的后缀.
.DEFAULT
其命令是当一个文件作为其他目标的依赖, 然而自己却不是另外一个规则的目标时, 默认执行的命令. make 缺少此文件并无法找到重建此文件的规则时就执行 .DEFAULT 目标所指定的命令.
.PRECIOUS
当 make 在执行期间被中断后, 默认情况下会删除此过程中已经重建的目标。 此目标后的依赖将不会被删除。
.INTERMEDIATE
待补充
.SECONDARY
待补充
.DELETE_ON_ERROR
.IGNORE
为其指定依赖文件. 将忽略创建这些文件的错误. 当此目标没有依赖文件时, 将忽略所有命令执行的错误.
此目标的命令无意义.
.LOW_RESOLUTION_TIME
其依赖文件被认为是低分辨率时间戳.
此目标的命令无意义.
.SILENT
指定其依赖文件, 在重建这些文件的时候不打印命令. 当此目标没有依赖文件时, 将不打印任何命令.
相当于给每条命令前加 "@".
此目标的命令无意义.
同义词
-n 或 --silent
命令的前导 "@".
.EXPORT_ALL_VARIABLES
此目标后的变量将会被传递给子 make.
此目标 应当无依赖.
.NOTPARALLEL
所有命令串行执行. 忽略 make 命令中的参数 '-j'.
此目标的命令无意义.
选项
大多数选项都会通过 MAKEFLAG 传递给子 make. -C, -f, -o, -W 四个选项不会通过 MAKEFLAGS 传递. -j 虽然会传递给子 make, 但是父 make 和子 make 之间会建立通信, 保证一共有 N 个任务在执行.
--job
-j [ n] <=> -j 3
// 最多同时允许 3 条指令执行
允许 make 同时执行多条指令。
没有没有参数表示没有上限限制。
--ignore-errors
-i
忽略所有执行指令的错误, 但会给出错误信息。 相当于为每条指令加了前导 ‘-’。
--keep-going
-k
出现错误时不立即退出, 而是继续后续命令的执行。
-C
重置 CURDIR 变量。 即, 改变当前工作目录。
-w
开始跟踪 make 执行的目录. 此选项在使用 -C 或使用 "cd" 进入一个目录时自动开启.
惯用法
使用 $(RM) 代替 rm 命令. RM = rm -f.
在 make 执行失败时, 执行 make clean 明确清除此次错误重建的所有目标。
在定义空命令时, 不要使用命令行方式, 因此这样和空行看起来没有区别.
empty_target: # Bad
较好的方法是:
empty_target: ; # Good
为了重建其依赖文件, 而定义一个不是实际文件的目标, 最好使用为目标而不是空命令. 因为一个实际不存在的文件的依赖文件, 可能不会被正确的重建.
对于空命令, 最好不要给它指定依赖文件, 以避免特殊情况下产生错误.
将对变量的注释放在单独的一行. 因为 make 对变量进行处理时, 变量 值中尾空格是 不被忽略的. 所以
dir:= /foo/bar # Directory to put the frobs in. Wrong!
$(dir) 的值将是红色背景部分.
除非必须, 否则 不要在 makefile 中 重新设置 SHELL 的值. 因为一个不正确的命令行解释程序可能会导致规则定义的命令执行失败, 甚至是无法执行.
变量
当引用一个没有定义过的变量时, make 默认它的值为空.
当执行 make 时, 如果通过命令行定义了一个变量, 那么它将替代在 makefile 中使用 '='/':='/define 定义的同名变量.
系统中所有环境变量对 makefile 都是可见的.
默认情况下, 只有命令行变量和环境变量才会传递给子 make 进程.
递归展开式变量
通过 '=' 或 'define' 定义的变量.
这种变量在引用的地方执行严格的文本替换. 并 在引用的地方进行递归的 展开.
如果此变量包含其他变量的引用, 则这些被引用的变量会在此变量被展开的同时被展开.
优点:
允许引用其它在它 之前没有定义的变量.
缺点:
变量的定义中直接或间接 包含自身, 会导致自身的 无限展开.
如果变量内有函数, 那么 每次展开, 就会 执行一次 函数. 多次边缘效应可能会导致不期望的结果.
.SILENT: # Backward reference foo=$(bar) # Function define bar echo * endef # Multiple side effects x=$(foo) y=$(foo) # Illegal: self contain var=$(var) -o
直接展开式变量
通过 ':=' 定义的变量.
在定义时, 对变量中对其它变量或者函数的引用进行展开. 最终的变量是一个实际需要的文本, 不包含任何变量或者函数的引用.
优点:
避免了 "递归展开式变量" 在 无限展开和 多次边缘效应上的不足.
缺点:
无法实现对 其后定义变量的引用.
.SILENT: # Ok, self contain # '$(var)' is empty var:=$(var) # define bar echo + endef # Ok, $x == $y foo:=$(bar) x:=$(foo) y:=$(foo) # Illegal: backward reference foo:=$(undefined) undefined:=cmd
条件赋值
通过 '?=' 定义或赋值一个变量.
此操作符的特点是: 如果之前不存在此变量, 则定义此变量并为变量赋值. 若之前存在此变量, 则不作任何操作.
多行定义
使用 define 对多行进行定义.
当定义中某一行以 [Tab] 开头时, 当引用此变量时这一行会被认为是 命令行处理.
追加操作符
通过 '+=' 操作的变量.
如果被追加的变量之前 没有定义, 那么 '+=' 会自动变成 '='.
如果之前 存在变量的 定义, 那么 '+=' 继承之前定义的变量风格.
一个变量使用 '=' 定义, 之后使用 '+=' 操作时不对此变量值中的任何引用进行替换展开.
一个变量使用 ':=' 定义, 之后使用 '+=' 操作时将会展开此变量的值, 而后在末尾添加需要追加的值, 并用 ':=' 重新给此变量赋值.
对于 递归展开式变量的追加, make 程序同样会按照递归展开式的定义来实现对变量的 重新赋值, 不会发生递归展开式变量展开过程的 无限循环.
override 修饰符
在 makefile 文件中, 通过此修饰符定义的变量 不会被命令行的同名变量所 覆盖.
当使用 '+=' 追加一个有 override 修饰符 定义的变量时, 必须使用带有 override 修饰符的方式追加.
export
此修饰符使变量能够传递给子 make 进程.
目标指定变量
只在一个规则, 以及此规则的目标引发的规则中起效.
可以与全局变量同名, 两者相互独立.
与全局变量有相同的优先级, 所以可能被命令行或者系统环境变量覆盖.
可以通过 override 修饰符防止被覆盖.
CFLAGS:=-Wall EXEF:=foo.c bar.c foo: foo.c foo: CFLAGS+=-O2 bar: bar.c bar: CFLAGS+=-g # 对 foo.c 附加使用 -O2 # 对 bar.c 附加使用 -g $(EXEF:%.c=%): debug.h $(CC) $(FLAGS) $(EXEF) -o $@
模式指定变量
PATTERN ...: VARIABLE-ASSIGNMENT
或
PATTERN ...: override VARIABLE-ASSIGNMENT
当 单独使用 '%' 作为 目标时, 指定的变量会对 所有类型的目标文件 失效.# 为所有 '.o' 的目标制定编译选项 %.o: CFLAGS+=-o
其他
当前目录永远是第一搜索路径。
VPATH 或 vpath 对隐式规则同样有效。
库的搜索路径顺序:
1. 当前目录
2. VPATH 或 vpath 指定的目录
3. /lib
4. /usr/lib
5. PREFIX/lib (在 linux 中是 /usr/local/lib)
在搜索库文件的时候, 以 .LIBPATTERNS 为轴心, 深度遍历所有路径。即先尝试 .LIBPATTERNS 中的第一个方法, 遍历所有路径后, 在尝试第二个 .LIBPATTERNS 中的方法。因此可以使用此变量防止 Make 在搜索库文件时的过度展开。
如果多个规则同时给出重建此目标的命令, make 将使用最后一个规则中所定义的命令, 同时提示错误信息. 一个例外是: 使用 '.' 开头的多规则目标文件可以在多规则中给出重建命令, 但是在 GNU make 中要避免使用.
每一行的命令都是独立的 shell 命令, 因此, 书写在不同行的 cd 命令不能改变其他行的工作目录。
在 makefile 中, 书写在同一行的多个命令属于一个完整的 shell 命令。 同一行的不同命令用 ‘;’ 隔开。希望把一个完整的 shell 命令写在多行, 可以使用 ‘\’
makefile 中绝大多数变量的值都可以直接从同名的系统环境变量中获取。
在指令执行完毕后, 其返回值会被检查。 默认情况下, 如果某一条指令返回值不为零, 则会放弃后续指令的执行。
'$' 在 makefile 中有特殊含义, 若要使用 '$' 在文件或变量名中, 需要使用 '$$'.
在 makefile 中, 变量和函数的展开类似于 C 语言的宏, 执行严格的文本替换.
在引用多余一个字符的变量时, 使用 "()" 或 "{}" 包围所有变量名, 这和 shell 有所不同.
要避免 make 的 -j 选项并行的使用 'ar' 命令操作同一静态库. 否则会破坏静态库, 甚至导致静态库不可用.
FAQ
’完整路径名‘ 就是绝对或相对路径 + 文件名。
"茎" 是在有类似文件名的文件中, 匹配其不同的部分. 比如: "bigoutput" 和 "littleoutput", 在静态模式 'bigoutput littleoutput: %output: Xoutput' 中, "bit" 和 "little" 就是其茎. 具体内容参阅 '静态模式'.
一个目标可以出现在多个规则中, 但不允许同时出现在普通规则和双冒号规则中.
每次 makefile 将要执行一条独立的指令时, 将启动一个新的 shell 执行它。 因此, 在不同独立的指令中, 不能相互传递环境。
shell 只解释纯粹的命令, 其前导 ‘-’ 是被 make 处理的。
空命令唯一的意义是防止 make 在执行时试图重建这个目标而去寻找隐含命令. 包括使用隐含规则中的命令和 .DEFAULT 指定的命令.
变量名是一个字符串, 这个有字幕或数字组成的字符串. 这个字符串代表一个变量, 如 var 就是一个变量名. 而变量的引用指通过变量名, 对此变量的引用, 以 "$" 开头, 括号包裹的形式出现, 如 "$(var)" 既是对变量 var 的引用.