写Recipe(四)

5 写Recipe

规则(rule)中通常包含多条recipes,这些recipes用来被shell按顺序依次执行。这些命令用来更新规则(Rule)中的目标文件(target)。

用户使用许多不同种类的shell程序,但是默认情况下makefile中的recipe总是使用/bin/sh进行解释(除非指定了别的shell)。

5.1 Recipe语法

Makefile中包含两种语法,其中大部分使用make语法,但是recipe需要被shell解释,所以其使用shell语法。make不需要理解shell语法,只是在将recipe提交给shell时做简单的转化。

recipe中的每一行必须以tab开始,除非recipe与target和prerequisite放在一行,并用;隔开。makefile中,在规则语境(rule context)下的,任意以tab开始的一行,被视为rule的一部分。所谓规则语境,是指从一条规则开始,到下一条规则或变量定义为止的范围。空行或注释行会被忽略。

这些规则包含以下结论:

  • 以tab开始的空行不是空行,被视为空recipe。

  • recipe中的注释不是make中的注释,它会原封不动的传给shell,是否被视为注释,由shell决定。

  • 在规则语境下,以tab缩进定义的变量被视为recipe的变量,会被传给shell,而不是被视为一个make变量定义。

  • 在规则语境下,以tab缩进的条件表达式(ifdefifeq等)被视为recipe的一部分传给shell。

5.1.1 分割Recipe行

makefile的语法中,一个逻辑行可以被\分割成若干物理行,这样一列由\分开的行被视为一行recipe,并且shell也会把它当作一条语句执行。

与makefile其它地方的\相比,recipe中新行前的\不会被移除。\和新行字符都会被保留并传给shell,其解释取决于shell。如果下一行的第一个字符是recipe的前导符(默认是tab),那么这个\换行符会被移除,并且不会在该处增加空格。

例如,下面makefile中,对于all目标:

all :
        @echo no\
space
        @echo no\
        space
        @echo one \
        space
        @echo one\
         space

被视为四个隔开的shell命令,其输出如下:

nospace
nospace
one space
one space

更复杂的例子:

all : ; @echo 'hello \
        world' ; echo "hello \
    world"

会通过如下命令调用shell:

echo 'hello \
world' ; echo "hello \
    world"

根据shell的引号规则,会有如下输出:

hello \
world
hello    world

注意到,新行字符\在双引号字符串中被移除,在单引号字符串中被保留。这是默认shell(/bin/sh)的处理方式,如果你指定了不同的shell,行为会有不同。

有时候你想要在单引号中将一个长行分割为多行,但是并不想在引号中使用新行字符\。通常这种情况出现在将一个脚本传递给一个语言时(如Perl),脚本中的额外的\会被解释称不同的意思,甚至导致语法错误。一种简单的处理方式是,将引号中的字符串或者整个命令放入一个make变量中,然后在recipe中使用这个变量。在这种情况下,makefile的新行引用规则就会生效,\会被移除。重写上面的例子:

HELLO = 'hello \
world'

all : ; @echo $(HELLO)

会得到如下输出:

hello world

你也可以使用target-specific变量,使变量和使用它的recipe关系更紧密(后面介绍)。

5.1.2 在recipe中使用变量

另一种处理recipe的方式是展开其中的变量引用。这发生在make读完了所有的makefile,并且确定target需要被重建之后。因此不需要重建的target的recipe并不会发生变量展开。

recipe中的变量和函数引用与makefile中其它地方的变量和函数引用有完全相同的语法和语义。它们有相同的引用规则:如果要在recipe中输入一个$符号,必须使用$$。在shell中使用$引用变量,因此你必须清楚你想要引用的变量是make变量(使用$)还是shell变量(使用$$)。例如:

LIST = one two three
all:
        for i in $(LIST); do \
            echo $$i; \
        done

将会传递如下命令给shell:

for i in one two three; do \
    echo $i; \
done

其产生预期结果:

one
two
three

5.2 Recipe Echoing(回显)

通常,make在执行recipe之前先打印每一行recipe。我们称之为echoing(回显),因为这就像打印你的输入。

当一行以@开始时,这行就不再回显。在将这行传递给shell前,@被丢弃。通常这个语法用于那些只是打印信息的命令,例如用echo命令标识程序处理makefile的过程:

@echo About to make distribution files

当make使用-n--just-print选项时,只是回显将要执行的recipe,而并不真正执行它们。此时,即使以@开头的recipe也会被打印。当只是想查看make将要执行的recipe命令时很有用。

选项-s或者--silent将阻止make回显任何信息,相当于所有的recipe以@开头。makefile中没有prerequisites的特殊目标.SILENT具有类似功能,但是@更灵活。

5.3 执行Recipe

当执行recipe更新目标文件时,每一行recipe都会调用一个子shell,除非特殊目标.ONESHELL被指定。

请注意:这意味着shell中设置的变量,以及shell中执行的改变当前工作目录的程序(如cd)将不会影响到之后的recipe命令。如果想要cd影响下一个声明语句,就要把两个声明放到一个recipe行中。这样,make就会使用一个shell运行这一行,这个shell会依次执行这条recipe中的声明。例如:

foo : bar/lose
        cd $(@D) && gobble $(@F) > ../$@

这里我们使用shell的与(AND)操作符,因此当cd失败时,这个脚本就会退出,而不会在一个错误的目录中执行gobble命令。

5.3.1 使用One Shell

有时候,你想要把recipe中所有的命令行都发个一个shell。在两种情况下着非常有用:第一,当recipe包含很多命令行时,它可以在不需要额外处理的情况下优化makefile。第二,你可能想在recipe命令中包含新行字符(例如你使用的SHELL有不同的解释方式)。如果特殊目标.ONESHELL出现在makefile中的任何位置,那么这个makefile中的每个target的所有recipe行就使用一个shell。recipe行之间的新行字符会被保留。例如:

.ONESHELL
foo : bar/lose
        cd $(@D)
        g
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值