Reputation: 320
Consider the following:
$ cat a.sh
#!/bin/sh
echo in a.sh, BANANA=$BANANA
$ cat Makefile
.PHONY: foo
export BANANA = I am a banana
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
$ make
# BANANA is set in a.sh
echo `./a.sh`
in a.sh, BANANA=I am a banana
# BANANA is *not* set in a.sh
echo in a.sh, BANANA=
in a.sh, BANANA=
As demonstrated, the export
directive to Make tells make to set the variable BANANA
in the environment of its children. But that setting does not apply to the shell
function. It does seem to apply to the backticks. Is this a bug? How can I easily set make variables to the environment of the shell
function?
Note:
$ make --version
GNU Make 4.0
Built for x86_64-unknown-linux-gnu
Copyright (C) 1988-2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Upvotes: 4
Views: 812
Reputation: 1
Solved in GNU Make 4.4.1. Exports persist across the terminal of shell function.
Upvotes: 0
Reputation: 61575
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)
Upvotes: 3