Does anyone know how to use a here-document redirection on a recipe?
test:
sh <<EOF
echo I Need This
echo To Work
ls
EOF
I can't find any solution trying the usual backslash method (which basically ends with a command in a single line).
Rationale:
I have a set of multi-line recipes that I want to proxy through another command (e.g., sh, docker).
onelinerecipe := echo l1
define twolinerecipe :=
echo l1
echo l2
endef
define threelinerecipe :=
echo l1
echo l2
echo l3
endef
# sh as proxy command and proof of concept
proxy := sh
test1:
$(proxy) <<EOF
$(onelinerecipe)
EOF
test2:
$(proxy) <<EOF
$(twolinerecipe)
EOF
test3:
$(proxy) <<EOF
$(threelinerecipe)
EOF
The solution I would love to avoid: transform multiline macros into single lines.
define threelinerecipe :=
echo l1;
echo l2;
echo l3
endef
test3:
$(proxy) <<< "$(strip $(threelinerecipe))"
This works (I use gmake 4.0 and bash as make's shell) but it requires changing my recipes and I have a lot. Strip removes the newlines, from the macro, then everything is written in a single line.
My end goal is: proxy := docker run ...
Using the line .ONESHELL:
somewhere in your Makefile will send all recipe lines to a single shell invocation, you should find your original Makefile works as expected.
When make sees a multi-line block in a recipe
(i.e., a block of lines all ending in \
, apart from the last),
it passes that block un-modifed to the shell.
This generally works in bash,
apart from here docs.
One way around this is to strip any trailing \
s,
then pass the resulting string to bash's eval
.
You do this in make by playing with ${.SHELLFLAGS}
and ${SHELL}
.
You can use both of these in target-specific form if you only want it to kick in for a few targets.
.PHONY: heredoc
heredoc: .SHELLFLAGS = -c eval
heredoc: SHELL = bash -c 'eval "$${@//\\\\/}"'
heredoc:
@echo First
@cat <<-there \
here line1 \
here anotherline \
there
@echo Last
giving
$ make
First
here line1
here anotherline
Last
Careful with that quoting, Eugene. Note the cheat here: I am removing all backslashes, not just the ones at the ends of the line. YMMV.
With GNU make, you can combine multi-line variables with the export
directive to use a multi-line command without having to turn on .ONESHELL
globally:
define script
cat <<'EOF'
here document in multi-line shell snippet
called from the "$@" target
EOF
endef
export script
run:; @ eval "$$script"
will give
here document in multi-line shell snippet
called from the "run" target
You can also combine it with the value
function to prevent its value from being expanded by make:
define _script
cat <<EOF
SHELL var expanded by the shell to $SHELL, pid is $$
EOF
endef
export script = $(value _script)
run:; @ eval "$$script"
will give
SHELL var expanded by the shell to /bin/sh, pid is 12712
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With