如何在Makefile中设置子进程的环境变量

215

我希望修改这个 Makefile 文件:

SHELL := /bin/bash
PATH  := node_modules/.bin:$(PATH)

boot:
    @supervisor         \
      --harmony         \
      --watch etc,lib       \
      --extensions js,json      \
      --no-restart-on error     \
        lib

test:
    NODE_ENV=test mocha         \
      --harmony             \
      --reporter spec       \
        test

clean:
    @rm -rf node_modules

.PHONY: test clean

致:

SHELL := /bin/bash
PATH  := node_modules/.bin:$(PATH)

boot:
    @supervisor         \
      --harmony         \
      --watch etc,lib       \
      --extensions js,json      \
      --no-restart-on error     \
        lib

test: NODE_ENV=test
test:
    mocha                   \
      --harmony             \
      --reporter spec       \
        test

clean:
    @rm -rf node_modules

.PHONY: test clean

很遗憾第二个方法无效(节点进程仍然使用默认的 NODE_ENV)。

我错过了什么?


你的“不幸”的评论源于对环境变量与Makefile变量之间的误解。证明环境变量已设置的最佳方法是在另一个make将调用的程序中查询此环境变量。仅执行echo $(BLAH)只是在Makefile内部评估Makefile的键/值机制。在Python中,您可以使用print(os.getenv("MURDOC"))来真正查询环境变量。 - daparic
4个回答

223

默认情况下,Make 变量不会被导出到 make 调用的进程环境中...。但是,您可以使用 Make 的 export 命令来强制导出这些变量。修改为:

test: NODE_ENV = test

到这个:

test: export NODE_ENV = test
< p >(假设您有足够现代的GNU make版本> = 3.77)。


5
我有GNU make 3.81,all: <\n\t>export PROJ_ROOT=$(CURDIR)<\n\t>echo $(PROJ_ROOT)<\n> 输出了第一行正确的扩展,但只输出了 echo 第二行。运行make后 PROJ_ROOT 没有被设置。在=周围添加空格会导致"bad variable name"错误。像你的例子中将第一行作为前提条件会产生"commands commence before first target"错误。 - Gauthier
11
@Gauthier 当然可以。但这不是我所写的内容。你在 all: 后面加了一个 <\n\t>,而我的示例中没有这个东西。我的示例意图按照现有的方式使用:它定义了一个目标特定变量,而非向配方添加命令。此外,你不能同时在一个目标上使用一条配方和一个目标特定变量:你必须将目标写两次。请看问题中的第二个示例,如果这无法解释清楚,请提出新问题:评论区没有足够的空间或格式。 - MadScientist
2
多个变量呢? - holms
1
没问题,但是你把它添加到目标的头部了。仅仅在目标上下文中列出它们不起作用吗?因为对我来说这样做不起作用。 - holms
2
GNU make 3.77 版本中新增了目标特定变量。从 GNU make 3.81 开始,它们可以被导出。请参阅 http://git.savannah.gnu.org/cgit/make.git/tree/NEWS。 - MadScientist
显示剩余3条评论

203

正如MadScientist指出的那样,你可以使用以下方法导出单个变量:

export MY_VAR = foo  # Available for all targets

或者为特定目标(目标特定变量)导出变量:

my-target: export MY_VAR_1 = foo
my-target: export MY_VAR_2 = bar
my-target: export MY_VAR_3 = baz

my-target: dependency_1 dependency_2
  echo do something

你还可以指定.EXPORT_ALL_VARIABLES目标来——你猜对了!——导出所有的东西!!!:


.EXPORT_ALL_VARIABLES:

MY_VAR_1 = foo
MY_VAR_2 = bar
MY_VAR_3 = baz

test:
  @echo $$MY_VAR_1 $$MY_VAR_2 $$MY_VAR_3

请查看.EXPORT_ALL_VARIABLES


2
挺奇怪的,我之前测试过它,发现它能正常工作(现在不确定为什么会出问题了...)我可以回去删掉那个评论吧... - AnthonyC
3
它之所以能够起作用,是因为有两个MY_VAR:一个是在Makefile中访问的变量${MY_VAR},另一个是在bash中导出的变量,以$$MY_VAR访问。 - Sergei
有用。但是找不到仅导出一组变量的方法。 - Eric Chen
2
请注意,如果变量已经存在于您的 makefile 中,并且您只想将其导出到另一个命令中,则可以使用语法 export var ?= 而不需要分配新值。 - smac89
3
".EXPORT_ALL_VARIABLES 是正确的答案" - Ken
显示剩余3条评论

23

我仅需要在本地设置环境变量来调用我的测试命令。以下是在bash shell中设置多个环境变量并转义make中的美元符号的示例:

我只需要在本地设置环境变量来调用我的测试命令,以下是在bash shell中设置多个环境变量并转义make中的美元符号的示例。

SHELL := /bin/bash

.PHONY: test tests
test tests:
    PATH=./node_modules/.bin/:$$PATH \
    JSCOVERAGE=1 \
    nodeunit tests/

8
请修改您的回答以包含一些解释。仅提供代码的答案很少对未来的读者有所帮助。您的答案因为低质量而进入了审核队列。 - mickmackusa
ThorSummoner,这个解决方案不如上面的方法灵活。例如,一个人可能希望有一个单一的规则来调用命令,然后有几个其他规则来通过设置环境变量来修改该行为。考虑以下情况:test: cmdperf: export PERF="yes" perf: test如果'cmd'很复杂(通常是这样),那么这种方法更容易维护。您在cmd规则中设置环境变量的方法使得这更加困难。 - Keith Hanlan
这个解决方案可能确实不够灵活,但从某种定义上来说,它可以被认为是“更安全”的,因为没有在配方和 shell 调用之间 indiscriminately 污染环境。在许多情况下,使用 shell 自己的 VAR=... command ... 语法正是你想要的,也足够了。许多环境变量并不是为了被所有东西所消耗而存在的——有些进程期望“自己的”变量,只有这些进程才会使用。变量的设置至少与目标特定变量一样明确,所以也有这个好处。 - Armen Michaeli

4
我会重新编写原始目标测试,确保所需变量在与要启动的应用程序相同的子进程中定义:
test:
    ( NODE_ENV=test mocha --harmony --reporter spec test )

1
这里的括号(())用于创建子shell,没有实际意义--通常Make会使用自己相当简洁(故意如此)的shell进程调用每个逻辑行(因此您的子shell不会与其他需要隔离的内容共享),而像NODE_ENV=...这样的变量设置将以相同的方式实现所需的效果--无论是否使用子shell--这意味着唯一看到值为testNODE_ENV变量的程序是作为上述Make将调用的mocha程序(再次强调,无论是否在子shell中运行该程序)。 - Armen Michaeli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接