zwol
zwol

Reputation: 140455

Avoid regenerating files that won't change

I have a Makefile with several rules of this form

protolist.c: $(PROTOCOLS) Makefile src/genmodtable.sh
    $(SHELL) $(srcdir)/src/genmodtable.sh \
        $@ $(filter-out %Makefile %genmodtable.sh, $^)

As the name implies, protolist.c winds up containing a list of all the "protocols" defined by the .c files in $(PROTOCOLS). The contents of this file do formally depend on everything in $(PROTOCOLS), the Makefile, and the generator script, but it's very rare for the file to actually change when one of those .c files is edited. Therefore, genmodtable.sh is coded to not change the timestamp of protolist.c if it's not going to make any change to its contents. This causes Make to skip rebuilding protolist.o and its dependencies when it's not really necessary.

That all works fine; the problem is that, because protolist.c now appears to be out of date with respect to its dependencies, Make thinks it has to try to regenerate protolist.c on every run. This isn't a performance issue -- the script is very fast -- but it is confusing behavior. I dimly recall an idiom, involving timestamp files, that could be used to stop Make from doing this, but I have not been able to reconstruct it or find it described anywhere. Does anyone know what it is?

(Also if anyone can suggest how to get rid of that silly $(filter-out ...) construct, that would be helpful, as that is the only GNUmakeism in this Makefile.)

Upvotes: 2

Views: 442

Answers (1)

eriktous
eriktous

Reputation: 6649

This appears similar to an issue with Fortran programming and make, involving the files generated when compiling a module. (Not relevant, other than that is where I picked up how to do this.)

What you want is have make compare the timestamp of protolist.o to the timestamp of protolist.c, which remains 'old', and make the decision to run the recipe for protolist.c, depending on the timestamp of, well, a timestamp file, which gets updated each time the recipe is run.
In order to make this work, you have to link the two together with an empty rule.

protolist.o: protolist.c
    [...]

protolist.c: protolist.c.time ;

protolist.c.time: $(PROTOCOLS) Makefile src/genmodtable.sh
    $(SHELL) $(srcdir)/src/genmodtable.sh \
        protolist.c $(filter-out %Makefile %genmodtable.sh, $^)
    touch protolist.c.time

In my own makefiles, I have to declare the timestamp files as prerequisites of the special target .PRECIOUS, to prevent make from deleting them, but I'm using pattern rules; I'm not 100% sure, but I think this isn't necessary when using explicit rules, like here.

To avoid the $(filter-out ...) construct, can you not simply replace it with $(PROTOCOLS)?
(Although, personally, I would stick to Paul's First Rule of Makefiles: Don't hassle with writing portable makefiles, use a portable make instead.)

Upvotes: 3

Related Questions