Defining local variable in Makefile target
Asked Answered
G

3

7

How to define local variable in Makefile target?

I would like to avoid repeating filename like:

zsh:
    FILENAME := "text.txt"
    @echo "Copying ${FILENAME}...";
    scp "${FILENAME}" "user@host:/home/user/${FILENAME}"

But I am getting an error:

FILENAME := "text.txt"
/bin/sh: FILENAME: command not found

Same with $(FILENAME)

Trying

zsh:
    export FILENAME="text.txt"
    @echo "Copying ${FILENAME} to $(EC2)";

Gives me an empty value:

Copying ...
Gaikwar answered 14/4, 2021 at 17:27 Comment(2)
Does this answer your question? This bash command is not running correctly inside MakefileKibe
@Kibe well, no. The question is How to declare a local variable in Makefile. Defining it through the shell is just a workaround.Gaikwar
B
13

You can't define a make variable inside a recipe. Recipes are run in the shell and must use shell syntax.

If you want to define a make variable, define it outside of a recipe, like this:

FILENAME := text.txt
zsh:
        @echo "Copying ${FILENAME}...";
        scp "${FILENAME}" "user@host:/home/user/${FILENAME}"

Note, it's virtually never correct to add quotes around a value when assigning it to a make variable. Make doesn't care about quotes (in variable values or expansion) and doesn't treat them specially in any way.

Bhopal answered 14/4, 2021 at 19:34 Comment(1)
See also: "Target-specific Variable Values," in the GNU Make manual. Maybe a bit too fancy, likely to cause problems on non-Linux systems, and perhaps better avoided.Inanity
P
3

The rules for a target are executed by the shell, so you can set a variable using shell syntax:

zsh:
    @FILENAME="text.txt"; \
    echo "Copying $${FILENAME}..."; \
    scp "$${FILENAME}" "user@host:/home/user/$${FILENAME}"

Notice that:

  • I'm escaping end-of-line using \ so that everything executes in the same shell
  • I'm escaping the $ in shell variables by writing $$ (otherwise make will attempt to interpret them as make variables).

For this rule, which apparently depends on a file named text.txt, you could alternatively declare text.txt as an explicit dependency and then write:

zsh: text.txt
    @echo "Copying $<..."; \
    scp "$<" "user@host:/home/user/$<"
Pylle answered 14/4, 2021 at 17:34 Comment(6)
Do I need `\` in the second case?Gaikwar
Escaping the lines in such a way would not allow me to suppress echo "Copying $${FILENAME}..."; string from makefile output, right?Gaikwar
The @ at the beginning of the rule suppresses make from echoing the rule before executing it. That's why both examples here start with @, because that appears to be what you wanted from your question.Pylle
And it would also suppress the scp "$<" "user@host:/home/user/$<" which I would like to keep for "user". Moreover, declaring a file as a dependency breaks thins because this file is located in subdirectly and I am getting the file copied to /home/user/subdir/text.txtGaikwar
Re: questions about suppression, just try it and see what happens. If you can't declare the explicit dependency, just use the first example.Pylle
See also: .ONESHELLInanity
M
0

You can use eval to set variables in a recipe. From this answer here, I understand that you can even create pseudo local variables that way, by automatically prefixing them with the target. This is achieved by prefixing the variable with $@_.

mytarget:
    $(eval $@_foo = bar)
    @echo mytarget: $($@_foo)

other: mytarget
    @echo other: $($@_foo)

Now we can see that this works as intended. mytarget has the variable, while other cant see it.

$ make other
mytarget: bar
other:
Milli answered 31/3 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.