What does a percent symbol do in a makefile? - makefile

I have a makefile that looks like this :
include $(patsubst %,$(src)/%/Make.tests, $(TEST_SUBDIRS))
%-test:
Something here
I understand what it is intended for in the target rule line. What is the % sign doing in the first line ? Does it have anything to do percent sign in the target rule line ?
When I write make sometarget, are the lines in the makefile that are not written as part of any rule (like the first line in this makefile) executed at all ? If yes, then what is the order of execution ?

As you can read in the GNU make manual, the percent acts as a wildcard. The first argument of the patsubst function forms the pattern. Each item/word in the last argument is compared against this pattern, and if it matches, it is replaced with the second argument. If there is a wildcard symbol (%) in the pattern, this will match any number of characters, and these characters are copied into the replacement string at the place of the % in the second argument.
In your example the pattern is just the wildcard symbol, so it will match any word in the last argument to the function, and this word will be copied into the replacement string (the second argument) at the place of the %.
An example may make things more clear. Let's assume TEST_SUBDIRS contains two names.
TEST_SUBDIRS := test1 test2
include $(patsubst %,$(src)/%/Make.tests, $(TEST_SUBDIRS))
This is then equivalent to the following.
include $(src)/test1/Make.tests $(src)/test2/Make.tests
A makefile is processed sequentially, line by line. Variable assignments are "internalized", and include statements cause the contents of other files to be inserted literally at that location after which that content is processed as part of the makefile.
A dependency graph is formed from the rules as they are being read in, and after the entire file is processed, the necessary recipes are executed to update the requested target.

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.

How to replace using patsubst 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).

Can a makefile pattern match contain both prefix and suffix as vairables?

I am trying to match a pattern in the rule to compile and generate binary files from c files like below
$(BIN_TARGETS) : $(BIN_DIR)/%.$(BIN_EXT) : $(BIN_DIR)/%.$(EXE_EXT)
I see the following error
../../rules/mkrules.mk:13: target 'bin/hello.bin' doesn't match the target pattern
I have tried the below and it seems to work
$(BIN_TARGETS) : $(BIN_DIR)/%.bin : $(BIN_DIR)/%.elf
I would like to make the file name extensions not fixed.
I have assigned the variables as shown below at the start of makefile
EXE_EXT = .elf
BIN_EXT = .bin
I see here it is mentioned
"Note that expansion using ‘%’ in pattern rules occurs after any variable or function expansions, which take place when the makefile is read."
So, aren't variables supposed to be expanded before % is substituted?
The answer is simple:
EXE_EXT = .elf
BIN_EXT = .bin
$(BIN_TARGETS) : $(BIN_DIR)/%.$(BIN_EXT) : $(BIN_DIR)/%.$(EXE_EXT)
So $(BIN_DIR)/%.$(BIN_EXT) expands to $(BIN_DIR)/%..bin (note double periods).
You'll have to decide whether you want the period either in the variable or in the pattern, but it can't be in both places.

Makefile: reuse value of % of a pattern rule inside the recipe

In a Makefile I have:
images/schematic.pdf: images/schematic.svg
inkscape -D -z --file=$^ --export-pdf=$# --export-latex
sed -i "s:schematic:images/schematic:g" $#_tex
What this rule does is:
Use inkscape to convert to a latex-ready .pdf PDF file + its corresponding .pdf_tex text file (see this answer: https://tex.stackexchange.com/a/2107/104581)
Modify the mentioned .pdf_tex file so that it will not break latex compiling from a "higher" directory (namely . when the .pdf_tex file is in ./images)
My problem:
I have many rules in this form i. e. where only schematic is changed. I would like to use a pattern-rule that replaces schematic by %. And to use % in the recipe (in the sed command).
However the rule:
images/%.pdf: images/%.svg
inkscape -D -z --file=$^ --export-pdf=$# --export-latex
sed -i "s:%:images/%:g" $#_tex
does not works: % is interpreted literally in the recipe.
I also tried to replace % in the recipe by $% but this variable seems to be empty.
Unsatisfactory solution:
Add a line in the recipe to create a (make) variable that will hold the result of notdir(removeprefix($<)) (using this question or a call to bash because there is no removeprefix in GNU Make).
You want $*.
From https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html :
$*:
The stem with which an implicit rule matches (see How Patterns Match).
If the target is dir/a.foo.b and the target pattern is a.%.b
then the stem is dir/foo. The stem is useful for constructing names of
related files.
In a static pattern rule, the stem is part of the file name that matched the ‘%’ in the target pattern.
In an explicit rule, there is no stem; so ‘$*’ cannot be determined in that way. Instead, if the target name ends with a
recognized suffix (see Old-Fashioned Suffix Rules), ‘$*’ is set to the
target name minus the suffix. For example, if the target name is
‘foo.c’, then ‘$*’ is set to ‘foo’, since ‘.c’ is a suffix. GNU make
does this bizarre thing only for compatibility with other
implementations of make. You should generally avoid using ‘$*’ except
in implicit rules or static pattern rules.
If the target name in an explicit rule does not end with a recognized suffix, ‘$*’ is set to the empty string for that rule.
$% is an automatic variable, but it's for archive (.a library) members, and almost never useful.

can't use variable in Makefile

My main Makefile call config.mk
include $(TOPDIR)/config.mk
then config.mk include some sentences like this:
ifdef CPU
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk
endif
ifdef SOC
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk
endif
I have prepared these two tree and necessary config.mks. But for "SOC", whose value is "versatile", there is a problem. If I put "versatile" directly here, it could find the file and everything is fine; but when I use $(SOC), il will meet an error, and say
/../../../cpu/arm926ejs/versatile: is a folder, stop
Anyone know what the problem is ??
Are you sure you gave the exact error message? What version of make are you using? That error doesn't look like anything GNU make would print.
Anyway, I'll bet the problem is that your assignment of the SOC variable has trailing whitespace. According to the POSIX definition of make, leading whitespace before a variable value is removed, but trailing whitespace is preserved. That means, for example, if you write your makefile like this:
SOC = versatile # this is the versatile SOC
then make will remove the comment, but keep the space, so the value will be 'versatile' (space at the end). This means when the value is expanded in the sinclude line you get:
sinclude $(TOPDIR)/cpu/$(CPU)/versatile /config.mk
which make interprets as trying to include two different values, the first of which is a directory.
Even if you don't have a comment there, any trailing whitespace will be preserved. When editing makefiles you should try to put your editor into a mode where it flags trailing whitespace, or even better removes it automatically. GNU Emacs, for example, can do this.

Resources