Skip to content

Precompiled Header File Handling by Different Compilers

Recently, in order to implement precompiled header file support for xmake, I studied the mechanisms and differences of how major mainstream compilers handle precompiled headers.

Most c/c++ compilers now support precompiled headers, such as: gcc, clang, msvc, etc., to optimize c++ code compilation speed. After all, if c++ header files contain template definitions, compilation speed is very slow. If most common header files can be placed in a header.h and precompiled before other source code compilation, subsequent code can reuse this part of the precompiled header, which can greatly reduce frequent redundant header file compilation.

However, different compilers have different levels of support and handling methods for it, and it's not very universal. It took a lot of effort to encapsulate it into a unified interface and usage method in xmake.

MSVC Precompiled Header Handling

Precompiled headers are very common in msvc projects. You often see files like stdafx.cpp, stdafx.h, which are used for this purpose. The msvc compiler generates the precompiled header file stdafx.pch by compiling stdafx.cpp.

The command line to create a precompiled header is as follows:

bash
$ cl.exe -c -Yc -Fpstdafx.pch -Fostdafx.obj stdafx.cpp

Among them, -Yc means creating the precompiled header stdafx.pch, -Fp is used to specify the output file path of *.pch, and -Fo is used to specify the object file generated by compiling stdafx.cpp.

How do other source files use this stdafx.pch? By passing stdafx.h to -Yu to tell the compiler to compile the current code, ignore #include "stdafx.h", and directly use the already compiled stdafx.pch file.

bash
$ cl.exe -c -Yustdafx.h -Fpstdafx.pch -Fotest.obj test.cpp

Finally, when linking, you need to link both: stdafx.obj and test.obj. This is also different from gcc and clang compilers.

bash
$ link.exe -out:test test.obj stdafx.obj

Note: You must also link stdafx.obj. Although stdafx.cpp is only used to generate stdafx.pch, the object file is also needed.

Another difference from gcc and clang is that msvc's -Yu specifies that stdafx.h must be the header file name in #include "stdafx.h", not the file path.

Clang Precompiled Header File Handling

I personally feel that clang's precompiled header file support is the most friendly and simplest.

Compared to msvc, it doesn't need stdafx.cpp, only a header file stdafx.h can generate a pch file. Compared to gcc, it can flexibly control the pch file path, which is more flexible.

Compile header file to generate pch file:

bash
$ clang -c -o stdafx.pch stdafx.h

Use precompiled header file:

bash
$ clang -c -include stdafx.h -include-pch stdafx.pch -o test.o test.cpp

Among them, -include stdafx.h is used to ignore #include "stdafx.h" when compiling test.cpp, and use the precompiled stdafx.pch through -include-pch.

And the stdafx.h and stdafx.pch specified here can not only be files in the includedir search path, but also specify full path file names, which is very flexible, for example:

bash
$ clang -c -include inc/stdafx.h -include-pch out/stdafx.pch -o test.o test.cpp

GCC Precompiled Header File Handling

gcc's precompiled header handling is basically similar to clang's. The only difference is: it doesn't support the -include-pch parameter, so it cannot specify the stdafx.pch file path to use.

It has its own search rules:

  1. Look for stdafx.h.pch file in the directory where stdafx.h is located
  2. Look for stdafx.h.pch in the -I header file search path

Compile header file to generate pch file:

bash
$ gcc -c -o stdafx.pch stdafx.h

Use precompiled header file:

bash
$ gcc -c -include stdafx.h -o test.o test.cpp

In order for the above code to compile normally, stdafx.h.pch must be placed in the same directory as stdafx.h, so that compilation can find it. I haven't found a method to specify the output directory yet.

Other Notes

For gcc and clang, compiling *.h header files by default is used as c precompiled headers, which is different from c++ pch and cannot be used by c++ code. If you want to generate c++ usable pch files, you must tell the compiler how to compile stdafx.h.

This can be solved through the -x c++-header parameter:

bash
$ gcc -c -x c++-header -o stdafx.pch stdafx.h

Of course, it can also be solved by modifying the suffix:

bash
$ gcc -c -o stdafx.pch stdafx.hpp

xmake Support for Precompiled Header Files

xmake supports accelerating c/c++ program compilation through precompiled header files. Currently supported compilers are: gcc, clang and msvc.

The usage for c precompiled header files is as follows:

lua
target("test")
    set_pcheader("header.h")

If it's precompilation of c++ header files, change to:

lua
target("test")
    set_pcxxheader("header.h")

For more usage instructions, see: target.set_pcheader

References

Speed up C++ compilation, part 1: precompiled headers