1

To demonstrate a major difference of how make treats single-line recipe than that of .ONESHELL-multiple-line recipe, I will use a sed command, where, we assume, that either of the following:

  1. sed -e 'p; p'
  2. sed -e 'p
    p'

are mostly equivalent. Right?

What we find, however, is that Make fails only on the single-line recipe.

Yeah! That's right. Make handles perfectly fine the multiple-line version (due to .ONESHELL, of course), but cannot do the same for a single-line recipe.

It defies common-sense, but here is the proof.



The Makefile:

.ONESHELL :

# Define a MULTIPLE-line recipe.
define define_cmd_foo
all : cmd_foo = echo xxx | sed -ne '
s/xxx/yyy/
p'
endef

# Define a SINGLE-line recipe.
define define_cmd_bar
all : cmd_bar = echo xxx | sed -ne 's/xxx/yyy/; p'
endef



# cmd_foo is multiple-line (3 line) recipe:
#                                         echo xxx | sed -ne '
#                                         s/xxx/yyy/
#                                         p'
$(define_cmd_foo)

# cmd_foo is a one and single-line recipe:
#                                        echo xxx | sed -ne 's/xxx/yyy/; p'
$(define_cmd_bar)


# This is a 1st file in a double-colon (linked list of) target 'all'.
# This will exeucte a multipe-line recipe.
all ::
    $(cmd_foo)

# This is a 2nd file in a double-colon (linked list of) target 'all'.
# This will exeucte a single-line recipe.
all ::
    $(cmd_bar)


Executing, we get:

echo xxx | sed -ne '
s/xxx/yyy/
p'
yyy
echo xxx | sed -ne 's/xxx/yyy/
/bin/sh: 1: Syntax error: Unterminated quoted string
makefile:34: recipe for target 'all' failed
make: *** [all] Error 2


Can you see here 2 commands?

The first command executes a multiple-line recipe, and succeeds.

In fact, the output for the first recipe: yyy, is exactly what we expected.

Any output, from the 5th line and the following lines - of the output - above, belongs to the execution of the other single-line recipe, which looks like something went wrong there. Very wrong!

Any reason, why a perfect good recipe goes well with .ONESHELL for a multiple-line version, but fails in a plain one-line version?

Community
  • 1
  • 1
Ji Cha
  • 929
  • 3
  • 12
  • 24
  • 1
    possible duplicate of [Is it possible to create a multi-line string variable in a Makefile](http://stackoverflow.com/questions/649246/is-it-possible-to-create-a-multi-line-string-variable-in-a-makefile) – keltar Aug 24 '15 at 04:29
  • 2
    @keltar: No; that's a different issue. If you want to choose a duplicate, then it is closely related to other recent questions asked by [Ji Cha](http://stackoverflow.com/users/5241638/ji-cha?tab=questions), though I think it is slightly different from any of them. The nearest ones were issues with `.ONESHELL` not working across multiple lines; this is it not working properly with a single line. There's probably a bug here that's been found, but it needs to be reported to the maintainers of GNU Make rather than continuing the exposition here – or as well as continuing the exposition here. – Jonathan Leffler Aug 24 '15 at 04:35
  • 1
    Thanks @JonathanLeffler, I didn't read it too carefully. It is indeed unrelated to oneshell, semicolon needs to be escaped with backslash (http://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_6.html#SEC67) – keltar Aug 24 '15 at 04:54
  • Can't test right now (*The .ONESHELL feature was added in GNU make 3.82 and I have 3.81*). But obviously the `;` character in the `sed` command is the issue since two commands separated by semicolon behave much like two separate shell commands. You can remove it (as it is not needed by sed for this command) or try to escape it. Edit: Didn't see the @keltar answer sorry... – jmlemetayer Aug 24 '15 at 12:02
  • The short answer is this: it's not well-supported to declare entire statements inside variables and expand them as first-level artifacts. Sometimes it works, sometimes it doesn't: there are many parser complications with doing this. This is one main reason why the `eval` function was created as a separate function; if you use `$(eval $(define_cmd_bar))` then it will work, and that's all that make guarantees. If you want to report this as a bug feel free. – MadScientist Aug 24 '15 at 14:22

0 Answers0