Is it possible to create a multi-line string variable in a Makefile
Solution 1
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.
Solution 2
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.
Solution 3
Assuming you only want to print the content of your variable on standard output, there is another solution :
do-echo:
$(info $(YOUR_MULTILINE_VAR))
Solution 4
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
...
Solution 5
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
Comments
-
jonner almost 2 years
I want to create a makefile variable that is a multi-line string (e.g. the body of an email release announcement). something like
ANNOUNCE_BODY=" Version $(VERSION) of $(PACKAGE_NAME) has been released It can be downloaded from $(DOWNLOAD_URL) etc, etc"
But I can't seem to find a way to do this. Is it possible?
-
jonner about 15 yearsThis is true, but it doesn't give me any formatting (newlines). It just becomes a single line of text
-
Roalt about 15 yearsSee my answer on why it does not work (at least from my attempt)
-
Roalt about 15 yearsI prefer the answer of Erik Melski but this might do the trick already for you, depending on your application.
-
Shahbaz over 12 yearsI got a question on this. This works principally fine, except I see an extra "space" in the beginning of every line (except the first). Does this happen to you to? I can put all the text in one line, separated by \n so effectively creating the output I like. The problem is it looks very ugly in the Makefile itself!
-
Shahbaz over 12 yearsI found a workaround. I put the text through
$(subst \n ,\n,$(TEXT))
although I wish there was a better way! -
mbauman about 12 yearsMulti-line here-documents do not work as described in GNU Make.
-
Ding-Yi Chen about 12 yearsThe manual does show you the proper way that works: Use
echo
. -
MadScientist about 12 yearsMultiline here docs inside recipes won't work in ANY standard version of make that supports the POSIX standard: the make standard requires that each separate line of the recipe must be run in a separate shell. Make does not do any parsing on the command to tell that it's a here-document or not, and handle it differently. If you know of some variant of make that does support this (I've never heard of one) you should probably state it explicitly.
-
MadScientist about 12 yearsNote that "echo -e" is not portable. You should probably prefer printf(1) instead.
-
anatoly techtonik almost 11 years
export ANNOUNCE_BODY
only sets the variable inside rules - it doesn't allow referencing $$ANNOUNCE_BODY to define other variables. -
Eric Melski almost 11 years@techtonik if you want to use the value of
ANNOUNCE_BODY
in other variable definitions, just reference it like any other make variable. For example,OTHER=The variable ANNOUNCE_BODY is $(ANNOUNCE_BODY)
. Of course you'll still need theexport
trick if you want to getOTHER
out in a command. -
blueyed about 10 yearsUnfortunately that does not work:
make version printf "Version 1.2.3 of foo-bar has been released. /bin/sh: 1: Syntax error: Unterminated quoted string make: *** [version] Error 2
(GNU make 3,81) -
sphakka about 10 years@blueyed, I just tested it with GNU Make 3.82 and GNU bash 4.2.45(1)-release: it works as expected. Also, please check the presence of the leading TAB character, instead of blanks, in front of the
@printf ...
statement -- it looks like TABs are always rendered as 4 spaces... -
blueyed about 10 yearsIt appears that
.ONESHELL
is new in make 3.82. -
blueyed about 10 yearsbtw: the error when using spaces instead of a tab would be
*** missing separator. Stop.
. -
mschilli over 9 yearsgreat answer, however, I had to remove the
=
afterdefine ANNOUNCE_BODY
to get it running. -
Guillaume Papin almost 8 yearsThis no-op rule produced an unwanted message:
make: 'do-echo' is up to date.
. By using a "no op" comand I was able to silence it:@: $(info $(YOUR_MULTILINE_VAR))
-
jlettvin almost 7 yearsI like this one best. But to keep columnar formatting, add one more thing. ` SYNOPSIS := :: Synopsis: Makefile\ | ::\ | :: Usage:\ | :: make .......... : generates this message\ | :: make synopsis . : generates this message\ | :: make clean .... : eliminate unwanted intermediates and targets\ | :: make all ...... : compile entire system from ground-up\ endef
-
jlettvin almost 7 yearsComments do not allow code. Will send as an answer. I like this one best. But to keep columnar formatting, add one more thing. ` SYNOPSIS := :: Synopsis: Makefile` ` | ::` ` | :: Usage:` ` | :: make .......... : generates this message` ` | :: make synopsis . : generates this message` ` | :: make clean .... : eliminate unwanted intermediates and targets` ` | :: make all ...... : compile entire system from ground-up` ` endef`
-
Admin almost 7 yearsA program's synopsis should be easy and obvious to locate. I'd recommend adding this level of information in a readme and/or manpage. When a user runs
make
, they generally do so expecting to start a build process. -
Admin almost 7 years@jlettvin See my response to your answer. A program's synopsis should definitely not be embedded inside a Makefile, especially not as a default task.
-
Xennex81 almost 7 yearsI have wanted many times to just see a list of make targets. Your comment makes no sense. What users expect is irrelevant if it takes them 3 seconds to know what to do, whereas in lieu of any information like this, it can sometimes take hours.
-
Xennex81 almost 7 yearsUsing expectations as a reason to do something is also a circular argument: because people expect it, we must do it, and because we do it, they expect it.
-
M3D over 6 years@GuillaumePapin A bit late, but you can use
.PHONY
to tell your Makefile that there is nothing to check for that rule. Makefiles were originally for compilers, if I am not mistaken, somake
is doing some magic that I don't understand to anticipate that the rule will not change anything, and as such assumes it to be 'done'. Adding.PHONY do-echo
in your file will tellmake
to ignore this and run the code anyway. -
Daniel Stevens over 4 yearsYou can place
$(info ...)
outside of a make rule. It will still generate output. -
Daniel Stevens over 4 yearsDocumentation: Make Control Functions
-
Ярослав Рахматуллин over 2 yearsDo you know why it's ok to double-define a target like that?
-
Ivan Ustûžanin over 2 years@ЯрославРахматуллин that's not a double-define, the first line is the target-specific variable definition, and only the second line is the target definition.