GNU make: Generating automatic dependencies with generated header files

后端 未结 4 665
死守一世寂寞
死守一世寂寞 2020-12-05 12:13

So I followed the Advanced Auto-Dependency Generation paper --

Makefile:

SRCS := main.c foo.c

main: main.o foo.o

%.o: %.c
    $(CC) -MMD -         


        
4条回答
  •  生来不讨喜
    2020-12-05 12:43

    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:

    1. We re-exec make if anything changes.
    2. Ugly but harmless warning: "main.d: No such file or directory"
    3. Fatal error "no rule to make targe foo.h" if foo.h file is removed, even if mention of it is removed from the .c file.

    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.

提交回复
热议问题