Makefile命令与变量

命令

make的命令默认是被 “/bin/sh” 即UNIX的标准 Shell 解释执行的。
每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键开头的,那么make会认为其是一个空命令。

显示命令

  1. 一般情况,make会打印出各个命令的执行顺序,如果不想打印某条命令执行过程,可以在其前加上符号 @ 。例如:@echo balabala,其输出不会打印echo balabala。
  2. -n 、–just-print 两个参数即只打印命令执行步骤但不执行。
  3. -s 、–slient 两个参数即全面禁止打印。

执行命令

当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。例如:
下面两个例子,前者pwd的输出还是makefile所处文件目录,而后者才能在cd命令基础上执行pwd。

exem1 :
	cd /home/xxx
	pwd

exem2 :
	cd /home/xxx;pwd

命令出错

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。
若想让make忽略错误继续执行(比如mkdir文件夹已存在的错误),有以下几个方法:

  • 在需要忽略错误的命令前添加符号 “-” 。
  • 全局方法,键入make指令时添加参数 “-i” 或 “ --inogre-errors” 。
  • 使用目标 “.IGNORE” ,这个目标下的命令自动忽略错误。

注意:make中的 “-k” 即 “ --keep-going” 参数的作用是如果当前规则有命令出错,则终止当前规则但继续执行其他规则。

嵌套执行

项目较大时,通常我们将不同模块放在不同目录,并为不同模块单独编写一个makefile,然后在总控makefile中调用次级makefile,方便修改维护。
在主makefile中调用下级makefile有以下两种写法:

targeta :
	cd adir && $(MAKE)
targetb :
	$(MAKE) -C bdir

这里的$(MAKE)用变量主要为了需要给make添加一些参数时便于修改。
最高一级的makefile称为总控makefile,总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数。
想要传递变量到下级makefile,可以使用export参数修饰,不想传递的参数可以用unexport修饰。
需要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,其总是要传递到下层Makefile中。MAKEFLAGS是一个系统级的变量,想不传递它需要在make参数中添加上 “MAKEFLAGS=” ,即将它在下级makefile重新赋空值。
即:

targeta :
	cd adir && $(MAKE) MAKEFLAGS=

(但是make命令中的有几个参数并不往下传递,它们是 “-C” ,“-f” ,“-h” ,“-o” 和 “-W” )
make参数中 “-w” 可以让make进入不同目录时将进入的目录打印出来,在嵌套执行中比较实用。“-C”会自动打开这个选项,而当有“-s”或“–no-print-directory”,“-w”不会生效。

命令包

相同的命令序列可以使用define关键字,语法上以define开始,以endef结束,使用时把它当成变量即可,例如:

define myshells
	mkdir xxx
	touch xxx
	...
endef

use :
	$(myshells)

make在执行命令包时,命令包中的每个命令会被依次独立执行。

变量

变量在声明时需要赋初始值,使用时要在变量名前加上美元 $ 符号,用()或{}括起来。若要使用真实的 $ 符号,需要用 $$ 来表示。

  • makefile中所有的变量本质上是一个字符串替换,它在使用的位置精确地展开。
  • 变量的展开总是在使用时才展开,若未执行到则不会主动展开,这与C语言中的预编译要区别开。
  • 变量可以使用在任何你想的位置,比如目标、依赖、命令、甚至其他变量之中。

变量中的变量

变量是可以迭代,定义一个变量时可以使用其他变量的值,这使得我们可以在声明一个变量之后再具体确定它的真实值,比如:

CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar

但在使用中我们需要避免递归定义,这会导致make进入无限的展开,例如:

CFLAGS = $(CFLAGS) -O

A = $(B)
B = $(A)

为了避免这种情况,可以使用 := 操作符。
附:

  • = 最基本的赋值
  • := 覆盖之前的值
  • ?= 如果没有被赋值过就赋予等号后面的值
  • += 添加等号后面的值

例:

x := foo
y := $(x) bar
x := later

则最终的结果等价于,y = foo bar,x = later。 :=不能使用后面的变量。

一般来说,make的变量声明中会忽略最前面的空格和最后面的空格,但有时我们又需要这些空格,要想保留某些空格,可以用空变量开头和注释符#结尾来解决,例如:

nullstring :=
space := $(nullstring) # end of the line

此处nullstring是一个空变量,space的声明中以nullstring开始,紧接一个空格,而后以#注释符表示变量的结束,于是这样就定义了一个值为一个空格的变量space。
而当我们不需要尾部的空格时,那就必须注意跟在行尾的注释了,它可能会给我们的变量多加几个空格但这也许并不是我们的本意。

一些方便的用法

$(var:a=b)

其作用为将var中所有以a结尾的字符串的a替换为b,例如:

foo := a.o b.o c.o
bar := $(foo:.o=.c)

执行的结果,bar的值就是 “a.c b.c c.c” 了。
另外,替换规则中也可以使用静态模式,将上述例子换为静态模式,就是:

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)

变量套娃

变量的值可以继续作为变量,比如:

x = y
y = z
a := $($(x))

$(x)的值是 “y” , $(y)的值是 “z” ,于是 $(a)的值最终为 “z” 。
总之记住变量都是字符串,可以套娃拼拼拼就完事了。

追加变量

即使用 += 赋值符。需要注意几点:

  • 追加字符串会自动在两者间加一个空格。
  • 当变量是第一次赋值,那么+=相当于=。
  • 当变量前面有定义,那么+=会使用上一次操作用的赋值符来进行附加。例如:
    variable := value
    variable += more
    等价于:
    variable := value
    variable := $(variable) more
    但如果是这种情况:
    variable = value
    variable += more
    由于前次的赋值符是“=”,所以“+=”也会以“=”来做为赋值,那么岂不会发生变量的递归定义,这是很不好的,所以make会自动为我们解决这个问题,我们不必担心这个问题。

override 指示符

如果有变量是通过make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。如果你想在Makefile中设置这类参数的值,那么,你可以使用“override”指示符。其语法是:
override < variable > = < value >
override < variable > := < value >
当然,你还可以追加:
override < variable > += < more text >
也可以使用多行定义:
override define foo
bar
endef

环境变量

make运行时可以把系统的环境变量加载到Makefile文件中,但如果Makefile文件中对相应环境变量有重新定义,那么make会使用文件中的值,除非make使用了-e参数。
优先级为: 命令行 > 文件内定义 > 系统设置
默认情况,命令行中设置的变量会自动传递到下层Makefile,环境变量也是一样的,若命令行没有设置又想传递到下层,需要使用export关键字。

目标局部变量

前面提到的变量声明定义操作都是全局变量,Makefile也支持针对某个目标设置局部变量,允许和全局变量重名,只影响指定的规则以及连带规则,不影响规则链之外的全局变量。

语法如下:
< target … > : < variable-assignment >
< target … > : overide < variable-assignment >

  • target 指定的目标
  • variable-assignment 赋值表达式
  • 可以使用前面提到的 override 忽略命令行传入的值

另外,GNU 的 make 支持模式变量,上面提到的<target …>可以使用模式字符串,即包含至少一个 “%” 的字符串,即可为所有符合模式的目标设置一个局部变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值