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:
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:
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:
And everything works.
For a space you need to get a space into a string, I find the easiest way is like this:
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:
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):
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:
Now that starts to look like escaping. In the examples above you can use these variables to make things a little clearer:
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:
You probably don't need any of those, but you never know...
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
$(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.
Thanks a lot guy, it works perfectly well.
The Make help page suggests defining a space this way:
empty:=
space:= $(empty) $(empty)
$(eval $(NEWLINE)ifndef X$(NEWLINE)X=Y$(NEWLINE)endif)
Thanks,
Andrew
$(eval $(NEWLINE)ifndef X$(NEWLINE)X=Y$(NEWLINE)endif)
Thanks,
Andrew
endef
seems to define a NEWLINE in whitespace, so I'm set! Thanks.
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
Your post is precious to use embedded perl in GMAKE programs.
Thus now it is possible to parse and process automatically generated programs.
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.
* 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= )
The `$() $()` and `$(subst ,, )` methods both still work.
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))