So I followed the Advanced Auto-Dependency Generation paper --
Makefile:
SRCS := main.c foo.c
main: main.o foo.o
%.o: %.c
$(CC) -MMD -
The makefile in the original question doesn't work for me with gcc 4.8.2:
cc -MMD -MG -MT main.d -c main.c -o main.o
cc1: error: -MG may only be used with -M or -MM
I guess gcc changed the behaviour of -MG at some point in the last 4 years.
It seems that if you want to support generated header files, there is no longer any way to generate the ".d" file and the ".o" file at the same time, without invoking the C preprocessor twice.
So I've updated the recipe to:
%.o: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $*.d $<
$(CC) -c $< -o $@
(Note also that gcc now has -MP to generate phony targets for each header,
so you no longer need to run sed on gcc's output.)
We still have the same problem as the original question -- running make the
first time fails to generate foo.h:
$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
main.c:1:17: fatal error: foo.h: No such file or directory
#include "foo.h"
^
compilation terminated.
Makefile:7: recipe for target 'main.o' failed
make: *** [main.o] Error 1
Running it again works:
$ make
./mk_header.sh foo
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
cc main.o -o main
Since we have to run the C preprocessor twice anyway, let's generate the .d
file in a separate rule:
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@ $<
%.o: %.c
$(CC) -c $< -o $@
Now it generates the header file correctly:
$ make clean
rm -f *.o *.d main foo.h
$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
./mk_header.sh foo
cc -c main.c -o main.o
cc main.o -o main
Does this suffer from the performance issue that the original question was trying to avoid? This is essentially the "Basic Auto-Dependencies" solution described in the Advanced Auto-Dependency Generation paper.
That paper claims 3 problems with this solution:
make if anything changes.Problem 2 is solved by using -include instead of include. As far as I can
tell, this is orthogonal to the paper's technique for avoiding re-exec of
make. At least I haven't been able to cause any problems by using -include
instead of include.
Problem 3 is solved by GCC's -MP (or the equivalent sed script) -- this is
also orthogonal to the technique for avoiding re-exec of make.
Problem 1 can be perhaps ameliorated somewhat by something like this:
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@.new $<
cmp $@.new $@ 2>/dev/null || mv $@.new $@; rm -f $@.new
Before that change:
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
After that change:
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
Slightly better. Of course if a new dependency is introduced, make will still
need to re-execute. Maybe there's nothing that can be done to improve this; it seems to be a tradeoff between correctness and speed.
All of the above was tested with make 3.81.