I appears from your own answer, which has beaten me to the punch, that
you're concerned only to trigger a preliminary action for targets that are
mentioned on the commandline - $(MAKECMDGOALS)
. From the posting I took
it that you wanted such an action for "every target in a Makefile", which
would include all targets that are prerequisite to the commandline targets or,
if there are no commandline targets, to the default target.
Anyhow, you may still be interested in a solution to the more general problem.
You want a preliminary action to be executed before the recipe for every target.
Your question is: how to match a patten rule before explicit rule?
This is an XY way of posing the problem, because make
will consult pattern
rules to find a way of making a target only if you don't give it an explicit
recipe. You know, for example, that make
has a pre-defined pattern rule for
making an .o
file from a .c
file. Even so, if my makefile is:
test.o:
@echo $@
then make
prints test.o
, without any attempt to find test.c
and compile it.
And if my make file is:
test.o: test.c
@echo $@
test.c:
@echo $@
then make
prints:
test.c
test.o
needing no resort to the pattern rule. But if my makefile is:
test.o: test.c
Then make
says:
make: *** No rule to make target 'test.c', needed by 'test.o'. Stop
So you can't do what you're after in the way your question supposes,
because the preliminary action you want to provoke from the pattern
rule could be provoked only if there were no other action for the target.
In that case the reasons for the failures of your two posted attempts are fairly academic,
and you may wish to scroll to The Chase.
In your first attempt, with:
%: $*
@echo 'Logging $* target'
The pattern rule - which is unemployed by make test
- is equivalent to:
%:
@echo 'Logging $* target'
because $*
only assumes a value in the recipe, not in the pattern rule. You
can make this pattern rule be employed by making any target for which the
makefile does not provide a recipe, e.g. make nonsuch
will print Logging nonsuch target
;
but that is of no use.
The second attempt, with:
.SECONDEXPANSION:
%: $$*
@echo 'Logging $* target'
does the right thing to create the rule you intend to create. But the
meaning of that rule is:
<target>: <target>
@echo 'Logging <target> target'
making every target to which this rule is applied a prerequisite of itself.
Inevitably this will result in a circular dependency error for all such targets.
As you saw, this circularity does not affect the your test
target because
it has an explicit recipe and does not employ the rule. But it does provoke
the surprising error:
make: Circular Makefile <- Makefile dependency dropped.
That happens because the first target that make
automatically considers is
the makefile itself. Unlike the test
target, you have no recipe for
the makefile; so the pattern rule applies to it, making the makefile dependent
on itself.
The Chase
You can achieve what you want by a different approach. In a actual project
it is more than likely that in any makefile you can compute a list of
all possible targets. From this you can generate a corresponding list of
auxiliary targets, say, target => target.prelim, where the
sole purpose of target.prelim is to provoke, when it should and not
otherwise, the required preliminary action for target; and you can get make
to generate a list of order-only rules, target: | target.prelim,
for each target, such that target.prelim will not be considered in determining whether target
must be made, but will be made before target whenever target needs to be made.
Here is an illustration:
SRCS := main.c foo.c
OBJS := $(SRCS:.c=.o)
TARGETS := all prog $(OBJS)
PRELIMS := $(patsubst %,%.prelim,$(TARGETS))
define prelim_rule =
$(1): | $(1).prelim
endef
$(foreach target,$(TARGETS),$(eval $(call prelim_rule,$(target))))
.PHONY: all
all: prog
prog: $(OBJS)
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
clean:
rm -f $(OBJS) $(PRELIMS) prog
%.prelim:
@echo "Logging target $(@:%.prelim=%)"
@touch $@
And a sample session:
$ make
Logging target all
Logging target main.o
cc -c -o main.o main.c
Logging target foo.o
cc -c -o foo.o foo.c
Logging target prog
cc -o prog main.o foo.o
$ make
make: Nothing to be done for 'all'.
$ make clean
rm -f main.o foo.o all.prelim prog.prelim main.o.prelim foo.o.prelim prog
$ make main.o
Logging target main.o
cc -c -o main.o main.c
$ make main.o
make: 'main.o' is up to date.
$ # A prelim can't out-date its target...
$ touch main.o.prelim
$ make main.o
make: 'main.o' is up to date.