makefilegnu-make

How to include/eval a command output in GNU Make?


Consider that a piece of GNU Makefile is generated by a command (probably because the list of variables to define is too complicated to achieve otherwise).

Script test.sh:

echo 'V1 = foo bar'
echo 'V2 = ab cd'

If I try this in the GNU Makefile:

$(eval $(shell ./test.sh))
$(info V1='$(V1)')
$(info V2='$(V2)')

I get this:

V1='foo bar V2 = ab cd'
V2=''

while I would like this:

V1='foo bar'
V2='ab cd'

I understand that $(shell) transforms new lines in spaces. I found similar, although not identical, questions online but answers were incorrect. For instance, using an explicit NL variable in the Makefile:

define NL


endef
$(eval $(shell ./test.sh))
$(info V1='$(V1)')
$(info V2='$(V2)')

and then creating a reference to $(NL) in the script:

echo 'V1 = foo bar$(NL)'
echo 'V2 = ab cd$(NL)'

I get this:

V1='foo bar
 V2 = ab cd
'
V2=''

The reference to $(NL) is replaced by one newline but that newline is swallowed into V1 instead of separating the definitions of variables.

I haven't found a way to use include directive from a command output (apart from creating a temporary file, which is quite ugly in terms of cleanup).


Solution

  • This is an addendum to Thierry's self-answer to show how the ugliness can be isolated in a single function, which I'll call shell-lines, with no need to modify the shell script. We can pipe the script's output through sed to replace newline with a marker of our choice and subst it back to newline:

    define NL
    
    
    endef
    
    EOL = --EOL--
    shell-lines = $(subst $(EOL),$(NL),$(shell ( $1 ) | sed 's/$$/$(EOL)/'))
    

    Then we simply call this function - here, I'll inline a couple of commands rather than execute an external script:

    $(eval $(call shell-lines, echo 'V1 = foo bar'; echo 'V2 = ab cd'))
    

    Some notes:


    Full makefile (for testing):

    define NL
    
    
    endef
    
    EOL = --EOL--
    shell-lines = $(subst $(EOL),$(NL),$(shell ( $1 ) | sed 's/$$/$(EOL)/'))
    
    $(eval $(call shell-lines, printf '%s = %s\n' V1 'foo bar' V2 'ab cd'))
    
    $(info V1='$(V1)')
    $(info V2='$(V2)')
    
    all:
        @: