Does make not support backtick notation?

Viewed 1544

Scenario

I was running into some trouble trying to assign values from a command into a variable in a makefile. Using backtick notation seems to work fine, but as soon as I use the more modern $(pwd) notation, the value of the variable returns nothing.

$ cat Makefile
FOO=moo
BAR=$(pwd)
BAZ=`pwd`

all: foo bar baz

foo: 
    @echo foo:$(FOO)';'
bar:
    @echo bar:$(BAR)';'
baz:
    @echo baz:$(BAZ)';'
$ make all
foo:moo;
bar:;
baz:/home/mazunki;

As you can see, FOO and BAZ work as expected. But not BAR.

Problem

Let's look at my problem now, by adding some more examples. I'm skipping the echoing, as you get the idea.

$ cat Makefile
SHELL=/bin/env bash

FOO=dog
BAR=$(pwd)
BAZ=`pwd`
QUZ=`$(FOO)`/cat
ZOT=`\`FOO\``/owl
BIM=`$(BAZ)`/rat
BAM=`\`BAZ\``/pig
BUM=`BUM`/fox

all: foo bar baz quz zot bim bam bum
# targets for all the things

$ make
foo:dog;
bar:;
baz:/home/mazunki;
bash: dog: command not found
quz:/cat;
bash: FOO: command not found
zot:/owl;
bim:pwd/rat;
bash: BAZ: command not found
bam:/pig;
bash: BAZ: command not found
bum:/fox;

I really had hopes for BIM. But sadly, it only return the command which I wanted to run instead. I tried adding another layer of $() and/or `` to it, but the result is an empty output instead.

My desired output is what I'd get from running echo -n $(pwd)/rat in Bash. None of my tested examples allow me to do this. I want to be able to do this, as some of the commands I use for my preface variables are based on initial settings, as such:

# User settings
ROOT=`pwd`
SRCPATH=$(ROOT)/src/
OBJPATH=$(ROOT)/objects/

# Scripting
SRCFILES=`find $(SRCPATH) -name '*.ext' -printf '%p '`
OBJFILES=`find $(SRCPATH) -name '*.ext' -printf '$(OBJPATH)/%p ' | sed 's/^$(SRCPATH)/^$(OBJPATH)//g; s/.ext;/.obj;/g; s/;$//g'`

It might look kind of a convoluted scenario, but I'm doing this for two main reasons: I want it to be highly configurable, without having to edit the commands themselves, and also because I want to learn how to make Makefiles properly.

3 Answers

No, Makefiles do not support backticks in this way. Makefiles are not shell scripts and do not share syntax with bash at all.

Your example with BAZ only appears to work because the backticks end up being copied literally to the shell, so the Make variable BAZ contains `pwd` (not /home/mazunki), and when injected into a shell command, causes the shell to run echo `pwd`. At no point did Make interpret the backticks in any way.

To invoke a shell command from make, you can use $(shell ...):

var := $(shell pwd)
foo:
        @echo $(var)

Note that the command is also a Make expression, not a shell command directly, so any $s you want to pass to the underlying shell have to be escaped.

Make doesn't support backticks. Makefiles are not shell scripts. However, makefiles do run shell scripts, and shell scripts support backticks. You wrote this:

$ cat Makefile
FOO=moo
BAR=$(pwd)
BAZ=`pwd`

all: foo bar baz

foo: 
        @echo foo:$(FOO)';'
bar:
        @echo bar:$(BAR)';'
baz:
        @echo baz:$(BAZ)';'
$ make all
foo:moo;
bar:;
baz:/home/mazunki;

The variable assignments simply create variables containing those literal strings, so FOO contains the string moo, BAR contains the string $(pwd), and BAZ contains the string `pwd`.

Make only substitutes variables that start with $ (maybe you can already see why your makefile doesn't work as you wanted). Nothing else is special to make. So when make runs the recipe for foo, it will replace the make variable $(FOO) with its value moo and runs the shell command echo foo:moo';'. When make runs the recipe for baz it replaces $(BAZ) with `pwd` and runs the shell command:

echo baz:`pwd`';'

and the shell (not make) will expand the backticks for you.

When make runs the recipe for bar, though, it will replace $(BAR) with the value $(pwd) which is another make variable, which will be expanded, and this make variable pwd is not set, so make expands this to the empty string and runs the shell command echo bar:';'.

If you want to use the new-style $(...) syntax for shell scripts you must escape the $ so make doesn't treat it as a make variable. You have to write:

BAR=$$(pwd)

then it will work as you expect.

You should really read https://www.gnu.org/software/make/manual/html_node/Reference.html and the associated pages.

To expand on answer https://stackoverflow.com/a/60628899 by https://stackoverflow.com/users/1899640, adding a working example:

SHELL=/bin/env bash

FOO=$(shell pwd)
BAR=$(FOO)/src
BUZ=ext

all: foo bar baz

foo: 
    @echo foo:$(FOO)';'  # returns /home/mazunki
bar:
    @echo bar:$(BAR)';'  # returns /home/mazunki/src
baz:
    find $(BAR) -name '*.$(BUZ)'  # returns all files /home/mazunki/src/**/*.ext
Related