How to replace using patsubst Makefile? - makefile

I have a makefile somewhat like this.
I need to generate a file and move that as abc.cpp (Basically get rid of anything after underscore including underscore
xyz:= abc_def
$(xyz):
(some commands here which generates a file)
mv file /tmp/$(patsubst _%,"",$#)
However this does not work. In fact it doesn't ever match the underscore "_" in $#
mv file /tmp/abc.cpp is what i want
How does the "%" wildcard work in patsusbst?

The patsubst function won't work for you, because it can match only ONE pattern. You want to match two patterns: anything before the _ and anything after the _. $(patsubst _%,...) only matches words that begin with _, and your word abc_def doesn't begin with _, so the patsubst is a no-op.
To do what you want using GNU make functions you need to play a trick; something like:
mv file /tmp/$(firstword $(subst _, ,$#))
This splits the string into words by changing the _ to space, then takes the first word.

If you don't shy away from using helper code (i.e. include a GNUmake library) then the GNUmake table toolkit surely can do the trick:
include gmtt.mk
xyz:= abc_def
$(xyz):
(some commands here which generates a file)
mv file /tmp/$(firstword $(call glob-match,$#,*_*)).cpp
The glob-match function splits a string in streaks of matching elements, where every glob character (*,?,[...]) and verbatim string portions (in your case just the _) constitute one match. Or simply said, $(call glob-match,this_is_a_string,*_is_a_*) splits this_is_a_string into a list this _is_a_ string (notice the spaces).

Related

What is the difference between % and * wildcards in Make?

I'm currently learning Make and am struggling to wrap my head around the wildcard concept. Specifically, it seems like there are two symbols that can represent wildcards: * and %
For example, say I want to to generate a variable that identifies all .c source files in the working directory. I would use the traditional * symbol in *.c However, if I use the patsubst function, I am required to use the % symbol instead of * symbol:
// WORKS
SRC = $(wildcard *.c) # list of source files
OBJS = $(patsubst %.c, %.o, $(SRC)) # list of object files
// DOES NOT WORK!!!!
SRC = $(wildcard *.c) # list of source files
OBJS = $(patsubst *.c, *.o, $(SRC)) # list of object files
Can someone explain the difference between * and % in the context of Make wildcards?
Can someone explain the difference between * and % in the context of Make wildcards?
TL;DR:
This is all specific to GNU make.
* and a few other characters are special to the wildcard function, but not to patsubst. This kind of pattern is expanded to the names of existing files and directories that match the pattern, possibly more than one for each pattern.
% is special to the patsubst function. This kind of pattern is used to select matching strings provided by the makefile or directly by make, in the process capturing the part matching the % for later use.
Both kinds have application to the target and prerequisite lists of rules, but their significance there is somewhat different from each other.
None of these are significant to make in recipes, but wildcard-style patterns are significant to the shell, and the shell will interpret them in recipes that it executes.
General
Understand first that $(wildcard) and $(patsubst) are features specific to GNU's implementation of make, and GNU make also attributes special significance to %, *, and a few other characters in rule targets and prerequisites. The POSIX specifications for make say nothing about any of that. GNU make is widely used these days, and with good reason, but it is not the only make you might encounter. If you want maximum portability among make implementations then you must avoid these altogether.
Understand also that a complete, albeit rather basic response to the question would be simply "yes, the wildcard and patsubst functions recognize different special characters." These functions do different things, so it is potentially useful that the special characters of one can be used as ordinary characters in the other.
Wildcards
The asterisk (*) is among the special characters recognized by the Bourne shell for "pathname expansion", which replaces patterns with the names of possibly-many existing files and directories matching the pattern. There are more characters than just * significant in pathname expansion, but % is not among them. Look up that term for a full description.
Additionally, there is the tilde (~), which make and some shells recognize for "tilde expansion", which involves interpreting the first segments of affected paths as specified users' home directories.
The GNU make documentation describes the characters and constructs it recognizes for pathname and tilde expansion as "wildcards", and it has this to say about them:
Wildcard expansion is performed by make automatically in targets and in prerequisites. In recipes, the shell is responsible for wildcard expansion. In other contexts, wildcard expansion happens only if you request it explicitly with the wildcard function.
(GNU make manual, section 4.4)
And that's where the wildcard function appearing in the question comes in -- if you want to perform the same wildcard expansion that make performs automatically on target and prerequisite names in some other context, such as a variable definition, then you can use the wildcard function to get it. Thus,
SRC = $(wildcard *.c)
results variable SRC representing a list of all the existing files in the working directory at the time the makefile is parsed whose names end with .c.
On the other hand, % is not significant for pathname or tilde expansion, so
SRC = $(wildcard %.c)
will expand to literally %.c.
Patterns
The shell documentation uses the term "pattern" to mean shell input that is interpreted to be subject to pathname expansion, rather than being literal. However, GNU make reserves the term for a different kind of pattern in which the % character features. This is relevant in two main areas: the patsubst function and pattern rules.
The patsubst function
The patsubst function computes one string or series of strings from another by replacing those that match a given pattern with a specified replacement. In a pattern, the first % character, if any, matches any number of characters, including zero. In this sense, it can be described as a wildcard, and the manual does, somewhat confusingly, use that term. If the replacement also contains at least one % then the first is replaced by the substring that was matched by the % in the pattern.
Note well that this has nothing inherently to do with file names, existing or otherwise.
Thus, with
SRC = main.c other.c
OBJ = $(patsubst %.c,%.o,$(SRC))
the %.c pattern matches first main.c (with % matching main) and then other.c (with % matching other), and the result for OBJ is the same as this:
OBJ = main.o other.o
regardless of whether any files named main.c, other.c, main.o, or other.o exist.
Pattern rules
If the target of a rule contains a %, then it is interpreted by GNU make as a pattern rule. Pattern rules use % in much the same way that patsubst does. When make needs to build a target but does not have a rule for that specific target, it will check whether the target name matches the target pattern of any pattern rule (including some built-in ones). There does not need to be any existing file of that name. If it finds a match, then it will use that rule to build the target. And in that case, for the purpose of building the target in question, the first % character, if any, in any prerequisite names specified in that rule will be replaced by the stem that matched the % in the target, much like in patsubst.
Combinations
Usually, only one of these kinds of pattern matching / expansion is desired in any given context, but sometimes they can usefully be combined. For example, consider this pattern rule:
%.o: %.c *.h
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $# $<
That says that any .o file can be built from a corresponding .c file and all the .h files in the working directory by applying the provided recipe.
The wildcard function expects wildcard characters (~, *, ? and [...] which match on file names) while the patsubst function expects a pattern (%) that operates on text. As such $(patsubst *.c, *.o, $(SRC)) means replace the literal string *.c with the string *.o and SRC probably does not contain any literal *.c strings.

Makefile string with spaces

var := 'C:/some/path here' labas
all:
#echo $(words $(var))
This returns 3.
How to make it return 2? Or make does not work the way I think??
Make definitely doesn't work the way you think, if you think that make has any interest in quotes of any type :). Make ignores (i.e., treats as normal characters like any other) both single- and double-quote characters in virtually every situation, and all make functions work in simple whitespace delimiters without regard to quote characters.
In general, it's not practical to use make with any system that requires spaces in target names. It just doesn't work: too much of make is based on whitespace as a separator character and there's no general, common way to escape the whitespace that works everywhere.
In your very particular case you can play a trick like this:
E :=
S := $E $E
path := C:/some/path here
xpath := $(subst $S,^,$(path))
var := $(xpath) labas
all:
#echo $(words $(var))
#echo $(subst ^,$S,$(var))
Basically, this creates a variable $S which contains a space, then it substitutes all spaces in the variable path with some other, non-space character (here I chose ^ but you can pick what you like, just note it has to be a character which is guaranteed to not appear in the path).
Now this path doesn't contain whitespace so you can use it with make functions.
This has major downsides: not only the complexity visible here, but note these paths cannot be used in target names since they won't actually exist on disk.
In general, you should simply not use paths containing whitespace with make. If you must use paths containing whitespace, then very likely make is not the right tool for you to be using.

Makefile patsubst multiple occcurence in replacement pattern

I have 3 dirs and want to link an executable against the libraries
I already have the directory list:
DIRS=Math Graph Test
I want to get the library list like this:
LIBS=Math/libMath.a Graph/libGraph.a Test/libTest.a
If I use this:
$(DIRS:%=%/%.a)
I get:
Math/%.a Graph/%.a Test/%.a
GNU Makefile says:
Only the first ‘%’ in the pattern and replacement is treated this way; any subsequent ‘%’ is unchanged.
Here I need 2 occurrences to be replaced, not just the first one.
You'll have to use a loop:
LIBS := $(foreach D,$(DIRS),$D/lib$D.a)
eventually I used:
LIBS := $(join $(DIRS), $(DIRS:%=/lib%.a))

How to remove remove multiple different extensions from a word list in GNU make?

I have a list of filenames:
FILES := a.b c.d e.f
and I want to remove the extensions (suffixes) of all words to obtain:
a c e
what is the best way to do that?
The best I could come up with was "cheating" with shell:
$(shell for f in $(INS_NODIR); do echo -n "$${f%.*} "; done )
but I am surprised there was not a more "built-in" way of doing this only with make built-in functions.
thing I tried:
patsubst. It seems that it can only have one single wildcard, others being treated literally, and I'd like to do something like %.%, %
looking for a notsufix function.
I was surprised that this does not exist, since the dir function has notdir counterpart, but the suffix function that exactly extracts extensions does not have a notsuffix counterpart
Simple, just:
NAMES = $(basename $(FILES))
See the GNU make manual section on Functions for File Names

How to prevent GNU Make from executing when a variable definition has trailing spaces

I had a simple variable definition in my makefile:
THIS := ~/edan
and it had trailing spaces in the line.
Later, when I defined another variable in terms of this one:
WARES := $(THIS)/wares
the actual variable defined was /home/directory/edan wares
and then the make clean rule removed /home/directory/edan instead of what I wanted it to.
How can I prevent a makefile from executing if a line has trailing spaces?
Although you could write the Makefile so that it checks the variable for trailing whitespace and exits if it finds some, there are better ways of dealing with this situation.
In this instance, I would recommend using the addsuffix function to catenate $(THIS) and /wares instead of doing it manually. The addsuffix function will strip the trailing whitespace for you. In other words:
WARES := $(addsuffix /wares,$(THIS))
If you really want it to exit if it discovers trailing whitespace, you could do this:
THIS := ~/edan
ifneq "$(THIS)" "$(strip $(THIS))"
$(error Found trailing whitespace)
endif

Resources