How to set child process' environment variable in Makefile

251,480

Solution 1

Make variables are not exported into the environment of processes make invokes... by default. However you can use make's export to force them to do so. Change:

test: NODE_ENV = test

to this:

test: export NODE_ENV = test

(assuming you have a sufficiently modern version of GNU make >= 3.77 ).

Solution 2

As MadScientist pointed out, you can export individual variables with:

export MY_VAR = foo  # Available for all targets

Or export variables for a specific target (target-specific variables):

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

You can also specify the .EXPORT_ALL_VARIABLES target to—you guessed it!—EXPORT ALL THE THINGS!!!:

.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

see .EXPORT_ALL_VARIABLES

Solution 3

I only needed the environment variables locally to invoke my test command, here's an example setting multiple environment vars in a bash shell, and escaping the dollar sign in make.

SHELL := /bin/bash

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

Solution 4

I would re-write the original target test, taking care the needed variable is defined IN THE SAME SUB-PROCESS as the application to launch:

test:
    ( NODE_ENV=test mocha --harmony --reporter spec test )
Share:
251,480

Related videos on Youtube

bodokaiser
Author by

bodokaiser

Hey, I am Bodo Kaiser and I have a bit knowledge around nodejs.

Updated on July 11, 2022

Comments

  • bodokaiser
    bodokaiser almost 2 years

    I would like to change this 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
    

    to:

    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
    

    Unfortunately the second one does not work (the node process still runs with the default NODE_ENV.

    What did I miss?

    • truthadjustr
      truthadjustr over 3 years
      Your Unfortunately comment stems from a misunderstanding between an environment variable versus a Makefile variable. The best way to prove that an environment variable has been set, is to query this environment variable inside another program that make wil call. Only doing echo $(BLAH) is merely evaluating Makefile's key/value mechanism inside the Makefile. In python, you can print(os.getenv("MURDOC")) to truly query the environment variable.
  • Gauthier
    Gauthier over 9 years
    I have GNU make 3.81, and all: <\n\t>export PROJ_ROOT=$(CURDIR)<\n\t>echo $(PROJ_ROOT)<\n> outputs the correct expansion for the first row, but only echo for the second one. PROJ_ROOT is not set after running make. Spaces around = give "bad variable name" for export. Having the first row as prerequisite as in your example gives "commands commence before first target"
  • MadScientist
    MadScientist over 9 years
    @Gauthier yes of course. That's not what I wrote. You added a <\n\t> after the all:, which is not in my example. My example is intended to be used as written: it's defining a target-specific variable, NOT adding a command to the recipe. Also you cannot use a recipe and a target-specific variable on a target at the same time: you have to write the target twice. See the second example in the question, and ask a new question if this doesn't help explain it: there's not enough space or formatting in comments.
  • mickmackusa
    mickmackusa about 7 years
    Please edit your answer to include some explanation. Code-only answers do very little to educate future SO readers. Your answer is in the moderation queue for being low-quality.
  • Keith Hanlan
    Keith Hanlan almost 7 years
    ThorSummoner, this solution is not as flexible as the approach above. For example, one might wish to have one single rule for invoking a command and then several other rules which modify that behaviour by setting environment variables. Consider: test: cmd perf: export PERF="yes" perf: test If 'cmd' is complicated (and it usually is), then this approach is a lot easier to maintain. Your approach to setting the environment variable in the cmd rule makes this more difficult.
  • AnthonyC
    AnthonyC over 5 years
    Oddly enough I did test it earlier that showed it worked.. (not sure why now..) I can go back and delete the comment I guess..
  • holms
    holms over 5 years
    What about multiple variables?
  • MadScientist
    MadScientist over 5 years
    Each variable has to be set on its own separate line in the makefile.
  • holms
    holms over 5 years
    That's fine but your added it to the head of target. Just listing them in context of target won't work? Because it doesn't work for me
  • MadScientist
    MadScientist over 5 years
    Sorry but I don't understand what you mean. Probably you should file a new question rather than asking in the comments here.
  • jjmerelo
    jjmerelo over 5 years
    How modern is modern?
  • MadScientist
    MadScientist over 5 years
    Target-specific variables were added in GNU make 3.77. They are exportable as of GNU make 3.81. See git.savannah.gnu.org/cgit/make.git/tree/NEWS
  • Sergei
    Sergei about 5 years
    @AnthonyC It works because there are two MY_VARs: one is makefile variable accessed as ${MY_VAR} and another one is bash exported variable, accessed as $$MY_VAR
  • Eric Chen
    Eric Chen almost 5 years
    Useful. But cannot find a way to export only a set of variables.
  • smac89
    smac89 about 3 years
    Note if the variable already exists in your makefile and you just want to export it to another command, you can use the syntax export var ?= without assigning a new value
  • Ken
    Ken almost 2 years
    .EXPORT_ALL_VARIABLES is right answer