Reputation: 302
I have two projects which I build with GNU make. Project B depends upon one of the outputs of project A. Let's call it OUTPUT. So the Makefile for project B contains something like this:
target: ../ProjectA/OUTPUT
do stuff
To ensure that OUTPUT is up to date, I would like the Makefile for B to launch the make for A and then only "do stuff" if OUTPUT was rebuilt (or was already newer than target).
I first tried doing it this way:
../ProjectA/OUTPUT:
(cd ../ProjectA; $(MAKE) OUTPUT)
but because there are no dependencies given for OUTPUT, make will simply assume that it is up to date if it exists and will not run the sub-make.
Declaring OUTPUT to be phony ensures that the sub-make will run:
.PHONY: ../ProjectA/OUTPUT
../ProjectA/OUTPUT:
(cd ../ProjectA; $(MAKE) OUTPUT)
but now make will ignore the actual date on OUTPUT and always consider it to be newer than target, meaning "do stuff" will always execute.
The only thing I've found to work is duplicating OUTPUT's entire dependency tree within B's Makefile. That's too horrible to contemplate. Only slightly less horrible is to put OUTPUT's dependency tree in a separate file that the Makefiles for both projects include.
Is there a nicer way to get make to do what I want?
Upvotes: 0
Views: 443
Reputation: 15493
Well I didn't know double colon rules worked like that. That is really useful.
Just for the record, there is a way to get make to attempt to rebuild ../ProjectA/OUTPUT
every time that is a little less obscure(?). Basically, you use an intermediate .PHONY
target.
.PHONY: FORCE
FORCE: ;
../ProjectA/OUTPUT: FORCE
${MAKE} -C ${@D} ${@F}
Due to FORCE
, make will realise OUTPUT
may be out of date. It will run the recipe, and then check to see whether OUTPUT
was actually updated.
(FWIW I like the double colon formulation. Seems a lot cleaner to me.)
Upvotes: 1
Reputation: 29260
You could add a phony target, with a recipe that makes the other project, such that it is always made, even if there is nothing to do, then launch a second sub-make to make your actual target:
.PHONY: all
all:
$(MAKE) -C ../ProjectA OUTPUT
$(MAKE) target
target: ../ProjectA/OUTPUT
do stuff
Not very elegant and not very efficient but it should do what you want:
../ProjectA/OUTPUT
.../ProjectA/OUTPUT
changed, also rebuild target
.There is another way, more elegant and efficient, but it uses a rather obscure GNU make feature: the double-colon rule (see this section of the manual), in a rather unusual way, and for which it was certainly not invented:
target: ../ProjectA/OUTPUT
do stuff
../ProjectA/OUTPUT::
$(MAKE) -C ../ProjectA OUTPUT
So, if you do not like obscurity, prefer the first one. At least, it is easier to understand and nobody will ever break it by deleting this extra colon which was certainly a typo...
Upvotes: 3