The Question
Example 1
Consider the following user-defined function:
define func =
if [ -f $(1) ]; then \
printf "'%s' is a file\n" '$(1)'; \
printf "This is a relatively long command that"; \
printf " won't fit on one line\n"; \
fi
endef
all:
$(call func,foo)
This will output the following:
$ make
if [ -f foo ]; then printf "'%s' is a file\n" 'foo'; printf "This is a rel
atively long command that"; printf " won't fit on one line\n"; fi
For readability, I would like make to print the command on multiple lines,
as written in the Makefile. How do I accomplish this?
What I've Tried
Example 2
The following works the way I want, but does not allow the parameterized function:
filename := foo
.PHONY: foo
foo:
if [ -f $(filename) ]; then \
printf "'%s' is a file\n" '$(filename)'; \
printf "This is a relatively long command that"; \
printf " won't fit on one line\n"; \
fi
Output:
$ make foo
if [ -f foo ]; then \
printf "'%s' is a file\n" 'foo'; \
printf "This is a relatively long command that"; \
printf " won't fit on one line\n"; \
fi
Example 3
My obvious first instinct was to escape the backslashes:
define func =
if [ -f $(1) ]; then \\
printf "'%s' is a file\n" '$(1)'; \\
printf "This is a relatively long command that"; \\
printf " won't fit on one line\n"; \\
fi
endef
Output:
$ make
if [ -f foo ]; then \\
printf "'%s' is a file\n" 'foo'; \\
printf "This is a relatively long command that"; \\
printf " won't fit on one line\n"; \\
fi
/bin/sh: \: command not found
'foo' is a file
/bin/sh: line 1: \: command not found
This is a relatively long command that/bin/sh: line 2: \: command not found
won't fit on one line
/bin/sh: line 3: \: command not found
make: *** [Makefile:11: all] Error 127
Example 4
Okay, so why not try \\\?
$ make
if [ -f foo ]; then \ printf "'%s' is a file\n" 'foo'; \ printf "This is a
relatively long command that"; \ printf " won't fit on one line\n"; \ fi
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [Makefile:11: all] Error 1
Example 5
Interesting. Let's go for four...
$ make
if [ -f foo ]; then \\\\
printf "'%s' is a file\n" 'foo'; \\\\
printf "This is a relatively long command that"; \\\\
printf " won't fit on one line\n"; \\\\
fi
/bin/sh: \\: command not found
'foo' is a file
/bin/sh: line 1: \\: command not found
This is a relatively long command that/bin/sh: line 2: \\: command not found
won't fit on one line
/bin/sh: line 3: \\: command not found
make: *** [Makefile:11: all] Error 127
Now we're back to where we were last time.
What Works
Example 6
This is the only thing that seems to work:
define func =
if [ -f $(1) ]; then #\\
printf "'%s' is a file\n" '$(1)'; #\\
printf "This is a relatively long command that"; #\\
printf " won't fit on one line\n"; #\\
fi
endef
Output:
$ make
if [ -f foo ]; then #\\
printf "'%s' is a file\n" 'foo'; #\\
printf "This is a relatively long command that"; #\\
printf " won't fit on one line\n"; #\\
fi
But man, that looks ugly, and it feels hackish. There's got to be a better way to do this. Or am I just going about this the wrong way in the first place?
It seems to me that make is just confused by the magic that happens when
escaping newlines within a recipe. The lines printed to the terminal during
execution do not match what the shell sees. Should this be considered a bug?
I am using GNU Make 4.2.1 on Cygwin.
Edit
To clarify: make normally gives special treatment to trailing backslashes within a recipe. They do not indicate line continuation, as they do elsewhere. Instead, they indicate that multiple recipe lines are to be treated as a single command, and they are passed to the shell intact.
When not in a recipe, but defining a variable, this special treatment does not apply. The lines are simply joined, as in Example 1. This is expected.
I would expect that a double backslash would be translated to a single literal backslash in the variable, but instead both backslashes are retained. When the variable is expanded in the recipe, I would expect make to behave as if the recipe had \\ at the end of every line. If this were the case, each line would be executed separately. But as you can see from Examples 3 and 6, the lines are executed together.
The point is, it is possible to get magic backslash parsing from the expansion of a variable. The problem is the mechanics of this behavior are inconsistent and confusing.