blitzen9872
blitzen9872

Reputation: 320

How can you export a variable to Make's 'shell' function?

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

Answers (2)

Satyam Kapoor
Satyam Kapoor

Reputation: 1

Solved in GNU Make 4.4.1. Exports persist across the terminal of shell function.

Upvotes: 0

Mike Kinghan
Mike Kinghan

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

Related Questions