---
title: Precompiled Header File Handling by Different Compilers
tags: [xmake, lua, precompiled headers, c++ compilation acceleration, optimization compilation, cross-platform]
date: 2017-07-31
author: Ruki
outline: deep
---

Recently, in order to implement precompiled header file support for [xmake](https://xmake.io), 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](https://xmake.io/)

#### References

[Speed up C++ compilation, part 1: precompiled headers](https://xmake.io/)

