I think writing up a cheat sheet for GNU Make and environment variables before Christmas is a good idea, right?
In GNU Make, by default a single line runs in its own shell. ie.
# Makefile test1: export key=asdf # GNU make's variable appears as $(key) while bash environment variable appears as $${key}, as the $ needs to be escaped echo $${key} # run it make test1 # the value `asdf` doesn't get passed to the next line because the next line runs in a separate shell
We can use backslash to fake multi-line like this:
# Makefile test1: export key=asdf; \ echo $${key} # run it make test1 asdf
With .ONESHELL directive multiple lines in one target run in a same shell, which I prefer a lot:
# Makefile .ONESHELL test1: export key=asdf echo $${key} # run it make test1 asdf
A shell environment variable can be accessed inside a Makefile as a make variable with same name, eg.:
# Makefile test1: echo $(key) # run it with the required environment variable key=asdf make test1 asdf
Here it’s getting more interesting, as shell environment variables will be copied into its sub-shell, in this case the make
command runs in a sub-shell of current session. However if an environment variable is set in one target, the result is not available in a sibling target or its parent target. eg.:
# Makefile test1: export key=asdf test2: test1 echo $${key} # run it, can you guess the result? make test2 # yep, blank again. the `test1` target in `test2` runs in a sub-shell and doesn't share environment variables with `test2`
To let test
1 “export” environment variables to test2
target, I learned that eval
command is quite handy in this situation:
# Makefile .ONESHELL test1: echo export key=asdf test2: eval $$(make test1) echo $${key} # run it make test2 asdf
The reason this works is that the eval
command will construct a new shell command using the arguments it gets, in this case, it’s export key=asdf
from the output of test1
target. Then by running it, the value ‘asdf’ has been assigned to the variable ‘key’.
Hope this helps 🙂