Makefile变量初始化和导出

发布于 2024-09-01 06:58:43 字数 202 浏览 4 评论 0原文

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

all:
    @echo $(update)

我希望将 apple 作为命令的输出,但是它是空的,这让我认为导出和 := 变量扩展发生在不同的阶段。如何克服这个问题?

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

all:
    @echo $(update)

I was hoping to apple as output of command, however it's empty, which makes me think export and := variable expansion taking place on different phases. how to overcome this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清泪尽 2024-09-08 06:58:43

问题在于 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"'

输出是:

echo method 1 
method 1
perl -e 'print "method 2 $ENV{somevar}\n"'
method 2 apple
echo method 3 apple
method 3 apple
perl -e 'print "method 4 $ENV{somevar}\n"'
method 4 apple

The problem is that export exports the variable to the subshells used by the commands; it is not available for expansion in other assignments. So don't try to get it from the environment outside a rule.

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"'

The output is:

echo method 1 
method 1
perl -e 'print "method 2 $ENV{somevar}\n"'
method 2 apple
echo method 3 apple
method 3 apple
perl -e 'print "method 4 $ENV{somevar}\n"'
method 4 apple

送你一个梦 2024-09-08 06:58:43

@Beta 的回答包含关键指针:使用 GNU make变量标记为 < code>export 仅适用于[为]启动的 shell 配方命令(属于规则一部分的命令),遗憾的是不适用于 的调用$(shell ...) (它们只能看到 make 本身在启动时看到的环境)。

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

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

不过 在 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,因为 >$somevar 引用在 somevar=apple 生效之前扩展。在这种情况下,要达到所需的效果,您必须使用 2 语句: update := $(shell export somevar="$(somevar)"; echo "v=$$某些变量”)


至于 bug 与功能的争论:

虽然可以说 shell 函数应该看到与配方命令相同的环境,但文档使得没有这样的承诺 - 请参阅 http://www.gnu.org/ software/make/manual/make.html#Shell-Function。相反,http://www.gnu.org/software/make/manual /make.html#Variables_002fRecursion 仅提到使导出的变量可用于recipe 命令。

@Beta's answer contains the crucial pointer: with GNU make, variables marked with export are only available to [the shells launched for] recipe commands (commands that are part of rules), regrettably not to invocations of $(shell ...) (they only see the environment that make itself saw when it was launched).

There is a workaround, however: explicitly pass the exported variable as an environment variable to the shell function:

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

By prepending the shell command with <var>=<val>, that definition is added as an environment variable to the environment that the command sees - this is a generic shell feature.

Caveat: @Kaz points out in a comment that this method misbehaves if $(somevar) contains certain chars., because the variable expansion is verbatim (no escaping), which can break the resulting shell command, and suggests the following variant to also work with embedded ' instances (breaks the input value into single-quoted substrings with quoted ' spliced in):

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

This should work with all values except multi-line ones (which are rare; there is no workaround for multi-line values that I'm aware of).

On a side note, literal $ chars. in values must be represented as $$, otherwise make will interpret them as references to its own variables.


Note that I've deliberately NOT chosen the OP's original statement, update := $(shell echo "v=$$somevar"), for demonstration, because it contains a pitfall that muddles the issue: due to how the shell evaluates a command line, somevar=apple echo v=$somevar does NOT evaluate to v=apple, because the $somevar reference is expanded before somevar=apple takes effect. To achieve the desired effect in this case, you'd have to use 2 statements: update := $(shell export somevar="$(somevar)"; echo "v=$$somevar")


As for the bug-vs.-feature debate:

While it can be argued that the shell function should see the same environment as recipe commands, the documentation makes no such promise - see http://www.gnu.org/software/make/manual/make.html#Shell-Function. Conversely, http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion only mentions making exported variables available to recipe commands.

红ご颜醉 2024-09-08 06:58:43

运行 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 在运行命令时处理扩展 (您仍然需要导出)

Running the makefile

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

gives for me (with foo undefined in the environment)

$ make
>
>apple

$ make foo=bar
>
>apple

$ export foo=bar; make
>bar
>apple

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

Try using the quoted form (update := "v=$$somevar") and let the shell handle expansion when a command is run (you'll still need the export)

北笙凉宸 2024-09-08 06:58:43

虽然 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 命令来实现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

Run:

$ 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),因此无论如何您都会 shell_escape,即使 GNU Make 将导出的变量传递给 $(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

Although export does not play nicely with $(shell ...), there is a simple workaround. We can pass the data to the shell script via the command line.

Now of course, environment passage is robust against issues of escaping and quoting. However, the shell language has a single quote quoting method '...' which handles everything. The only problem is that there is no way to get a single quote in there; but of course that is solved by terminating the quote, backslash-escaping the needed single quote and starting a new quote: In other words:

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

In the shell script executed by $(shell ...) we just generate a variable assignment of the form var='$(...)', where $(...) is some make expression that interpolates suitably escaped material. Thus, 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

Sample run:

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

If we want to communicate an environment variable to a command, we can do that using the shell syntax VAR0=val0 VAR1=val1 ... VARn=valn command arg .... This can be illustrated by some minor alterations to the above 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

Run:

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

file.txt contains a dump of environment variables, where we can see somevar. If export in GNU Make did the right thing, we would have been able to just do:

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

but the end result is the same.

Since the end result you want is to echo $(update), you would shell_escape anyway, even if GNU Make passed exported vars to $(shell ...). That is to say, look at one more 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)

Output:

apple with 'quoted' "stuff" and dollar $signs
apple with quoted stuff and dollar
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文