Skip to main content

Escaping comma and space in GNU Make

Sometimes you need to hide a comma or a space from GNU Make's parser because GNU Make might strip it (if it's a space) or interpret it as an argument separator (for example, in a function invocation).

First the problem. If you wanted to change every , into a ; in a string in GNU Make you'd probably head for the $(subst) function and do the following:

$(subst ,,;,$(string))

See the problem? The argument separator for functions in GNU Make is , and hence the first , (the search text) is considered to be separator. Hence the search text in the above is actually the empty string, the replacement text is also the empty string and the ;, is just preprended to whatever is in $(string).

A similar problem occurs with spaces. Suppose you want to replace all spaces with ; in a string. You get a similar problem with $(subst), this time because the leading space is stripped:

$(subst ,;,$(string))

That extra space isn't an argument it's just extraneous whitespace and hence it is ignored. GNU Make just ends up appending ; to the $(string).

So, how do you solve this?

The answer is define variables that contain just a comma and just a space and use them. Because the argument splitting is done before variable expansion it's possible to have an argument that's a comma or a space.

For a comma you just do:

comma := ,
$(subst $(comma),;,$(string))

And everything works.

For a space you need to get a space into a string, I find the easiest way is like this:

space :=
space +=
$(subst $(space),;,$(string))

That works because += always space separates the value of the variable with the appended text.

Now, GNU Make has really liberal variable naming rules. Pretty much anything goes, so it's possible to define a variable with the name , or even having the name consisting of a space character.

First, here's how to define them:

, := ,
space :=
space +=
$(space) :=
$(space) +=

The first line is clear, it does an immediate define of a , to the variable named ,. The second one is a little more complex. First, I define a variable called space which contains a space character and then I use it to define a variable whose name is that space character.

You can verify that these work using $(warning) (I like to wrap the variable being printed in square brackets for absolute certainty of the content):

$(warning [$(,)])
$(warning [$( )])

$ make
Makefile:1: [,]
Makefile:2: [ ]

Yes, that's pretty odd looking, but it gets stranger. Since GNU Make will interpret $ followed by a single character as a variable expansion you can drop the braces and write:

$(warning [$,])
$(warning [$ ])

Now that starts to look like escaping. In the examples above you can use these variables to make things a little clearer:

$(subst $(,),;,$(string))
$(subst $ ,;,$(string))

Note that you have to use the $(,) form because function argument splitting occurs before the expansion and GNU Make gets confused. In the second line the space is 'escaped' with the $ sign.

You might be wondering about other crazy variable names: here are a few that's possible with GNU Make:

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

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

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

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

; := ;
% := %

You probably don't need any of those, but you never know...

Comments

Mad Scientist said…
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.
Unknown said…
@John Graham-Cumming

Thanks a lot guy, it works perfectly well.
Unknown said…
After actually trying this I have discovered that your space function doesn't do what you think it does and my best guess is that even though it looks like a space it's actually EOL.

The Make help page suggests defining a space this way:
empty:=
space:= $(empty) $(empty)
dontfidget said…
Thanks for the info. How can I escape a newline in gnu make so I can do

$(eval $(NEWLINE)ifndef X$(NEWLINE)X=Y$(NEWLINE)endif)

Thanks,

Andrew
dontfidget said…
Thanks for the info. How can I escape a newline in gnu make so I can do

$(eval $(NEWLINE)ifndef X$(NEWLINE)X=Y$(NEWLINE)endif)

Thanks,

Andrew
dontfidget said…
define NEWLINE

endef

seems to define a NEWLINE in whitespace, so I'm set! Thanks.
C.Alberto said…
Thank you very much,

finally I was able to embed perl programs into just one gmake program.

It is extremely useful when you have to parse&mangle automatically generated files before compilation
C.Alberto said…
Thank you very much.

Your post is precious to use embedded perl in GMAKE programs.

Thus now it is possible to parse and process automatically generated programs.
Cube said…
Thanks for these crazy tips. I was trying out some of them and looking at how the Make database stores them (--print-data-base).

There are two ways that I prefer for getting the $( ) space variable set in one line:

$() $() := $() $()

which is essentially the $(empty) solution but using $() with the assumption that it always resolves to nil, and

$(subst ,, ) := $(subst ,, )

$(subst ,, ) is how the Make database appears to be storing a space as assigned by any of the above methods.
tanzislam said…
The preparation of the $(space) variable will no longer work as of GNU Make v4.3. From the Release Notes:


* WARNING: Backward-incompatibility!
Previously appending using '+=' to an empty variable would result in a value
starting with a space. Now the initial space is only added if the variable
already contains some value. Similarly, appending an empty string does not
add a trailing space.


A workaround is as follows:

space := a
space := $(space:a= )
Caleb Maclennan said…
Note that GNU Make 4.3 changed the behaviour so that variable assignment with `+=` no longer adds leading space. The primary method in this article will no longer work.

The `$() $()` and `$(subst ,, )` methods both still work.
Daniel said…
FYI, the following syntax no longer works since GNU make 4.3:

space :=
space +=
$(subst $(space),;,$(string))

due to an intentional backwards incompatible change https://lists.gnu.org/archive/html/bug-make/2020-01/msg00057.html

As an alternative it is possible to write

space := $(null) $(null)
$(subst $(space),;,$(string))

Popular posts from this blog

How to write a successful blog post

First, a quick clarification of 'successful'. In this instance, I mean a blog post that receives a large number of page views. For my, little blog the most successful post ever got almost 57,000 page views. Not a lot by some other standards, but I was pretty happy about it. Looking at the top 10 blog posts (by page views) on my site, I've tried to distill some wisdom about what made them successful. Your blog posting mileage may vary. 1. Avoid using the passive voice The Microsoft Word grammar checker has probably been telling you this for years, but the passive voice excludes the people involved in your blog post. And that includes you, the author, and the reader. By using personal pronouns like I, you and we, you will include the reader in your blog post. When I first started this blog I avoid using "I" because I thought I was being narcissistic. But we all like to read about other people, people help anchor a story in reality. Without people your bl

Your last name contains invalid characters

My last name is "Graham-Cumming". But here's a typical form response when I enter it: Does the web site have any idea how rude it is to claim that my last name contains invalid characters? Clearly not. What they actually meant is: our web site will not accept that hyphen in your last name. But do they say that? No, of course not. They decide to shove in my face the claim that there's something wrong with my name. There's nothing wrong with my name, just as there's nothing wrong with someone whose first name is Jean-Marie, or someone whose last name is O'Reilly. What is wrong is that way this is being handled. If the system can't cope with non-letters and spaces it needs to say that. How about the following error message: Our system is unable to process last names that contain non-letters, please replace them with spaces. Don't blame me for having a last name that your system doesn't like, whose fault is that? Saying "Your

The Elevator Button Problem

User interface design is hard. It's hard because people perceive apparently simple things very differently. For example, take a look at this interface to an elevator: From flickr Now imagine the following situation. You are on the third floor of this building and you wish to go to the tenth. The elevator is on the fifth floor and there's an indicator that tells you where it is. Which button do you press? Most people probably say: "press up" since they want to go up. Not long ago I watched someone do the opposite and questioned them about their behavior. They said: "well the elevator is on the fifth floor and I am on the third, so I want it to come down to me". Much can be learnt about the design of user interfaces by considering this, apparently, simple interface. If you think about the elevator button problem you'll find that something so simple has hidden depths. How do people learn about elevator calling? What's the right amount of