gccmakefiledependency-managementpch

Using precompiled header with dependency generation


ers:

I'm trying to combine precompiled headers with dependency generation (-MM). Final goal is to generate dependency file for make. Not able to combine them together with -fpch-deps. The motivation is to speed up (incremental) builds for a huge code base that take hours to build.

Using GCC 9.3.1, "C" code. same results with GCC10 (Redhat builds)

src/a.c:
#include "h.h"
int main(int argc, char **argv) { printf("hello\n") ; }

include/h.h
#include <stdio.h>

Testing with something like:

mkdir obj
gcc include/h.h -o obj/h.h.gch
# Compile against PCH
gcc -I obj  src/a.c

# Validation:
# Next line will print hello - showing PCH-based build works.
obj/a.out

# Build dependecy from pch - fail
gcc -MM -I obj -fpch-deps a.c -Winvalid-pch

GCC message is fatal error: h.h: no such file or directory, implying that it did not search for the obj/h.h.gch.

Any help, am I misinterpreting/misusing the '-fpch-deps'

Already verified:


Solution

  • You have this directory tree:

    $ tree
    .
    ├── include
    │   └── h.h
    ├── obj
    └── src
        └── a.c
    

    So the commands:

    $ gcc include/h.h -o obj/h.h.gch
    $ gcc -I obj  src/a.c
    

    must be run in .to work and the latter will output ./a.out, not ./obj/a.out

    The command:

    $ gcc -MM -I obj -fpch-deps a.c -Winvalid-pch
    cc1: fatal error: a.c: No such file or directory
    

    could not work unless you:

    $ cd src
    

    Where it will still fail as you observed:

    $ gcc -MM -I obj -fpch-deps a.c -Winvalid-pch
    a.c:1:10: fatal error: h.h: No such file or directory
        1 | #include "h.h"
          |          ^~~~~
    

    Why that happens can be seen with:

    $ gcc -v -MM -I obj -fpch-deps a.c -Winvalid-pch
    ...[cut]...
    ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
    ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu"
    ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed"
    ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include"
    ignoring nonexistent directory "obj"
    #include "..." search starts here:
    #include <...> search starts here:
     /usr/lib/gcc/x86_64-linux-gnu/13/include
     /usr/local/include
     /usr/include/x86_64-linux-gnu
     /usr/include
    End of search list.
    a.c:1:10: fatal error: h.h: No such file or directory
        1 | #include "h.h"
          |          ^~~~~
    compilation terminated.
    

    There is no h.h in the current directory and:

    ignoring nonexistent directory "obj"
    

    There is no directory obj under src.

    If -I obj is not to be ignored then you must:

    $ cd .. # up from src
    

    and then run:

    $ gcc -v -MM -I obj -fpch-deps src/a.c -Winvalid-pch
    ...[cut]...
    ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
    ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu"
    ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed"
    ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include"
    #include "..." search starts here:
    #include <...> search starts here:
     obj
     /usr/lib/gcc/x86_64-linux-gnu/13/include
     /usr/local/include
     /usr/include/x86_64-linux-gnu
     /usr/include
    End of search list.
    src/a.c:1:10: fatal error: h.h: No such file or directory
        1 | #include "h.h"
          |          ^~~~~
    compilation terminated.
    

    Include-directory obj is now found, but header h.h remains not found, notwithstanding that:

    $ ls obj
    h.h.gch
    

    That is because there:

    GCC Manual: 3.22 Using Precompiled Headers:

    A precompiled header file is searched for when #include is seen in the compilation. As it searches for the included file (see Search Path in The C Preprocessor) the compiler looks for a precompiled header in each directory just before it looks for the include file in that directory. The name searched for is the name specified in the #include with ‘.gch’ appended. If the precompiled header file cannot be used, it is ignored.

    For instance, if you have #include "all.h", and you have all.h.gch in the same directory as all.h, then the precompiled header file is used if possible, and the original header is used otherwise.

    So if you do:

    $ mv obj/h.h.gch include/
    

    then:

    $ gcc -MM -I include -fpch-deps src/a.c -Winvalid-pch
    a.o: src/a.c include/h.h
        
    

    will succeed, and there you have the make dependency line for a.o.

    You can check that pre-compiled header is live with:

    $ gcc -H -I include src/a.c
    ! include/h.h.gch
     src/a.c
    

    But if you then were to do:

    $ mv include/h.h include/hide-h.h
    

    then once again:

    $ gcc -MM -I include -fpch-deps src/a.c -Winvalid-pch
    src/a.c:1:10: fatal error: h.h: No such file or directory
        1 | #include "h.h"
          |          ^~~~~