| 
Home arrow Articles arrow Columns arrow Ask Mr. Make arrow GNU Make escaping: a walk on the wild side
GNU Make escaping: a walk on the wild side Print
Written by John Graham-Cumming   
Sunday, 08 July 2007

Sometimes you find yourself needing to insert a 'special' character in a Makefile: perhaps you need a newline inside a $(error) message, or a space character in a $(subst) or a comma as the argument to a GNU Make function.  Those three simple things can be frustratingly hard in GNU Make; this article takes you through simple GNU Make syntax that removes the frustration.

GNU Make's escaping rules
GNU Make's handling of 'tab' as the start of a command is a legendary language feature, but some other special characters can trip you up.  GNU Make's handling of $, %, ?, *, [, ~, \, and # are all special.

Every GNU Make user is familiar with $ for starting a macro reference.  It's possible to write $(macro) or ${macro} to get the value of macro and if the macro name is a single character (such as a) then the parens can be dropped and the short hand $a used.

To get a literal $ you write $$.  So to define a macro containing a single $ symbol you'd write:

dollar := $$

The % character rears its ugly head in three different places: in the vpath directive, in a $(patsubst) and in a pattern or static-pattern rule.

Escaping % is not as simple as $, but it only needs to be done in the three situation above, and the same rules apply for each.

The three rules for % escaping are:

1. % can be escaped with a single \ character (i.e. \% becomes a literal %.

2. If you need to put a literal \ in front of a % (i.e. you want the \ to not escape the %) then escape it with \ (i.e. \\% becomes a literal \ followed by a % character that will be used for the pattern match).

3. Don't worry about escaping \ anywhere else in a pattern, it will be treated as literal (i.e. \hello is \hello).

?, *, [, and ] get treated specially when they appear in a filename.  A Makefile that does

*.c:
    @command

Will actually search for all .c files in the current directory and define a rule for each.  The target (and the same applies for prerequisites and files mentioned in the include directive) are globbed if they contain a wildcard character.  The globbing characters have the same meaning as in the Bourne shell.

The ~ character is also handled specially in filenames and is expanded to the home directory of the current user.

All of those special filename characters can be escaped with a \.  The following Makefile defines a rule for the file named literally *.c.

\*.c:
    @command

As well as the escaping function mentioned in this section, the \ can also be used as an continuation character at the end of a line:

all: \
prerequisite \
something else
    @command

The # character is used to start a comment and it can be made literal with a \ escape:

pound := \#

I just want a newline!
GNU Make does its best to insulate you from the newline character.  You can't escape a newline, there's no syntax for special characters (e.g. you can't write \n), and even the $(shell) function strips newlines from the returned value.

But you can define a macro that contains a newline using the define syntax:

define newline

endef

(Note that there are two blanks lines in the definition above).  With that definition $(newline) will expand to a newline and it can be used to format an error message nicely:

$(error This is an error message$(newline)with two lines)

Because of GNU Make's rather liberal macro naming rules it's possible to actually define a macro called \n so if you line to keep things looking familiar you can do

define \n

endef

$(error This is an error message $(\n)with two lines)

(More on special macro names in The Twilight Zone below)

Function arguments: space and comma
A problem that many GNU Make users run into is that handling of spaces and commas in GNU Make function arguments.  Consider the following use of $(subst) (which takes three arguments separated by commas: the from text, the to text and the string to change):

spaces-to-commas = $(subst  ,,,$1)

It defines a function called spaces-to-commas to convert all spaces in its argument to commas (might be handy for making a CSV file for example).  Unfortunately, it doesn't work.

It doesn't work for two reasons:

1. The first argument of the $(subst) is a space.  Unfortunately, GNU Make strips all leading and trailing whitespace around function arguments.  In this case, the first argument is interpreted as an empty string.

2. The second argument is a comma.  Unfortunately, GNU Make cannot distinguish between the commas used for argument separators and the comma as an argument.  In addition, there's no way to 'escape' the comma.

Fortunately, it's possible to work around this by knowning that GNU Make does the whitespace stripping, and separation of arguments before it doesn any expansion of the arguments themselves.   So, if we can define a macro containing a space and a macro containing a comma it will be possible to write:

spaces-to-commas = $(subst $(space),$(comma),$1)

to get the desired effect.

Defining a macro containing a comma is easy:

comma := ,

but space is a bit harder.   There are a couple of ways of defining a space.  The first uses the fact that whenever you append to a macro (using +=) a space is inserted before the appended text.

space :=
space +=

Another way is to first define a macro that contains nothing and then use it to surround the space so that it doesn't get stripped by GNU Make

blank :=
space := $(blank) $(blank)

This technique can also be used to get a literal tab character into a macro

blank :=
tab := $(blank)   $(blank)

(The whitespace in the definition of tab was created by hitting the tab key).

Much in the way I defined $(\n) above it's possible to define specially named space and comma macros.  GNU Make's rules are liberal enough to allow us to do:

, := ,

blank :=
space := $(blank) $(blank)
$(space) := $(space)

The first line defines a macro called , (which can be used as $(,) or even $,) containing a comma. 

The last three lines define a macro called space containing a space character and then use it to define a macro name (that's right, it's name is a space character) containing a space.

With that definition it's possible to write $( ) or even $ to get a space character.

Using those definitions the spaces-to-commas function can be written:

spaces-to-commas = $(subst $( ),$(,),$1)

which I think it very clear.

The Twilight Zone
If you read this column regularly, you'll know that I'm not going to let a chance to really mess with GNU Make go by without pushing it to its limits.  Here are some other interesting macro definitions:

# Defining the $= or $(=) macro which has the value =
equals := =
$(equals) := =

# Define the $# or $(#) macro which has the value #
hash := \#
$(hash) := \#

# Define the $: or $(:) macro which has the value :
colon := :
$(colon) := :

# Define the $($$) macro which has the value $
dollar := $$
$(dollar) := $$

These probably aren't useful, but if you really want to push GNU Make syntax to its limits try this:

+:=+

Yes, that defines a variable called + containing a +.

Paul's warning
When I first blogged about some of this (see http://www.jgc.org/blog/labels/gnu%20make.html) I received the following warning from Paul Smith (the maintainer of GNU Make):

I don't recommend using a space as a variable name. I've toyed with the idea of allowing some special syntactic sugar for user-defined functions, that would allow you to omit the $(call ...) prefix and just invoke the user-defined function directly, like:

$(my-func arg1, arg2)

Obviously the way this works is that if make detects a space in the variable name, it would assume that it is a user-defined function. If this were to happen I'd probaby make whitespace in variable names illegal, just to cut down on confusion and bizarre misbehaviors.

OK, I'll be careful.

John Graham-Cumming is Mr Make and Chief Scientist at Electric Cloud, Inc., a Silicon Valley start-up that speeds up builds by 10-20x using cluster technology.
Trackback(0)
Comments (4)add
...
written by Ginger Kid , April 08, 2008
Embedding newlines as canned commands doesn't work inside build scripts since it causes a string to be interpreted as two lines:

.PHONY: all

define nl


endef

TEST:= a$(nl)b

all:
echo $(TEST)

> gmake
echo a
a
b
gmake: b: Command not found
gmake: *** [all] Error 127

report abuse
vote down
vote up
Votes: +0
...
written by David Carson , August 16, 2007
My previous comment is being rendered by the web browser without the backslashes. The $(shell) function should have backslashes at the end of each intermediate line of the function.
report abuse
vote down
vote up
Votes: +0
...
written by David Carson , August 15, 2007
The trick for embedding newlines in the $(error) function does not work for older versions of make. If, like me, you are saddled with ancient systems and must use an older version of make, you can try this trick instead:

ifeq ($(THIS_IS_AN_ERROR),1)
DUMMY_VAR := $(shell
echo "first line" 1>&2;
echo "second line" 1>&2;
echo "last line" 1>&2)
$(error "summary of error")
endif

Note the redirection of 'echo' to stderr, which causes the shell command to have the side effect of writing the message to the screen.
report abuse
vote down
vote up
Votes: +0
...
written by apsownr , August 08, 2007
Here's the more detailed version of the warning

http://www.cygwin.com/ml/cygwin/2004-04/msg00754.html

report abuse
vote down
vote up
Votes: +0
Write comment
smaller | bigger

security image
Write the displayed characters


busy
 
< Prev   Next >

Video News

Whitepaper Spotlight

Stay up to date with Configuration Management and Application Lifecycle Management technology products and services by browsing our featured white papers below: See all the Featured Whitepapers>>
Tool Spotlight
ElectricCommander™
ElectricCommander™ is the only enterprise-class solution for automating and managing software...
Read More
CollabNet
CollabNet Subversion is an enterprise-ready distribution of Subversion® that includes, in one package,...
Read More
AccuRev
AccuRev is a best-of-breed, process-centric software configuration management (SCM) solution for...
Read More
IBM Rational Build Forge Express Edition
IBM Rational Build Forge Express Edition is a flexible and robust build automation framework developed,...
Read More