31

Is it possible to have variables in GNU Make recipes?

Something like this doesn't work:

%_t.mkd : %.mkd
    REV=$$(svn info $<|grep 'Last Changed Rev'|cut -f4 -d\ )
    echo $${REV}

Is there some way to make that work at all?

As you can see what I want is to extract the revision that a file was changed and then use that later in the full recipe. Unfortunately I can't use svn:keywords as I need the revision number outside of the document in question.

5 Answers 5

18

This doesn't work because the make tool starts a new shell process for each recipe line. And shell variables – even 'exported' environment variables – cannot possibly propagate "upwards"; they're gone as soon as the shell process exits.

  • The traditional method is to join the recipe lines using \ in the Makefile:

    foo: bar baz
        line1; \
        line2; \
        line3
    

    (Note that the commands must be separated using ; or &&, because the backslashes are also passed to the shell which does the same line-joining.)

    See also info make "Splitting Lines" and info make "Splitting Recipe Lines" in the GNU Make manual.

  • The other method is to tell make to always use one shell process for the entire recipe, using the .ONESHELL directive:

    .ONESHELL:
    
    foo: bar baz
        line1
        line2
        line3
    

    See info make "One Shell".

    (Note that while .ONESHELL is recommended by POSIX, not all make versions support it; e.g. BSD make only has a command-line flag for it. This shouldn't be a problem though.)

25

Thanks to https://stackoverflow.com/questions/6519234/cant-assign-variable-inside-recipe

This is the solution to change a variable in a recipe:

recipe: 
        $(eval variablename=whatever)
5

taking what @user3645902 mentioned, here is the solution to the main question:

recipe:
    @$(eval REV=`svn info $<|grep 'Last Changed Rev'|cut -f4 -d`)
    @echo $(REV)
1
  • 1
    I believe that this creates a race condition. Two recipes executing in parallel could stomp on each others' variables. Commented Jan 24, 2021 at 20:09
1

According to Gnu Make 6.5 Setting Variables:

The shell assignment operator != can be used to execute a program and set a variable to its output. This operator first evaluates the right-hand side, then passes that result to the shell for execution. If the result of the execution ends in a newline, that one newline is removed; all other newlines are replaced by spaces. The resulting string is then placed into the named recursively-expanded variable.

So you could try the following (not tested):

REV != $$(svn info $<|grep 'Last Changed Rev'|cut -f4 -d\ ) \
echo $${REV}
2
  • 3
    This would work at the top level of a makefile, but not in the middle of a recipe (where shell syntax is used, not Make syntax). It'd be REV != svn info ... too. Commented Jul 31, 2014 at 8:01
  • 1
    Nope, as you see I need it as part of the recipe since the value will be different for different files.
    – Magnus
    Commented Aug 1, 2014 at 10:56
0

this way of setting undef Makefile function parameter in this case $(1) argument works:

# usage:
# install-api-node:
#   $(call install-img,linux_user)

define install-img

    APPUSR=$(eval APPUSR=`echo appusr`)
    APPUSR=$(or $(1),$(APPUSR))
   
    @echo example usage : docker run -it -d --restart=always \
        -v $$(pwd):/opt/project-source-dir \
        -v $$HOME/.aws:/home/$(APPUSR)/.aws \
        --name my-container my-image ;


endef

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .