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

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.

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.

What's the meaning of targets in a Makefile that start with a ., and how is it different/similar to suffix rules?

In a Makefile, I saw this:
all: .made
# ... some other code
.made: $(Program) bashbug
#echo "$(Program) last made for a $(Machine) running $(OS)" >.made
Is the .made rule a suffix rule as per: https://www.gnu.org/software/make/manual/make.html#Suffix-Rules?
Also, are all rules with targets that have a . in front suffix rules?
Otherwise, what is the significance of targets that start with .? It seems like there's a significance as per this in https://www.gnu.org/software/make/manual/make.html#How-Make-Works:
By default, make starts with the first target (not targets whose names
start with ‘.’).
But its significance is not mentioned.
Also, if it is a suffix rule, how come .made can be used as a prerequisite in the all rule? (It's not mentioned that the targets of suffix rules can be used as prerequisites in other rules.)
P.S.: This question is related to but different from what is the meaning for . (dot) before the target in makefile. This question asks explicitly for the difference between a target with . and a target of a suffix rule.
Is the .made rule a suffix rule
No, because .made is not a "known suffix".
Also, are all rules with targets that have a . in front suffix rules?
No, only those where the word after the dot is a "known suffix":
Your first two questions are answered by https://www.gnu.org/software/make/manual/make.html#Suffix-Rules:
Suffix rule definitions are recognized by comparing each rule’s target against a defined list of known suffixes. When make sees a rule whose target is a known suffix, this rule is considered a single-suffix rule. When make sees a rule whose target is two known suffixes concatenated, this rule is taken as a double-suffix rule.
In your example .made is an actual filename. Your Makefile has a rule for it that creates/updates the file:
#vvvv
.made: $(Program) bashbug
#echo "$(Program) last made for a $(Machine) running $(OS)" >.made
# ^^^^^
All pretty normal; it would work the same way with any other name.
The only significance of the leading dot is that it makes the file "hidden" by convention, i.e. ls won't show it (without specifying -a), plain * won't match it, etc.

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.

How to include path prefix in GNU Make pattern rule

Consider the following:
%.foo: %.bar
echo $< > $#
Assuming we have one file 1.bar, the command executed is simply echo 1.bar > 1.foo. However, when % contains a path, rather than just a file name, it start becoming finicky. My problem is that I want to prepend another path to %.bar, the pattern becomes completely mangled. I.e., when %.bar is nice/path/1.bar, this becomes impossible:
%.foo: /project/something/%.bar
echo $< > $#
This will run, but it executes echo nice/path//project/something/1.bar > 1.foo in stead of echo /project/something/nice/path1.bar > 1.foo
The reason for this is in how make does its pattern rules. From the docs:
When the target pattern does not contain a slash (and it usually does not), directory names in the file names are removed from the file name before it is compared with the target prefix and suffix. [...] The directories are ignored only for the purpose of finding an implicit rule to use, not in the application of that rule. Thus, ‘e%t’ matches the file name src/eat, with ‘src/a’ as the stem. When prerequisites are turned into file names, the directories from the stem are added at the front, while the rest of the stem is substituted for the ‘%’. The stem ‘src/a’ with a prerequisite pattern ‘c%r’ gives the file name src/car
Is there any way I can turn this off for a specific rule?
You may like to read up on How Patterns Match:
When the target pattern does not contain a slash (and it usually does not), directory names in the file names are removed from the file name before it is compared with the target prefix and suffix. After the comparison of the file name to the target pattern, the directory names, along with the slash that ends them, are added on to the prerequisite file names generated from the pattern rule’s prerequisite patterns and the file name. The directories are ignored only for the purpose of finding an implicit rule to use, not in the application of that rule. Thus, ‘e%t’ matches the file name src/eat, with ‘src/a’ as the stem. When prerequisites are turned into file names, the directories from the stem are added at the front, while the rest of the stem is substituted for the ‘%’. The stem ‘src/a’ with a prerequisite pattern ‘c%r’ gives the file name src/car.
The above explains why nice/path/ is prepended to prerequisite /project/something/1.bar.
One fix would be to use full file names in rules, e.g.:
${CURDIR}/nice/path/%.foo: /project/something/%.bar

What does a percent symbol do in a 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.

Resources