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报错