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 shellWe can use backslash to fake multi-line like this:
# Makefile
test1:
export key=asdf; \
echo $${key}
# run it
make test1
asdfWith .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
asdfA 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 test1 “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
asdfThe 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 🙂
