Make语言详解
Make
赋值操作
=:赋值,将右值赋给左值
?=:条件赋值,如果变量没有初始化,就用右边初始化左边
:=:直接赋值,不过展开方式不同
::=:与:=一致
+=:追加赋值
!=:右值为一条shell命令,shell命令的输出赋给左值
!=例子
1 | |
等价于
1 | |
在Makefile中使用$,要用$$表示,shell中用\$
变量
变量的扩展方式
简单扩展
赋值时就确定了变量的值,不管它是否引用了其他变量,:=,::=,!=都是简单扩展
循环递归扩展
在赋值时不确定,如果引用了其他变量,make会先确定其他变量,然后再确定该变量,=,?=属于循环变量
直接扩展
+=属于直接扩展,原变量是啥,左边就是啥
创建私有变量
默认是全局变量,若要使用私有变量,使用private
删除变量
删除变量使用unset
内置变量
$@:表示所有目标
$<:依赖中的第一个依赖
$^:依赖列表中的所有文件
$?:依赖列表中所有更新的文件
$*:在模式中,该变量代表茎,也就是%的部分
~:用户家目录
./:Make的当前目录
@:禁止命令回显
-:出错则忽略,而不是报错退出
+:当make -n时,只有带+号的会执行
@::占位符,防止make出现Nothing to be done
变量可以定义在命令行中,会覆盖掉make自带的变量
override:让make采用Makefile的赋值
make启动时,所有来自环境的变量都成为make变量
make会在执行一个规则的命令脚本之前,立刻创建自动变量
常用Make变量
VPATH:搜文件的路径
MAKEFLAGS:Make的参数
MAKELEVEL:Make的嵌套层数
MAKECMDGOALS:工作目标的列表
CURDIR:当前工作目录
MAKEFILE_LIST:读取的Makefile列表,最后一个是当前的Makefile
MAKE_VERSION:make的版本
VARIABLES:make从各个Makefile读进的变量列表,不含工作目标
变量扩展规则
- 对于变量赋值,make会立即扩展其左边
- 对于
=,?=,其右边会被延后到变量使用时进行扩展,在分析依存图时进行 :=的右边会立即被扩展- 如果
+=的左边是一个简单变量,+=的右边会被立即扩展,否则,其求值会被延后 - 对于宏定义,其宏名会被立即扩展,宏体会被延后扩展
- 对于规则,工作目标和依赖总是被立即扩展,命令则总是被延时扩展
工作目标的专属变量
1 | |
专属变量的定义会附加在工作目标之上,且只在该工作目标以及相应的任何必要条件被处理时才会发生作用
此类变量的赋值动作会在处理工作目标时进行
make参数
-f:指定一个文件作为Makefile
-k:遇到错误不停止,一次发现所有的编译错误
-n:输出将要执行的步骤,而不真的执行
-c:切换到指定目录,执行该目录下的Makefile
-l:指定make去寻找的链接库libName.so或libName.a
-s:静默输出
伪目标
.PHONY:指示不要将一个目标当作文件来处理
伪目标总是最新的
通配符
*
*匹配任何东西,包括空
?
通常在依赖中,匹配所有更新的目标
使用通配符本身时,需要使用转义字符\
如果使用OBJ = *.o,一般情况下,会匹配通配符,但是若当前目录下没有可匹配的文件,就会将*.o这个字符串赋给OBJ
通配符函数
1 | |
其好处是,如果没有匹配的文件,那么赋给左边的将是一个空
模式规则
模式规则类似于普通规则,模式规则中包含模式字符%,包含有模式字符%的目标被用来匹配一个文件名,%可以匹配任何非空字符串,依赖中的%取值依赖于目标的%
静态模式规则
1 | |
另一种常用语法
1 | |
将OBJ中所有.c后缀文件替换为.o后缀
当模式出现在目标和依赖时,由make进行扩展
当模式出现在命令中时,由shell进行扩展
Makefile的执行顺序
- 读入所有的Makefile
- 初始化变量
- 分析规则,若有显式规则,则加入依赖库,若没有,则分析隐式规则
- 根据依赖关系,决定哪些目标需要更新
- 执行命令
命令脚本初始化的顺序
- 读取程序代码
- 扩展变量
- 对make表达式求值
- 执行命令
- 命令脚本的求值会被延后的执行的时候
- ifdef的处理会在读入的时候
- 在执行之前,首先会看是否有可扩展的变量和可求值的表达式
- 宏被扩展时,会为每一行增加tab
Make内置函数
调用方式:${<function_name> <arguments>},参数之间以逗号comma分割,函数名和参数之间以空格分割
字符串操作函数
subst——字符串替换
1 | |
返回替换过后的字符串
patsubst——模式字符串替换
1 | |
查找text中的单词(以空白符分割)是否符合pattern,如匹配则以replacement替换,如果replacement中含有%,则与pattern中的%含义一致
返回被替换后的字符串
strip——去空格函数
1 | |
去掉string中开头和结尾的空格
返回去掉首位空格后的字符串
findstring——查找字符串
1 | |
在in中查找find子串
如果找到,返回find,否则返回空
filter——过滤函数
1 | |
以pattern模式过滤text字符串中的单词,保留符合pattern模式的单词,可以有多个模式
返回符合pattern模式的子串
filter-out——反过滤函数
1 | |
去掉符合pattern模式的子串,可以有多个模式
返回不符合pattern模式的子串
sort——排序函数
1 | |
给list中的单词进行排序(升序),
返回排序后的字符串,会去掉重复单词
word——取单词函数
1 | |
取字串text中的第n个单词,从1开始
返回text中的第n个单词,如果n比text 中的单词数大,则返回空
wordlist——取单词串函数
1 | |
从text中取从ss到e的单词
返回子串,若ss比末尾大,返回空
words——单词个数统计函数
1 | |
统计text中单词个数
返回数量
firstword——首单词函数
1 | |
取text中第一个单词
返回单词字串
文件名操作函数
dir——取目录函数
1 | |
从文件名序列中取出目录部分,目录部分是指最后一个/之前的部分,如果没有/,则返回./
返回目录名
notdir——取文件名函数
1 | |
从字符串序列中取出文件名,指最后一个/之后的内容
返回文件名
suffix——取后缀函数
1 | |
返回后缀,若无后缀返回空
basename——取前缀函数
1 | |
返回前缀,若无则返回空
addsuffix——加后缀函数
1 | |
把后缀suffix加到names的每个单词后面
返回加了后缀的字符串
addprefix——加前缀函数
1 | |
把前缀prefix加到names的每个单词前面
返回加了前缀的字符串
join——连接函数
1 | |
将list2对应连接到list1后面,若list1长,list1中多出来的保持原样,list2长,则复制到list1后面
返回连接后的字符串
realpath——取真实路径函数
1 | |
对names中的每个文件名,反会规范的绝对路径,规范名指的是不含.,..,也不包含任何重复路径,分隔符,符号链接
返回路径,失败返回空
abspath——取绝对路径
1 | |
对names中的每个文件名,返回一个不含.,..的绝对路径,也不含重复路径,与realpath相比,abspath不解析符号链接,也不要求文件名names引用现有的文件或目录,使用通配符函数来测试函数是否存在,如果目标不存在,也返回绝对地址
返回绝对地址
功能函数
foreach——循环遍历
1 | |
将list中的单词逐个放入var指定的变量中,然后执行text中的表达式,结果放入返回值中
var是个临时变量,作用域只在foreach里
if——条件判断
1 | |
判断condition,如果为真,执行then-part,否则,执行else-part(若存在)
call——调用某函数
1 | |
唯一一个可以调用创建的新函数,param*会取代expression中的变量
call处理参数时,第二个及之后的参数会保留空格
origin——参数来源
1 | |
判断这个变量来自哪里
可能的返回值
1 | |
shell——运行shell命令
1 | |
与’`’相同,其会启动一个子shell,然后运行命令,将结果赋给左边的变量
error——报错函数
1 | |
产生一个致命错误,text是其输出信息
warn——警告函数
1 | |
产生一个警告,text是其输出信息
eval——二次命令
1 | |
将文本直接放入make的解析器中
简单而言,eval会将后面的求值结果,当作make命令再执行一次
条件指令
1 | |
执行上述指令时,variable-name不需要用$()包裹
条件指令可以用于宏定义和命令脚本中
1 | |
test可以表示为"a" "b"或(a, b)
这里的test有点微妙,如果采用()的形式,逗号comma后的空格会被忽略,逗号comma之前的会被保留
引入指令
也就是include
引入指令流程:
- 当make看到include时,首先对通配符及变量进行扩展,然后试着引入该文件
- 如果该文件存在,则流程继续,如果该文件不存在,make产生报告,并继续读取其余Makefile
- 当所有读取完成,make会从规则库中找出任何可用来更新引入文件的规则,如果找到了一个相符的规则,就更新工作目标,如果任何一个引入文件规则被更新,make会清除其内部数据,并重新引入该Makefile
- 如重复以上流程后,仍有引入文件不存在,则make报错