Here is an example from the GNU Make manual section on Secondary Expansion (slightly simplified):
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$@ expands to the target ("foo" in this case)
foo: $$(patsubst %.c,%.o,$$($$@_SRCS))
This works great; it builds bar.o
and baz.o
:
cc -c -o bar.o bar.c
cc -c -o baz.o baz.c
But if I tweak this example only slightly, the patsubst
stops working:
all: foo.a
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$* expands to the stem of the match ("foo" in this case).
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $@ $^
It is no longer building bar.o
and baz.o
, and instead is using the *.c
files directly as prerequisites!
ar rcs foo.a bar.c baz.c
Please note that the $$($$*_SRCS)
part is clearly working, as evidenced by the fact that it found foo_SRCS
and used that as the prerequisites. But for some reason the patsubst
part has become a no-op! Instead of replacing %.c
with %.o
, it is just using foo_SRCS
directly.
What is going on here? How can I get my example to work?
EDIT: I had a theory that the %
characters inside the patsubst
were getting evaluated early, using the stem match (foo), so that the patsubst itself was looking something like this:
$(patsubst foo.c,foo.o,bar.c baz.c)
To test this theory, I added a file called foo.c
to foo_SRCS
:
all: foo.a
foo_SRCS := foo.c bar.c baz.c
.SECONDEXPANSION:
# $$* expands to the stem of the match ("foo" in this case).
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $@ $^
That resulted in something even weirder:
make: *** No rule to make target `foo.a', needed by `all'. Stop.
The percent characters are being read by make as matches to the wildcard in the stem and are being replaced with the stem match. If you check the make -p
output for your example you'll see that the parsed target line looks like this:
%.a: $(patsubst %.c,%.o,$($*_SRCS))
Which, as far as make is concerned, is just a really odd set of patterned targets (or something like that).
If you escape the percent characters from make parsing in a similar way to how you escape the $
from make evaluation you can get what you want to work:
pc := %
$$(patsubst $$(pc).c,$$(pc).o,$$($$*_SRCS))
For added information substitution references (i.e. $(foo_SRCS:.c=.o)
) can be used for transformations like this in place of the longer call to patsubst. In this case however, while it works in this scenario with a similar escaping of :
(via c := :
) it doesn't seem to function as the sole prerequisite of the target (with make giving a Makefile:23: *** commands commence before first target. Stop.
error that I don't quite understand) at least with GNU Make 3.81.
You're mixing three features that don't go well together: secondary expansion, pattern rules, and patsubst. I'll try to explain in detail what make is doing when evaluating your code (AFAIUI).
Let's start with your first Makefile:
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $@ $^
Read phase. All dollar signs are escaped, so no evaluation happens here. Make enters the following rule in its database:
%.a: $(patsubst %.c,%.o,$($*_SRCS))
Pattern substitution. As far as make is concerned, this is just another pattern rule, with target %.a
and two prerequisites separated by whitespace: $(patsubst
and %.c,%.o,$($*_SRCS))
.
foo.a
matches the target pattern, and so the first %
in each prerequisite will be replaced by foo
. The rule becomes:
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
Target update phase. As you requested secondary expansion, the pattern is evaluated again in the target update phase:
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
==> foo.a: $(patsubst foo.c,%.o,bar.c baz.c)
==> foo.a: bar.c baz.c
And so make ends up executing the command
ar rcs foo.a bar.c baz.c
And what about foo.c? If you add foo.c to foo_SRCS, the secondary expansion looks like this:
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
==> foo.a: $(patsubst foo.c,%.o,foo.c bar.c baz.c)
==> foo.a: %.o bar.c baz.c
And the rule fails because make doesn't know how to build %.o
.
Work-around. You can escape the %
characters with a backslash:
.SECONDEXPANSION:
%.a: $(patsubst \%.c,\%.o,$$($$*_SRCS))
ar rcs $@ $^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With