Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create a multi-line string variable in a Makefile

People also ask

How can you create multi line strings?

Use triple quotes to create a multiline string You will need to enclose it with a pair of Triple quotes, one at the start and second in the end. Anything inside the enclosing Triple quotes will become part of one multiline string.

Can a string be multiple lines?

Raw StringsThey can span multiple lines without concatenation and they don't use escaped sequences. You can use backslashes or double quotes directly.

What is $$ in Makefile?

$$ means be interpreted as a $ by the shell. the $(UNZIP_PATH) gets expanded by make before being interpreted by the shell.


Yes, you can use the define keyword to declare a multi-line variable, like this:

define ANNOUNCE_BODY
Version $(VERSION) of $(PACKAGE_NAME) has been released.

It can be downloaded from $(DOWNLOAD_URL).

etc, etc.
endef

The tricky part is getting your multi-line variable back out of the makefile. If you just do the obvious thing of using "echo $(ANNOUNCE_BODY)", you'll see the result that others have posted here -- the shell tries to handle the second and subsequent lines of the variable as commands themselves.

However, you can export the variable value as-is to the shell as an environment variable, and then reference it from the shell as an environment variable (NOT a make variable). For example:

export ANNOUNCE_BODY
all:
    @echo "$$ANNOUNCE_BODY"

Note the use of $$ANNOUNCE_BODY, indicating a shell environment variable reference, rather than $(ANNOUNCE_BODY), which would be a regular make variable reference. Also be sure to use quotes around your variable reference, to make sure that the newlines aren't interpreted by the shell itself.

Of course, this particular trick may be platform and shell sensitive. I tested it on Ubuntu Linux with GNU bash 3.2.13; YMMV.


Another approach to 'getting your multi-line variable back out of the makefile' (noted by Eric Melski as 'the tricky part'), is to plan to use the subst function to replace the newlines introduced with define in your multi-line string with \n. Then use -e with echo to interpret them. You may need to set the .SHELL=bash to get an echo that does this.

An advantage of this approach is that you also put other such escape characters into your text and have them respected.

This sort of synthesizes all the approaches mentioned so far...

You wind up with:

define newline


endef

define ANNOUNCE_BODY
As of $(shell date), version $(VERSION) of $(PACKAGE_NAME) has been released.  

It can be downloaded from $(DOWNLOAD_URL).  

endef

someTarget:
    echo -e '$(subst $(newline),\n,${ANNOUNCE_BODY})'

Note the single quotes on the final echo are crucial.


Assuming you only want to print the content of your variable on standard output, there is another solution :

do-echo:
    $(info $(YOUR_MULTILINE_VAR))

Yes. You escape the newlines with \:

VARIABLE="\
THIS IS A VERY LONG\
TEXT STRING IN A MAKE VARIABLE"

update

Ah, you want the newlines? Then no, I don't think there's any way in vanilla Make. However, you can always use a here-document in the command part

[This does not work, see comment from MadScientist]

foo:
    echo <<EOF
    Here is a multiple line text
    with embedded newlines.
    EOF

Not completely related to the OP, but hopefully this will help someone in future. (as this question is the one that comes up most in google searches).

In my Makefile, I wanted to pass the contents of a file, to a docker build command, after much consternation, I decided to:

 base64 encode the contents in the Makefile (so that I could have a single line and pass them as a docker build arg...)
 base64 decode the contents in the Dockerfile (and write them to a file)

see example below.

nb: In my particular case, I wanted to pass an ssh key, during the image build, using the example from https://vsupalov.com/build-docker-image-clone-private-repo-ssh-key/ (using a multi stage docker build to clone a git repo, then drop the ssh key from the final image in the 2nd stage of the build)

Makefile

...
MY_VAR_ENCODED=$(shell cat /path/to/my/file | base64)

my-build:
    @docker build \
      --build-arg MY_VAR_ENCODED="$(MY_VAR_ENCODED)" \
      --no-cache \
      -t my-docker:build .
...

Dockerfile

...
ARG MY_VAR_ENCODED

RUN mkdir /root/.ssh/  && \
    echo "${MY_VAR_ENCODED}" | base64 -d >  /path/to/my/file/in/container
... 

Just a postscript to Eric Melski's answer: You can include the output of commands in the text, but you must use the Makefile syntax "$(shell foo)" rather than the shell syntax "$(foo)". For example:

define ANNOUNCE_BODY  
As of $(shell date), version $(VERSION) of $(PACKAGE_NAME) has been released.  

It can be downloaded from $(DOWNLOAD_URL).  

endef