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.


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; \

    (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:

    foo: bar baz

    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.)


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

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

        $(eval variablename=whatever)

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

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

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
    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.
    – user1686
    Jul 31 '14 at 8:01
  • 2
    Perhaps the op can set the variable once at the top level of his makefile then? :/
    – DavidPostill
    Jul 31 '14 at 8:04
  • 1
    Nope, as you see I need it as part of the recipe since the value will be different for different files.
    – Magnus
    Aug 1 '14 at 10:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.