GNU make 使用

153 阅读6分钟

变量

  • name = value 这种定义变量可以引用后面定义的变量的值,但不可以引用本身
  • name := value 这种定义变量在引用后面定义变量时为空,但可以引用本身
  • name ::= value 与 := 是等价的,但这是符合POSIX标准的,而 := 不是
  • name ?= value 仅当变量name未定义时才将其值设为value
  • name += value 向变量name中值中追加字符串value
  • name != value value被当成一个shell命令执行,并将执行的结果赋值给name,结果中开头的换行被删除,中间的换行被换成空格
  • 以上给变量的赋值方式也可以通过命令行的方式定义,并且默认会覆盖makefile内的定义
  • 使用override给变量赋值有最高优先级,还会覆盖命令行的定义,并且后面的非override的赋值都会忽略。
  • 多行变量使用define和endef来定义,例如:
define muline +=
hello
world
endef

muline 是变量名 +=是赋值操作,和普通变量一样,可以省略,默认是= define 和 endef之间的每一行都是换行结束的变量的内容,使用上和普通变量一样

  • 取消变量定义用undefine,对多行变量也适用,直接将变量置空和undefine在使用flavor和origin函数时有区别
  • make启动时会将环境变量转换为同名同值的make变量,makefile中的显式赋值或命令行参数会将其覆盖,-e选项可以让环境变量覆盖显式赋值或命令行参数

自动变量

  • $@ 目标名称
  • $< 第一个先决条件
  • $? 比目标新的先决条件的列表
  • $^ 所有先决条件的列表(不包含重复条件)
  • $+ 所有先决条件的列表(包含重复条件)
  • $| order-only 先决条件的列表
  • $* 模式规则中匹配中的词干
  • 通过在以上变量后面加 'D'或 'F' 来表示对应的目标名称和文件名称,如:(@D)表示目标的目录,(@D)表示目标的目录,(<F)表示第一个先决条件的文件
  • 变量替换的两种形式:
  1. $(var:a=b) 将变量var中每个单词结尾的a换成b
  2. 与上一种一样,但是可以有模式匹配%

内建目标

多目标

伪目标

  • 如果一个目标是内建目标.PHONY的先决条件,则该目标就是伪目标,伪目标无论对应的目标文件是否存在以及最后更新时间是多少都会被执行。
  • .PHONY目标的先决条件始终被解释为普通文件名称,而不是模式,即使包含了%
  • 执行.PHONY的先决条件时会跳过隐式规则搜索,所以能提升性能
  • 如果伪目标是真实目标的先决条件,则真实目标永远会被执行,因为伪目标会永远被执行,其做为依赖对应的目标自然也会被执行

先决条件类型

  • 分为普通先决条件和order-only先决条件,使用管道符号|分开,前面的是普通先决条件,后面的是order-only先决条件。order-only先决条件不会检查其最后修改更新时间,只要存在即使比目标更新也不会更新目标

双冒号规则

  • 指的是目标后面紧跟的是双冒号而不是单冒号。
  • 如果一个目标出现在多个规则中,若是都是单冒号规则,则后面的规则会覆盖前面的规则;如果是都是多冒号规则,则这些目标是相互独立的,会依次执行。
  • 一个没有依赖的规则,当目标已存在时,如果是单冒号规则,则目标不会被更新;如果是双冒号规则,则目标会被更新

VPATH变量和vpath指令

后缀规则

  • 已过时的,使用普通模式规则来代替。
  • 目标 .c.o 相当于 %.o : %.c;目标.c 相当于 % : %.c
  • 已知的后缀目标都是内建目标.SUFFIXES的先决条件,可以通过为.SUFFIXES目标添加更多的先决条件来添加自定义后缀
  • 变量SUFFIXES是默认的后缀列表,为.SUFFIXES目标添加先决条件不会改变该变量的值

隐式规则

模式规则

静态模式规则

静态体现在目标上,与普通模式规则相比,其目标是确定的,而普通模式规则中只要目标匹配模式,可以有无数个目标 。所以静态模式规则可以是默认目标(列表中的第一个),而普通模式规则不可以(不确定第一个目标到底是什么)

自动生成依赖

条件指令

ifX (conditional)
    statement
else ifX
    statement
else
    statement
endif

这里的ifX可以是ifeq ifneq ifdef ifndef。ifeq和ifneq 表示条件是否相等和是否不相等,conditional是两个要比较的参数;ifdef和ifndef表示变量是否定义和是否没有定义,conditional是一个变量名。else 都是可选的

函数

字符串处理函数
  • $(subst from,to,text) 将text中所有的from都替换成to
  • $(patsubst pattern,replacement,text) 将text中所有与pattern匹配的单词都替换成replacement, text是一个以空格分隔的单词列表,pattern支持%
  • $(strip string) 将string中前后的空格都移除,并且中间连续的空格换成一个空格
  • (findstring find,in) find中查找in,找到返回find,否则返回空
  • $(filter patterns,text) 返回text中与pattern匹配的单词列表,text是空格分隔的单词列表,pattern支持%, 可以有多个,以空格分隔
  • $(filter-out pattern…,text) 与filter相反,返回不匹配的单词列表
  • $(sort list) 将以空格分隔的单词列表list按字母顺序进行排序,并删除重复的单词
  • $(word n,text) 返回text中的第n个单词
  • $(wordlist s,e,text) 返回text中第s到第n个单词
  • $(words text) 返回text中单词的个数
  • $(firstword names) 与$(word 1,names) 作用相同
  • $(lastword names) 返回names中的最后一个单词,与 (word(word (words names),names)的作用相同
文件名处理函数
  • $(dir names) 提取names中文件的目录名,names是一系列以空格分隔的带有目录名的文件名
  • $(notdir names) 与dir相反,将文件名中的目录名去掉
  • $(suffix names) 提取文件中的后缀名
  • $(basename names) 与suffix相反,提取文件名的basename
  • $(addsuffix suffix,names) 给names中的每个文件名都添加一个后缀suffix
  • $(addprefix prefix,names) 给names中的每个文件名都添加一个前缀prefix
  • $(join list1,list2) 将list1中的第N个单词与list2中的第N个单词连接起来,返回连接后的结果
  • $(wildcard pattern) 返回与pattern匹配的文件列表,pattern支持通配符 * ? ~ []
  • $(realpath names) 返回names中每个文件的真实路径,对符号链接会解析
  • $(abspath names) 返回names中每个文件的绝对路径,不会解析符号链接

条件函数

  • $(if condition,then-part\[,else-part\]) 如果condition的结果是非空,则then-part的结果作为函数的返回值,否则else-part的结果作为函数的返回值,else-part可省略
  • $(or condition1[,condition2[,condition3…]]) 将第一个非空的contition作为函数的结果,如果所有的contidion都是空则函数的结果就是空
  • $(and condition1[,condition2[,condition3…]]) 如果所有的condition都是非空,则最后一个condition就是函数的结果,否则函数结果就是空

打印函数

  • $(error msg)
  • $(warning msg)
  • $(info msg)

其他函数

  • $(foreach var,list,text) 首先将var和list展开,list展开后有多少个单词就执行多少次循环,每次循环都将list展开的单词赋值给以var展开结果为名称的变量,并执行text,text执行的结果是追加的,最终的结果就是函数的结果,在text里可以访问var展开的变量。
  • $(let var [var ...],[list],text) 和foreach类似,不同的是把list展开的多个单词依次赋值给var展开的多个变量
  • $(file op filename[,text]) 读取文件, op的值 > >> < 分别表示覆盖写入、追加写入、读取。写入时如果文件不存在会自动创建,text是要写入的内容,读取时不需要该参数
  • $(call variable,param,param,…) 调用一个函数,变量variable在这里相当于一个函数,param是要传递给函数的参数,在函数内部可以通过(N)访问第N个参数,(N) 访问第N个参数,(0)是变量的名字
  • $(eval text) text会被扩展为makefile新的变量、目标、规则的定义,然后make再解析它,没有返回值,在哪里调用该函数就相当于在哪里定义了新的变量、目标、规则。
  • $(shell command) 执行一个shell命令,并将命令的结果为做函数的返回值
  • $(origin variable) 查询变量variable的来源,返回值有:
  1. undefined 未定义过的
  2. default 默认定义,如CC
  3. environment 从环境变量继承的
  4. environment override 从环境变量继承且覆盖了makefile中的设置
  5. file makefile文件中定义的
  6. command line 命令行定义的
  7. override 用override定义的
  8. automatic 自动变量
  • $(flavor variable) 查询变量variable的扩展类型,返回值有:
  1. undefined 未定义过的
  2. recursive 递归扩展的变量
  3. simple 简单扩展的变量
  • $(value variable) 在不扩展变量的情况下获取变量的值,也就是说如果变量的值中包含了$,会当成普通字符

命令行参数

  • -f / --file --makefile 指定makefile文件的名称,而不使用默认名称
  • -B / --always-make 始终假设目标是过时的
  • -C dir 将dir目录而非当前目录当成make的工作目录
  • -d 打印更多的调试信息 相当于 --debug=a
  • -e 让环境变量中的变量定义优先于makefile文件中的变量定义
  • -i / --ignore-errors 忽略recipe中的错误
  • -j jobs 同时运行多个任务