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

I want to create a makefile variable that is a multi-line string (for example, the body of an email release message). 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 find a way to do this. Is it possible?

+84
variables makefile
Mar 16 '09 at 4:15
source share
17 answers

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

 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 returning your multi-line variable from the makefile. If you just do the obvious thing to use "echo $ (ANNOUNCE_BODY)", you will see the result that others have posted here - the shell is trying to process the second and subsequent lines of this variable as the commands themselves.

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

 export ANNOUNCE_BODY all: @echo "$$ANNOUNCE_BODY" 

Note the use of $$ANNOUNCE_BODY , which indicates a reference to the shell environment variable, rather than $(ANNOUNCE_BODY) , which will be a regular reference to the make variable. Also, be sure to use quotation marks around the variable reference to make sure that newlines are not interpreted by the shell itself.

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

+120
Mar 16 '09 at 6:39
source share

Another approach to “returning your multiline variable from the makefile” (marked by Eric Melsky as the “hard part”) is to plan on using the subst function to replace the newlines entered with define in your multiline string with \n . Then use -e with echo to interpret them. You may need to set .SHELL = bash to get an echo that does this.

The advantage of this approach is that you also put other similar escape characters in your text and respect them.

This species synthesizes all the approaches mentioned so far ...

You conclude:

 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 that single quotes in the final echo are crucial.

+17
May 4 '11 at 17:55
source share

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

 do-echo: $(info $(YOUR_MULTILINE_VAR)) 
+8
May 24 '12 at 8:29
source share

Yes. You leave newlines with \ :

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

Update

Do you need new lines? Then no, I don’t think Vanilla Make. However, you can always use the document here 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 
+4
Mar 16 '09 at 4:19
source share
+3
Mar 16 '09 at 6:05
source share

Just the postscript for Eric Melsky answer: you can include the output of commands in the text, but you should use the Makefile syntax "$ (shell foo)", and not 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 
+2
Mar 23 2018-11-11T00:
source share

You should use the "define / endef" Make construct:

 define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). etc, etc. endef 

Then you must pass the value of this variable to the shell command. But if you do this by replacing the Make variable, this will split the command into several:

 ANNOUNCE.txt: echo $(ANNOUNCE_BODY) > $@ # doesn't work 

Qouting will not help either.

The best way to pass a value is to pass it through an environment variable:

 ANNOUNCE.txt: export ANNOUNCE_BODY:=$(ANNOUNCE_BODY) ANNOUNCE.txt: echo "$${ANNOUNCE_BODY}" > $@ 

Note:

  • The variable is exported for this specific purpose, so you can reuse this environment, which will not be heavily polluted;
  • Use an environment variable (double qoutes and curly braces around the variable name);
  • Using quotes around a variable. Without them, newline characters will be lost, and all text will appear on one line.
+2
Jun 09 2018-12-12T00:
source share

Why don't you use the \ n character in your line to define the end of the line? Also add an extra backslash to add it across multiple lines.

 ANNOUNCE_BODY=" \n\ Version $(VERSION) of $(PACKAGE_NAME) has been released \n\ \n\ It can be downloaded from $(DOWNLOAD_URL) \n\ \n\ etc, etc" 
+1
Mar 16 '09 at 6:43
source share

I believe that the safest answer for cross-platform use would be to use a single echo per line:

  ANNOUNCE.txt: rm -f $@ echo "Version $(VERSION) of $(PACKAGE_NAME) has been released" > $@ echo "" >> $@ echo "It can be downloaded from $(DOWNLOAD_URL)" >> $@ echo >> $@ echo etc, etc" >> $@ 

This avoids any assumptions regarding the echo version.

+1
Apr 26 2018-12-12T00:
source share

With GNU Make, the .ONESHELL option is your friend when it comes to multi-line shell fragments. Combining the hints from the other answers, I get:

 VERSION = 1.2.3 PACKAGE_NAME = foo-bar DOWNLOAD_URL = $(PACKAGE_NAME).somewhere.net define nwln endef define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). etc, etc. endef .ONESHELL: # mind the *leading* <tab> character version: @printf "$(subst $(nwln),\n,$(ANNOUNCE_BODY))" 

Make sure that when copying and pasting the above example into your editor, any <tab> characters are saved, otherwise the version target will be violated!

+1
Feb 13 '14 at 12:26
source share

This does not provide a document here, but it displays a multi-line message in a way that is suitable for pipes.

=====

 MSG = this is a\\n\ multi-line\\n\ message method1: @$(SHELL) -c "echo '$(MSG)'" | sed -e 's/^ //' 

=====

You can also use macros called by Gnu:

=====

 MSG = this is a\\n\ multi-line\\n\ message method1: @echo "Method 1:" @$(SHELL) -c "echo '$(MSG)'" | sed -e 's/^ //' @echo "---" SHOW = $(SHELL) -c "echo '$1'" | sed -e 's/^ //' method2: @echo "Method 2:" @$(call SHOW,$(MSG)) @echo "---" 

=====

Here's the conclusion:

=====

 $ make method1 method2 Method 1: this is a multi-line message --- Method 2: this is a multi-line message --- $ 

=====

+1
Oct 10 '14 at 21:11
source share

In the spirit of .ONESHELL, you can come close to the difficult conditions .ONESHELL:

 define _oneshell_newline_ endef define oneshell @eval "$$(printf '%s\n' '$(strip \ $(subst $(_oneshell_newline_),\n, \ $(subst \,\/, \ $(subst /,//, \ $(subst ','"'"',$(1))))))' | \ sed -e 's,\\n,\n,g' -e 's,\\/,\\,g' -e 's,//,/,g')" endef 

A usage example would be something like this:

 define TEST printf '>\n%s\n' "Hello World\n/$$$$/" endef all: $(call oneshell,$(TEST)) 

This shows the output (assuming pid 27801):

 > Hello World\n/27801/ 

This approach allows you to use some additional features:

 define oneshell @eval "set -eux ; $$(printf '%s\n' '$(strip \ $(subst $(_oneshell_newline_),\n, \ $(subst \,\/, \ $(subst /,//, \ $(subst ','"'"',$(1))))))' | \ sed -e 's,\\n,\n,g' -e 's,\\/,\\,g' -e 's,//,/,g')" endef 

These shell options will be:

  • Print each command when it is executed
  • Exiting the first failed team
  • Use shell variables undefined as an error

Other interesting features are likely to tell themselves.

+1
Nov 14 '16 at 5:22
source share

I like alhadis better. But to keep the formatting of the columns, 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 

Outputs:

 :: 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 
+1
May 28 '17 at 17:10
source share

Not a very useful answer, but just to indicate that "define" does not work as Ax answered (did not fit into the comment):

 VERSION=4.3.1 PACKAGE_NAME=foobar DOWNLOAD_URL=www.foobar.com define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released It can be downloaded from $(DOWNLOAD_URL) etc, etc endef all: @echo $(ANNOUNCE_BODY) 

It gives an error that the command "This" could not be found, so it tries to interpret the second line of ANNOUNCE BODY as a command.

0
Mar 16 '09 at 6:29
source share

This worked for me:

 ANNOUNCE_BODY="first line\\nsecond line" all: @echo -e $(ANNOUNCE_BODY) 
0
Jan 28 '10 at 11:25
source share

The GNU Makefile can do the following. This is ugly, and I will not say that you should do it, but I do it in certain situations.

 PROFILE = \ \#!/bin/sh.exe\n\ \#\n\ \# A MinGW equivalent for .bash_profile on Linux. In MinGW/MSYS, the file\n\ \# is actually named .profile, not .bash_profile.\n\ \#\n\ \# Get the aliases and functions\n\ \#\n\ if [ -f \$${HOME}/.bashrc ]\n\ then\n\ . \$${HOME}/.bashrc\n\ fi\n\ \n\ export CVS_RSH="ssh"\n # .profile: echo -e "$(PROFILE)" | sed -e 's/^[ ]//' >.profile 

make .profile creates a .profile file if it does not exist.

This solution was used when the application will use the GNU Makefile only in the POSIX shell environment. The project is not an open source project where there is a platform compatibility issue.

The goal was to create a Makefile that makes it easy to set up and use a specific kind of workspace. Makefile combines various simple resources without requiring things like another special archive, etc. This is, in a sense, a shell archive. The procedure can then say things such as dumping this Makefile into a folder for work. Set up your workspace, type make workspace , then type blah, type make blah , etc.

What may seem complicated is figuring out what a shell is. The above work is close to the idea of ​​specifying a document here in a Makefile. Whether this is a good idea for general use is a whole other problem.

0
Jul 21 '11 at 15:31
source share

Use string replacement :

 VERSION := 1.1.1 PACKAGE_NAME := Foo Bar DOWNLOAD_URL := https://go.get/some/thing.tar.gz ANNOUNCE_BODY := Version $(VERSION) of $(PACKAGE_NAME) has been released. \ | \ | It can be downloaded from $(DOWNLOAD_URL) \ | \ | etc, etc 

Then put in your recipe

  @echo $(subst | ,$$'\n',$(ANNOUNCE_BODY)) 

This works because Make replaces all occurrences | (note the space) and replaces it with a newline ( $$'\n' ). You can think of equivalent shell script calls as something like this:

Before:

 $ echo "Version 1.1.1 of Foo Bar has been released. | | It can be downloaded from https://go.get/some/thing.tar.gz | | etc, etc" 

After:

 $ echo "Version 1.1.1 of Foo Bar has been released. > > It can be downloaded from https://go.get/some/thing.tar.gz > > etc, etc" 

I'm not sure that $'\n' is available on systems other than POSIX, but if you can access a single newline character (even by reading the line from an external file), the basic principle will be the same.

If you have a lot of these messages, you can reduce the noise using a macro :

 print = $(subst | ,$$'\n',$(1)) 

If you call it like this:

 @$(call print,$(ANNOUNCE_BODY)) 

Hope this helps someone. =)

0
Oct 03 '16 at 6:06
source share



All Articles