Makefile recipe with a here-document redirection

拥有回忆 提交于 2020-01-03 10:42:12

问题


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 ...


回答1:


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.




回答2:


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.




回答3:


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:

export define script
cat <<'EOF'
here document in multi-line shell snippet
called from the "$@" target
EOF
endef

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



回答4:


Not a here doc but this might be a useful workaround. And it doesn’t require any GNU Make’isms. Put the lines in a subshell with parens, prepend each line with echo. You’ll need trailing sloshes and semi-colon and slosh where appropriate.

test:
( \
    echo echo I Need This ;\
    echo echo To Work ;\
    echo ls \
) \
| sh


来源:https://stackoverflow.com/questions/35516379/makefile-recipe-with-a-here-document-redirection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!