First, be clear about the meaning of your recipe:
foo:
$(eval F=`./a.sh`) # BANANA is set in a.sh
echo $F
$(eval G=$(shell ./a.sh)) # BANANA is *not* set in a.sh
echo $G
This recipe contains two (not four) commands:
foo:
echo $F
echo $G
and the two make-functions:
$(eval F=`./a.sh`)
$(eval G=$(shell ./a.sh))
will be evaluated, in that order, for the scope of the two-line recipe when make
decides to
run it. If you are surpised by this point, read this question
and answer.
Be clear also that F
and G
are both make variables, not shell variables. You
only get away with referring to $F
and $G
rather than $(F)
and $(G)
thanks
to the last para of 6.1 Basics of Variable References
A dollar sign followed by a character other than a dollar sign, open-parenthesis or open-brace
treats that single character as the variable name. Thus, you could reference the variable x with ‘$x’.
However, this practice is strongly discouraged, except in the case of the automatic variables
It wouldn't work for, say, FF
and GG
.
So the normal way to write your makefile would be:
.PHONY: foo
export BANANA = I am a banana
foo: F=`./a.sh`
foo: G=$(shell ./a.sh)
foo:
echo $(F)
echo $(G)
which has exactly the same effect.
And this perhaps clarifies the difference between the output of echo $(F)
and echo $(G)
.
$(shell ./a.sh)
invokes a make function that executes ./a.sh
in a shell
directly spawned by make
and returns the stdout
of so doing. Thus for target foo
, make-variable G
will
be defined as the stdout
of executing ./a.sh
in a child shell of make
.
`./a.sh`
does not invoke any make-function. As far as make is concerned, it is just
a string. For the target foo
, make-variable F
will be defined as `./a.sh `
The exported make-variable BANANA
is not injected into the environment of a shell spawned by
$(shell ...)
. 5.7.2 Communicating Variables to a Sub-make
To pass down, or export, a variable, make adds the variable and its value to the
environment for running each line of the recipe
An exported variable and its definition is only injected into the environments of the shells that
run the lines of recipes.
Thus BANANA
is not defined in the environment of a.sh
when it is run by $(shell ./a.sh)
to generate the definition of G
. But it is defined in the environment of the shell
that that runs the recipe line echo $(F)
, with $(F)
= `a.sh`
. That shell (not make
) interprets
`a.sh`
as a back-tick invocation of a subshell, which inherits the definition of
BANANA
.
To get BANANA
exported into the environment of $(shell ...)
, you have to do
it yourself since it is not done by make
:
G=$(shell export BANANA='$(BANANA)'; ./a.sh)