Specifiy a combined target in a makefile
Using GNU Make, how can I ensure other targets dependencies are made before a final task runs?
(I know this is a a bit of an abuse of make, but can it be done?)
Say I have 2 targets a and b. Building just a or just b is easy, but I want to trigger a special type of build if targetting both a and b.
For example, my Makefile (simplified from a real problem)
a_prereq:
@echo "making a"
b_prereq:
@echo "making b"
a: a_prereq default
b: b_prereq default
default:
@echo "made!"
Calling make a
prints making a; made!
. This is correct.
Calling make b
prints making b; made!
. This is correct.
Calling make a b
prints making a; made!; making b
. Not what I wanted!
How do I get make a b
to print making a; making b; made!;
if I can only change the makefile and not the calling make a b
.
(I know I could I could introduce c: a_prereq b_prereq default
and call make c
but can it be done without the new target?)
Solution 1:
This looks like an XY problem. What you apparently want (not 100% sure) is to force default
to be built after a_prereq
and b_prereq
if and only if the a
and b
goals are passed on the command line. This is quite strange and not really what make is intended for but let's give it a try.
Forcing default
to be built after a_prereq
and b_prereq
, even with parallel make, is straightforward: declare them as prerequisites of default
(indeed, I do not know another way to do it):
default: a_prereq b_prereq
@echo "made!"
Or, if you prefer to separate the declaration of prerequisites from that of the recipe:
default: a_prereq b_prereq
default:
@echo "made!"
But as you want this only if the a
and b
goals are passed on the command line we need to detect this situation. Fortunately the MAKECMDGOALS
GNU make variable lists the goals specified on the command line. So a bit of filter
applied to it should make it.
We can set make variable MATCH
to string ab
if and only if the a
and b
goals are passed on the command line. And we can use make conditionals (ifeq
) to declare a_prereq
and b_prereq
as prerequisites of default
if and only if $(MATCH)
equals ab
. Just add the following 4 lines to your Makefile:
MATCH := $(filter a,$(MAKECMDGOALS))$(filter b,$(MAKECMDGOALS))
ifeq ($(MATCH),ab)
default: a_prereq b_prereq
endif
Note: if the real default
recipe makes use of automatic variables that expand as prerequisites ($<
, $^
, ...) you will have to remember that their values are different for make a
and for make a b
...
Note: if targets a
and b
must be rebuild but are not passed as goals on the command line this will not work. For instance, if a
and b
are prerequisites of another, requested, goal. This is one of the main reasons why what you ask for looks so strange.
Note: if you also need to build default
after a_prereq
when running make a
(same for b
) an even simpler solution would be:
default: $(patsubst %,%_prereq,$(filter a b,$(MAKECMDGOALS)))
This way, if you run make a
, a_prereq
is declared as prerequisite of default
, if you run make b
, b_prereq
is declared as prerequisite of default
, is you run make a b
both a_prereq
and b_prereq
are declared as prerequisites of default
, and if you run make foo
, default
has no prerequisites at all.