Wednesday, 29 May 2013

GNU Make quirk

(Updated 2013-07-20: Correctly describe the issue and provide examples)

Make has several useful built-in functions, among which $(patsubst pattern,replacer,string). The pattern can contain the character % which is a wildcard meaning any string and that can be referred in the replacer parameter. Only the first occurrence of % is treated with this special meaning, just as the manual says.

What the manual doesn't say is that only the form %bla works, while the form bla% does NOT work GNU Make's '%' is not analogous to shell's '*', so you can't expect to remove substrings in the middle of a string without knowing the exact string until the part you want to remove.

This does not work if you want to obtain '../some/CPU12/include' in OTHERPATH:

PATH := ../some/prefCPU12/include
OTHERPATH := $(patsubst pref%,%,$(PATH))

The result will be:
 OTHERPATH = ../some/prefCPU12/include
This bahaviour is somewhat asymetric to how subst behaves and how one would expect it to, considering '%' is explained as a glob pattern:

0 eddy@heidi /tmp $ cat makefile
PATH      := ../some/prefCPU12/include
OTHERPATH := $(patsubst pref%,%,$(PATH))
SPATH     := $(subst pref,,$(PATH))

    @echo "PATH      = $(PATH)"
    @echo "OTHERPATH = $(OTHERPATH)"
    @echo "SPATH     = $(SPATH)"
0 eddy@heidi /tmp $ make
PATH      = ../some/prefCPU12/include
OTHERPATH = ../some/prefCPU12/include
SPATH     = ../some/CPU12/include
Not sure what make developers would say about this, and I am not sure if having % work as a glob pattern and using it, instead of invoking sed would be better for performance, but sure I would like to have the option :-) .


Jeff Epler said...

You didn't say what you tried, so I tried this on GNU Make 3.81 (2006):

words = fee fie foe fum

.PHONY: default
@echo $(patsubst f%,%g,$(words))
@echo $(patsubst %e,q%,$(words))
@echo $(patsubst f%e,x%x,$(words))

It prints:
eeg ieg oeg umg
qfe qfi qfo fum
xex xix xox fum

which is what I expected, showing that % works at the end, start, or even middle of a word. (note that each @echo line must be indented with a tab in order to obey Makefile syntax)

eddyp said...

@Jeff: GNU Make version 3.82. I checked on Linux and on Cygwin.

Anonymous said...

I can not reproduce your issue, either in 3.82 or with the latest version in the git repo. Can you show us what you are doing where you think make gets it wrong?

Anonymous said...

I wouldn't say % does not behave like shell * because it, in fact, appears to work exactly like that. Your description clearly shows the pattern must match the whole string and so do glob patterns in shell, both for file name generation and case statements.