Makefile变量初始化和导出

时间:2010-05-15 02:46:46

标签: variables makefile export

somevar := apple
export somevar
update := $(shell echo "v=$$somevar")

all:
    @echo $(update)

我希望苹果作为命令的输出,但它是空的,这使我认为导出和:=变量扩展发生在不同的阶段。怎么克服这个?

4 个答案:

答案 0 :(得分:32)

问题是export将变量导出到命令使用的子shell;它不适用于其他作业的扩展。因此,不要试图从规则之外的环境中获取它。

somevar := apple
export somevar

update1 := $(shell perl -e 'print "method 1 $$ENV{somevar}\n"')
# Make runs the shell command, the shell does not know somevar, so update1 is "method 1 ".

update2 := perl -e 'print "method 2 $$ENV{somevar}\n"'
# Now update2 is perl -e 'print "method 2 $$ENV{somevar}\n"'

# Lest we forget:
update3 := method 3 $(somevar)

all:
    echo $(update1)
    $(update2)
    echo $(update3)
    perl -e 'print method 4 "$$ENV{somevar}\n"'

答案 1 :(得分:14)

@Beta's answer包含关键指针:使用GNU make标有export的变量仅适用于[为配方命令启动的shell (属于规则的命令),令人遗憾的是不是$(shell ...) 的调用(他们只看到make在启动时看到的环境)

有一个解决方法,但是: 显式将导出的变量作为环境变量传递给shell函数:< / p>

update := $(shell somevar='$(somevar)' perl -e 'print "$$ENV{somevar}"')

通过在<var>=<val>前加上shell命令,该定义将作为环境变量添加到命令所看到的环境中 - 这是一个通用的shell功能。

警告:@Kaz在评论中指出,如果$(somevar)包含某些字符,此方法会出错,因为变量扩展是逐字(没有转义),它可以破坏生成的shell命令,并建议以下变体也适用于嵌入式'实例(将输入值分解为带引号'的单引号子串拼接):

update := $(shell somevar='$(subst ','\'',$(somevar))' perl -e 'print "$$ENV{somevar}"')

这应该适用于除多行之外的所有值(这种情况很少见;对于我不知道的多行值没有解决方法)。

旁注,文字$字符。 in值必须表示为$$,否则make会将它们解释为对自己变量的引用。

请注意,我故意不选择OP的原始语句update := $(shell echo "v=$$somevar")进行演示,因为它包含了一个陷入困境的陷阱:由于shell的原因评估命令行,somevar=apple echo v=$somevar不评估为v=apple,因为{/ 1}}参考在 $somevar生效之前已展开。要在这种情况下达到预期效果,您必须使用 2 语句:somevar=apple 功能


关于错误与特征的争论:

虽然可以认为update := $(shell export somevar="$(somevar)"; echo "v=$$somevar")函数应该看到与配方命令相同的环境,但文档没有做出这样的承诺 - 请参阅http://www.gnu.org/software/make/manual/make.html#Shell-Function。相反,http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion仅提及将导出变量提供给 recipe 命令。

答案 2 :(得分:10)

运行makefile

foo:=apple
export foo
all:
        @echo ">"$(shell echo "$$foo")
        @echo ">""$$foo"

给我(在环境中未定义foo)

$ make
>
>apple

$ make foo=bar
>
>apple

$ export foo=bar; make
>bar
>apple

$ export foo=bar; make foo=bar
>bar
>bar

尝试使用引用的表单(update := "v=$$somevar")并在运行命令时让shell处理扩展(您仍需要导出)

答案 3 :(得分:1)

虽然export$(shell ...)无法很好地协作,但有一个简单的解决方法。我们可以通过命令行将数据传递给shell脚本。

当然,环境通过对于逃避和引用的问题是强有力的。但是,shell语言有一个单引号引用方法'...'来处理所有内容。唯一的问题是没有办法在那里得到单一的报价;但当然这可以通过终止引用,反斜杠 - 转义所需的单引号并开始一个新引用来解决:换句话说:

ab'cd -> 'ab'\''cd'

$(shell ...)执行的shell脚本中,我们只生成var='$(...)'形式的变量赋值,其中$(...)是一些make表达式,用于插入适当的转义素材。因此,Makefile

somevar := apple with 'quoted' "stuff" and dollar $$signs

shell_escape = $(subst ','\'',$(1))

update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v > file.txt)

.phony: all

all:
    cat file.txt

示例运行:

$ make
cat file.txt
apple with 'quoted' "stuff" and dollar $signs

如果我们想要将环境变量传递给命令,我们可以使用shell语法VAR0=val0 VAR1=val1 ... VARn=valn command arg ...来实现。这可以通过上述Makefile的一些小改动来说明:

somevar := apple with 'quoted' "stuff" and dollar $$signs

shell_escape = $(subst ','\'',$(1))

update := $(shell somevar='$(call shell_escape,$(somevar))' env > file.txt)

.phony: all

all:
        grep somevar file.txt

执行命令

$ make
grep somevar file.txt
somevar=apple with 'quoted' "stuff" and dollar $signs

file.txt包含一系列环境变量,我们可以看到somevar。如果GNU Make中的export做了正确的事情,我们就能做到:

export somevar
update := $(shell env > file.txt)

但最终结果是一样的。

由于您想要的最终结果是echo $(update),所以即使GNU Make将导出的变量传递给shell_escape,您也会$(shell ...)。也就是说,再看一个Makefile

somevar := apple with 'quoted' "stuff" and dollar $$signs

shell_escape = $(subst ','\'',$(1))

update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v)

.phony: all

all:
    @echo '$(call shell_escape,$(update))'
    @echo $(update)

输出:

apple with 'quoted' "stuff" and dollar $signs
apple with quoted stuff and dollar