0
votes

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?

2
There’s a classic essay on the difficulties of working with make between projects, well worth a read: aegis.sourceforge.net/auug97.pdfSijmen Mulder
It looks like you haven't gone the whole way in defining your dependencies. Either you can find a result of ProjectB which ProjectA depends upon, or there is none, and then it is not make's fault not to rebuild. If your project management just wants to have new timestamps, make them explicit and let ProjectA and B depend upon them.Vroomfondel

2 Answers

3
votes

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:

  1. Always try to rebuild ../ProjectA/OUTPUT.
  2. If ../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...

1
votes

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.)