Multi-line bash commands in makefile
You can use backslash for line continuation. However note that the shell receives the whole command concatenated into a single line, so you also need to terminate some of the lines with a semicolon:
foo: for i in `find`; \ do \ all="$$all $$i"; \ done; \ gcc $$all
But if you just want to take the whole list returned by the find
invocation and pass it to gcc
, you actually don't necessarily need a multiline command:
foo: gcc `find`
Or, using a more shell-conventional $(command)
approach (notice the $
escaping though):
foo: gcc $$(find)
As indicated in the question, every sub-command is run in its own shell. This makes writing non-trivial shell scripts a little bit messy -- but it is possible! The solution is to consolidate your script into what make will consider a single sub-command (a single line).
Tips for writing shell scripts within makefiles:
- Escape the script's use of
$
by replacing with$$
- Convert the script to work as a single line by inserting
;
between commands - If you want to write the script on multiple lines, escape end-of-line with
\
- Optionally start with
set -e
to match make's provision to abort on sub-command failure - This is totally optional, but you could bracket the script with
()
or{}
to emphasize the cohesiveness of a multiple line sequence -- that this is not a typical makefile command sequence
Here's an example inspired by the OP:
mytarget: { \ set -e ;\ msg="header:" ;\ for i in $$(seq 1 3) ; do msg="$$msg pre_$${i}_post" ; done ;\ msg="$$msg :footer" ;\ echo msg=$$msg ;\ }
The ONESHELL directive allows to write multiple line recipes to be executed in the same shell invocation.
all: fooSOURCE_FILES = $(shell find . -name '*.c').ONESHELL:foo: ${SOURCE_FILES} FILES=() for F in $^; do FILES+=($${F}) done gcc "$${FILES[@]}" -o $@
There is a drawback though : special prefix characters (‘@’, ‘-’, and ‘+’) are interpreted differently.
https://www.gnu.org/software/make/manual/html_node/One-Shell.html