GCC and Precompiled Headers
Current GCC (i.e. 4.7) and previous versions of it works nicely with precompiled headers only when you have a single common header to your application, and when that single header (which includes in turn all the system ones, and the library specific ones, required by the application) is #include
-d (as the first non-comment lexeme of your sources) by every source of your application.
So you should have a single yourapp.h
and have every source file (i.e. every compilation unit) of yourapp
starting with #include "yourapp.h"
with the same preprocessing options (i.e. -D
or -I
or -U
) on the command line. That youapp.h
header file is usually #include
-ing many others, e.g. system headers (or GTK or Qt ones) like <stdlib.h>
or <sys/poll.h>
or [in C++] <algorithm>
or <gtk/gtk.h>
or <QtGui>
etc.
Recall that -H
is a useful option to get gcc
tell you what is included.
Your source files might have some additional #include
after #include "yourapp.h"
if so wanted.
After a [single] precompiled header has been included by GCC, you may of course #define
macros, #include
some non-precompiled header, do conditional compilation with #ifdef
, etc. But that preprocessing won't be "pre-compiled"!
This may not fit your needs or habits.
Some people (notably from Google, notably Diego Novillo) are working on the PreParsed Header (pph) branch to improve the situation, but the current GCC trunk has not got that work yet.
The explanation about that behavior of GCC is that a preprocessed header is essentially a persistent serialized checkpoint of the entire GCC heap (related to memory management inside GCC thru Ggc and GTY and gengtype
). That checkpointed heap can be loaded only when gcc
is in its initial empty state. As soon as something is known to gcc
(actually cc1
or cc1plus
) it cannot load any more any precompiled header file *.h.gch
and will revert to parsing the textual header file *.h
.
addenda (november 2014 and later)
Even GCC 4.9 wants a single precompiled header. The pre-parsed header effort by Diego Novillo et al. has been abandoned.
Future versions (post C++14) of the C++ standard might define a module mechanism. See e.g. n4047 proposal and the C++20 standard.
(additional addenda, summer 2020) This still holds for GCC-10 where several static analyzer options exist. See also the Clang static analyzer and this draft report. Consider using Frama-C.
Precompiled headers not used by GCC when building with a makefile
You're not including the PCH anywhere in your make command. Try this:
CXX = g++
CXXFLAGS = -O2 -H -Wall -std=c++17
OBJ = main.o #more objects here eventually I would think!
PCH_SRC = lib.hpp
PCH_HEADERS = headersthataregoinginyourpch.hpp andanother.hpp
PCH_OUT = lib.hpp.gch
main: $(OBJ)
$(CXX) $(CXXFLAGS) -o $@ $^
# Compiles your PCH
$(PCH_OUT): $(PCH_SRC) $(PCH_HEADERS)
$(CXX) $(CXXFLAGS) -o $@ $<
# the -include flag instructs the compiler to act as if lib.hpp
# were the first header in every source file
%.o: %.cpp $(PCH_OUT)
$(CXX) $(CXXFLAGS) -include $(PCH_SRC) -c -o $@ $<
First the PCH gets compiled. Then all cpp commands get compiled with -include lib.hpp
this guarantees that lib.hpp.gch
will always be searched first before lib.hpp
Trying to set up a pre-compiled header
Run Powershell as an administrator and it should work fine - you gain all permissions you need.
Weird Behavior with gcc precompiled headers
With GCC, precompiled headers work only if they are the only header, and if they are included first (without any previous header).
This answer explains more why it is so.
See also the Precompiled headers chapter of GCC documentation, which says:
- Only one precompiled header can be used in a particular compilation.
- A precompiled header can't be used once the first C token is seen.
BTW, it could happen that pre-compiling some large header (notably in C++) is not worth the effort. YMMV.
GCC - Multiple precompiled headers and specific paths
Here is an MVCE for your problem
scenario:
main.c
#include <hw.h>
#include <stdio.h>
int main(void)
{
puts(HW);
return 0;
}
hw.h
#ifndef HW_H
#define HW_H
#define HW "Hello World"
#endif
Makefile
srcs := main.c
objs := $(addprefix tmp/,$(srcs:.c=.o))
pch := tmp/hw.h.gch
CPPFLAGS += -I.
.PHONY: all clean
all: hw
tmp:
mkdir -p tmp
tmp/%.o: %.c | $(pch)
gcc -c $(CPPFLAGS) -o $@ $<
$(pch): hw.h | tmp
gcc -c $(CPPFLAGS) -o $@ $<
ifdef ENFORCE_PCH
echo "#error Debug." >> $^
endif
hw: $(objs)
gcc -o $@ $^
clean:
sed -i '/^#error/d' hw.h
rm -fr hw tmp
This project outputs its intermediate files in tmp
. The .o
files go there
and so does the PCH hw.h.gch
.
Build and run it:
$ make && ./hw
mkdir -p tmp
gcc -c -I. -o tmp/hw.h.gch hw.h
gcc -c -I. -o tmp/main.o main.c
gcc -o hw tmp/main.o
Hello World
So far so good. But did it actually use the PCH? Let's see:
$ make clean
sed -i '/^#error/d' hw.h
rm -fr hw tmp
$ make ENFORCE_PCH=true
mkdir -p tmp
gcc -c -I. -o tmp/hw.h.gch hw.h
echo "#error Debug." >> hw.h
gcc -c -I. -o tmp/main.o main.c
In file included from main.c:1:0:
./hw.h:5:2: error: #error Debug.
#error Debug.
^
Makefile:15: recipe for target 'tmp/main.o' failed
make: *** [tmp/main.o] Error 1
No it didn't. We know that because, with ENFORCE_PCH
defined, we have
tacked an #error
directive to the end of hw.h
after generating the
good tmp/hw.h.gch
. So if the former is subsequently #include
-ed anywhere
instead of the latter, the build breaks. Which it just did.
And that is just as it should be. GCC manual 3.21 Using Precompiled Headers,
para. 3:
A precompiled header file is searched for when #include is seen in the compilation.
As it searches for the included file (see Search Path) 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 can't be used, it is ignored.
So, given include search path .
, the directive #include <hw.h>
will causegcc
to check for the PCH ./hw.h.gch
before using ./hw.h
, and as there is
no ./hw.h.gch
, it will use ./hw.h
.
It might appear, from the documentation just quoted, that adding tmp
to the include search path - CPPFLAGS += -Itmp -I.
- should causetmp/hw.h.gch
to be used in preference to./hw.h
. But in fact it makes no difference.
The documentation omits a crucial qualification. The second sentence ought to read:
As it searches for the included file (see Search Path) the compiler looks for a
precompiled header in each directory just before it looks for the include file in
that directory and will use the precompiled header for preference if the include file
is found.
To be found and used, a PCH has to be a sibling of the matching header. And on consideration
this is what you'd want. Otherwise, a a/foo.h.gch
without a matching sibling header might be
found and used thanks to -Ia
when there is a b/foo.h.gch
, with a matching b/foo.h
, that
could be found and used thanks to a later -Ib
. Clearly, the latter is the sounder choice.
With this insight it's not hard to see a solution: if you really want to compile and use
a PCH that's not a sibling of its source header, make sure to give it a phony matching header
that is a sibling. You can arrange this as you see fit, e.g.
Makefile (fixed)
srcs := main.c
objs := $(addprefix tmp/,$(srcs:.c=.o))
pch := tmp/hw.h.gch
# Seek headers in tmp first...
CPPFLAGS += -Itmp -I.
.PHONY: all clean
all: hw
tmp:
mkdir -p tmp
tmp/%.o: %.c | $(pch)
gcc -c $(CPPFLAGS) -o $@ $<
$(pch): hw.h | tmp
# Make phony header in tmp...
echo "#error You should not be here" > $(basename $@)
gcc -c $(CPPFLAGS) -o $@ $<
ifdef ENFORCE_PCH
echo "#error Debug." >> $^
endif
hw: $(objs)
gcc -o $@ $^
clean:
sed -i '/^#error/d' hw.h
rm -fr hw tmp
See that the PCH is used from tmp
:
$ make clean
sed -i '/^#error/d' hw.h
rm -fr hw tmp
$ make ENFORCE_PCH=true && ./hw
mkdir -p tmp
# Make phony header in tmp...
echo "#error You should not be here" > tmp/hw.h
gcc -c -Itmp -I. -o tmp/hw.h.gch hw.h
echo "#error Debug." >> hw.h
gcc -c -Itmp -I. -o tmp/main.o main.c
gcc -o hw tmp/main.o
Hello World
Related Topics
What's the Difference Between _Pretty_Function_, _Function_, _Func_
Why Are Unnamed Namespaces Used and What Are Their Benefits
Const VS Constexpr on Variables
Is There a Limit on Number of Open Files in Windows
Why Do C and C++ Support Memberwise Assignment of Arrays Within Structs, But Not Generally
Why Are References Not Reseatable in C++
Catch Exception by Pointer in C++
C++ Redefinition Header Files (Winsock2.H)
Why Dereferencing a Null Pointer Is Undefined Behaviour
Why Can't I Initialize Non-Const Static Member or Static Array in Class
Proper Stack and Heap Usage in C++
Why Does Int Pointer '++' Increment by 4 Rather Than 1
Is the Return Type Part of the Function Signature
Why Will Std::Sort Crash If the Comparison Function Is Not as Operator ≪